ViewAndroid开发程序员

Android-自定义ViewGroup(竖向导航页)

2016-08-17  本文已影响178人  c37d344afd22

自定义ViewGroup

自定义ViewGroup需要注意什么

首先我们要明确一点,ViewGroup就是用来存放View的容器,用来对子View进行管理,给子View添加规则,so

  1. 对子View进行测量,重写onMeasure()方法
  2. 对子View的布局进行控制,重写onLayout()方法
  3. 重写onTouchEvent(),dispatchEvent()来对点击触控事件进行控制

实例

长这个样子

从这个图就可以看的出来我们的需求

  1. 可以滑动
  2. 当滑动大于一定距离的时候显示下一个子View
  3. 如果小于一定距离那么就回到原位

首先这里可以看的出来,我们每一个子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();
    }
}
  1. 这里我们首先在ActionDown事件中获取到第一次碰到屏幕时候的Y轴偏移量
  2. 然后我们在ActionMove事件中来对这个值来进行运算并且调用scrollBy()方法来进行滑动
  3. 最后我们在ActionUp方法中对这个事件进行总的处理,首先我们在check()方法中判断用户是从上往下滑还是从下往上滑,然后通过滑动方向来计算值并且返回,接着我们用check()方法返回的数据来进行判断滑动的距离是否大于屏幕的三分之一,如果大于,则调用Scroller.startScroll()方法来进行滑动到下/上一屏,如果小于则返回原位。

至此自定义ViewGroup就完全结束了,运行一下程序,可以看到我们开始的效果已经呈现出来了。


最后

爱生活,爱小丽,爱Android

上一篇下一篇

猜你喜欢

热点阅读