您的位置:首页 > 移动开发 > Android开发

【Android】Android自定义ViewGroup

2016-02-27 16:43 393 查看
ViewGroup
存在的目的就是对其子
View
进行管理,为其子
View
添加显示、响应的规则。因此,自定义
ViewGrou
p通常需要重写
onMeasure()
方法对其子
View
进行测量,重写
onLayout()
方法来确定子
View
的位置,重写
onTouchEvent()
方法增加响应事件。所以我们需要做这几件事:

重写
onMeasure()
方法对其子
View
进行测量

重写
onLayout()
方法来确定子
View
的位置

重写
onTouchEvent()
方法增加响应事件

以一个粘性
View
效果作为示例,来看看每部分都需要怎么做。先看完整版代码,后面会进一步分析:

package com.wondertwo.app.customview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
* 实现粘性效果,即当子View向上滑动大于某值后,松开手指它将向上滑动并显示下一个子View
* 当滑动距离小于某值,松开手指后它将回到开始的位置
*
* Created by wondertwo on 2016/2/27.
*/
public class SticklyView extends ViewGroup {

private int mScreenHeight;
private Scroller mScroller;
private int mLastY;
private int mStart;
private int mEnd;

public SticklyView(Context context) {
super(context);
}

public SticklyView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public SticklyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

/**
* 重写onMeasure()方法,通过遍历子View来测量子View的大小
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount  = getChildCount();
// 设置ViewGroup的高度,高度等于每个子View的高度乘以子View的个数
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.height = mScreenHeight * childCount;
setLayoutParams(mlp);
for (int i = 0; i< childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
// public void layout(int left, int top, int right, int bottom)设置child的位置
child.layout(l, i * mScreenHeight, r, (i + 1) *mScreenHeight);
}
}
}

/**
* 手指触摸动作的监听
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
// 记录触摸起点
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
// 拦截动画
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
int dy = mLastY - y;
if (getScrollY() < 0) {
dy = 0;
}
if (getScrollY() > getHeight() - mScreenHeight) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
// 记录触摸终点
int dScrollY = checkAlignment();
if (dScrollY > 0) {
if (dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, mScreenHeight - dScrollY);
}
} else {
if (-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, -mScreenHeight - dScrollY);
}
}
break;
}
// 重绘
postInvalidate();
return true;
}

@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}

private int checkAlignment() {
int mEnd = getScrollY();
boolean isUp = ((mEnd - mStart) > 0) ? true : false;
int lastPrev = mEnd % mScreenHeight;
int lastNext = mScreenHeight - lastPrev;
if (isUp) {
//向上的
return lastPrev;
} else {
return -lastNext;
}
}
}


重写
onMeasure()
方法对其子
View
进行测量先要获得子
View
的数量:
int count = getChildCount()
。然后通过
for
循环遍历每个子
View
获得他们的大小。代码如下:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount  = getChildCount();
// 设置ViewGroup的高度,高度等于每个子View的高度乘以子View的个数
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.height = mScreenHeight * childCount;
setLayoutParams(mlp);
for (int i = 0; i< childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
// public void layout(int left, int top, int right, int bottom)设置child的位置
child.layout(l, i * mScreenHeight, r, (i + 1) *mScreenHeight);
}
}
}


重写
onLayout()
方法来确定子
View
的位置和重写
onTouchEvent()
方法增加响应事件了。其实这一步才是实现粘效果的关键。代码如下:

/**
* 手指触摸动作的监听
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
// 记录触摸起点
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
// 拦截动画
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
int dy = mLastY - y;
if (getScrollY() < 0) {
dy = 0;
}
if (getScrollY() > getHeight() - mScreenHeight) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
// 记录触摸终点
int dScrollY = checkAlignment();
if (dScrollY > 0) {
if (dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, mScreenHeight - dScrollY);
}
} else {
if (-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, -mScreenHeight - dScrollY);
}
}
break;
}
// 重绘
postInvalidate();
return true;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: