Android-自定义ViewGroup(竖向导航页)
2016-08-17 本文已影响178人
c37d344afd22
自定义ViewGroup
自定义ViewGroup需要注意什么
首先我们要明确一点,ViewGroup就是用来存放View的容器,用来对子View进行管理,给子View添加规则,so
- 对子View进行测量,重写onMeasure()方法
- 对子View的布局进行控制,重写onLayout()方法
- 重写onTouchEvent(),dispatchEvent()来对点击触控事件进行控制
实例
长这个样子
![](https://img.haomeiwen.com/i1822424/d70282c81ab583c5.gif)
从这个图就可以看的出来我们的需求
- 可以滑动
- 当滑动大于一定距离的时候显示下一个子View
- 如果小于一定距离那么就回到原位
首先这里可以看的出来,我们每一个子View都是沾满一个屏幕的,所以这里整个ViewGroup的高度就能确定出来了,就是屏幕的高度乘子View的个数
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i); //获得每一个子view
measureChild(child, widthMeasureSpec, heightMeasureSpec); //测量子view
}
}
我们先在onMeasure()方法中测量每一个子View,这里直接调用measureChild()方法就可以进行对子View的测量,用一个for循环很方便。
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
/**
* 这里对整个ViewGroup的高度进行初始化
* 屏幕的高度乘子View的个数
*/
mlp.height = mScreenHeight * childCount;
setLayoutParams(mlp);
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
/**
* 这里对子View进行布局,由于我们的ViewGroup是上下滑动
* 所以只需要确定top和bottom两个值就可以,让他们从上到下排列
*/
child.layout(
l,
i * mScreenHeight,
r,
(i + 1) * mScreenHeight
);
}
}
}
到这里我们就已经完成了对ViewGroup的测量和对子View位置的控制,接下来就是对触摸事件进行控制
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStartY = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
int dy = mLastY - y;
/**
* getScrollY()小于0则为第一屏
*/
if (getScrollY() < 0) {
dy = 0;
}
/**
* 最后一屏
*/
if (getScrollY() > mTotalHeight - mScreenHeight) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
int dScrollY = check();
if (dScrollY > 0) { //从下往上滑
/**
* 如果这个距离小于屏幕的1/3
* 那么就回到原位,也就是当前距离取负值
* 如果大于屏幕的1/3
* 那么就直接进入到下一个子View
*/
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;
}
private int check() {
int mEnd = getScrollY();
/**
* 判断滑动方向
* true 从下往上滑
* false 从上往下滑
*/
boolean isUp = mEnd - mStartY > 0;
/**
* 关于这两个变量,读者可以自行的打印出来理解一下
* 用语言来描述不好描述(好吧,其实是表达能力有限)
*/
int lastPrev = mEnd % mScreenHeight;
int lastNext = mScreenHeight - lastPrev;
if (isUp) {
return lastPrev;
} else {
return -lastNext;
}
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}
- 这里我们首先在ActionDown事件中获取到第一次碰到屏幕时候的Y轴偏移量
- 然后我们在ActionMove事件中来对这个值来进行运算并且调用scrollBy()方法来进行滑动
- 最后我们在ActionUp方法中对这个事件进行总的处理,首先我们在check()方法中判断用户是从上往下滑还是从下往上滑,然后通过滑动方向来计算值并且返回,接着我们用check()方法返回的数据来进行判断滑动的距离是否大于屏幕的三分之一,如果大于,则调用Scroller.startScroll()方法来进行滑动到下/上一屏,如果小于则返回原位。
至此自定义ViewGroup就完全结束了,运行一下程序,可以看到我们开始的效果已经呈现出来了。
最后
爱生活,爱小丽,爱Android