Android 自定义View基础(三)--ViewGroup

本文主要是实现类似于ScrollView的一个控件,代码相当简单:

自定义ViewGroup通常需要重写onMeasure()来对子View进行测量,重写onLayout方法来确定子View的位置。重写onTouchEvent()方法增加响应事件。

我们实现的ViewGroup可以实现ScrollView所具有的上下滑动功能,但是在滑动的过程中,增加一个粘性的效果,即当一个子View向上滑动大于一定距离后,松开手指,它将自动向上滑动。显示下一个子View.

1.首先,对ViewGroup中的所有子View进行遍历。代码如下

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取子View个数
        int countView = getChildCount();
        for (int i = 0; i < countView; i++) {
            View childView = getChildAt(i);
            //测量所有子View的大小
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

2.接下来,就是对子View进行位置的设定。代码如下:

  @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        //设置ViewGroup的高度,高度为所有子View之和。
        MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
        params.height = mScreenHeigth * childCount;
        setLayoutParams(params);

        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                //设置子View的位置
                child.layout(l, i * mScreenHeigth, r, (i + 1) * mScreenHeigth);
            }
        }

    }

3.通过上面步骤,就可以将View放置到ViewGroup中,但此时的ViewGroup还不能响应任何事件。自然也不能滑动。因此需要重写onTouchEvent()方法。在ViewGroup中添加滑动事件,通常可以使用scrollBy(0,dy) 方法,让手指滑动的时候让ViewGroup中的所有子View也滑动dy.

   @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getRawY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = y;
                //记录触摸起点
                break;
            case MotionEvent.ACTION_MOVE:
                //判断是否在滑动中,如果是在滑动中,这个动画不执行
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                //获取到手势移动的距离
                int dy = lastY - y;
                //如果是第一个子View时,不能向下滑动
                if (getScrollY()  < 0) {
                    dy = 0;
                }
                  //如果是第最后一个子View时,不能向上滑动
                if (getScrollY()  > getHeight() - mScreenHeigth) {
                    dy = 0;
                }
                //视图滚动
                scrollBy(0, dy);
                lastY = y;
                break;
                    //这里需要处理这个点击事件,因此返回true
                    }
        return true;
        }

4.通过上面的代码已经实现滑动,但是我们在使用这个控件的时候,和ScrollView不同,所有的子View直接放到这个ViewGroup中。而且,使用子View使用match_parent属性。使用具体数值的时候会出现,下拉不松开上推,在下拉,松开后再无法进行滑动。这是因为getScrollY变为了负值。这时需要在getScrollY()+dy就可以了。
5.最后我们来实现粘性效果
代码如下:

   case MotionEvent.ACTION_UP:
                //记录触摸终点
                mEnd = getScrollY();
                int dScrollY = mEnd - mStart;
                //最关键的一些判断
                if (dScrollY > 0) {
                    if (dScrollY < mScreenHeigth / 3) {
                        mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                    } else {
                        mScroller.startScroll(0, getScrollY(), 0, mScreenHeigth - dScrollY);
                    }
                } else {
                    if (-dScrollY < mScreenHeigth / 3) {
                        mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                    } else {
                        mScroller.startScroll(0, getScrollY(), 0, -mScreenHeigth - dScrollY);
                    }
                }
                break;

完整代码



    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getRawY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = y;
                //记录触摸起点
                mStart = getScrollY();
                break;
            case MotionEvent.ACTION_MOVE:
                //判断是否在滑动中,如果是在滑动中,这个动画不执行
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                //获取到手势移动的距离
                int dy = lastY - y;
                if (getScrollY()  < 0) {
                    dy = 0;
                }
                if (getScrollY()  > getHeight() - mScreenHeigth) {
                    dy = 0;
                }
                //视图滚动
                scrollBy(0, dy);
                lastY = y;
                break;
            case MotionEvent.ACTION_UP:
                //记录触摸终点
                mEnd = getScrollY();
                int dScrollY = mEnd - mStart;

                if (dScrollY > 0) {
                    if (dScrollY < mScreenHeigth / 3) {
                        mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                    } else {
                        mScroller.startScroll(0, getScrollY(), 0, mScreenHeigth - dScrollY);
                    }
                } else {
                    if (-dScrollY < mScreenHeigth / 3) {
                        mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
                    } else {
                        mScroller.startScroll(0, getScrollY(), 0, -mScreenHeigth - dScrollY);
                    }
                }
                break;
        }
        postInvalidate();
        //这里需要处理这个点击事件,因此返回true
        return true;
    }
  @Override
    public void computeScroll() {

        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        }
    }

到这里就实现了我们想要的效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值