Android Scroll分析(三)——ViewDragHelper

Google在其support库中为我们提供了DrawerLayout和SlidingPaneLayout两个布局来帮助我们实现侧边栏滑动的效果。在这两个功能强大的布局后面,有一个鲜为人知却功能强大的类——ViewDragHelper。通过ViewDragHelper,基本可以实现各种不同的滑动、拖放需求。

如何使用ViewDragHelper创建一个滑动布局,在下面的例子中,准备实现类似QQ滑动侧边栏的布局,初始时显示内容页面,当用户手指滑动超过一段距离时,内容界面侧滑显示菜单界面,效果如下图:

这里写图片描述

具体的代码如何实现:

初始化ViewDragHelper

ViewDragHelper通常定义在一个ViewGroup的内部,并通过其静态工厂方法进行初始化,代码如下:

mViewDragHelper = ViewDragHelper.create(this, mCallback);

它的第一个参数是要监听的View,通常需要是一个ViewGroup,即parentView;第二个参数是一个Callback回调,这个回调就是整个ViewDragHelper的逻辑核心。

拦截事件

接下来需要重写事件的拦截方法,将事件传递给ViewDragHelper进行处理,代码如下:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //将触摸事件传递给ViewDragHelper,此操作必不可少
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
处理computeScroll()

使用ViewDragHelper同样需要重写computeScroll()方法,因为ViewDragHelper内部也是通过Scroller来实现平滑移动的。模板代码如下:

    @Override
    public void computeScroll() {
        if(mViewDragHelper.continueSettling(true))
            ViewCompat.postInvalidateOnAnimation(this);
    }
处理回调Callback

首先创建一个ViewDragHelper.Callback,代码如下:

    private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {

        /**
         * 何时开始检测触摸事件
         * 该方法确定该ViewGroup中哪一个子View可以被移动
         * 该ViewGroup中有两个子View——MenuView与MainView
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //如果当前触摸的child是mMainView时开始检测,也就是只有mMainView可以被拖动
            return child == mMainView;
        }

        // 触摸到View后回调
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
        }

        // 当拖拽状态改变后回调,比如idle,dragging
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        // 当位置改变的时候回调,常用于滑动时更改scale进行缩放等效果
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }

        /**
         * 处理垂直滑动。如果要实现垂直方向滑动效果,该方法必须重写。因为它的默认值为0,即不发生滑动。
         * @param child
         * @param top 垂直方向上child移动的距离
         * @param dy 比较前一次的增量
         * @return
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }

        /**
         * 处理水平滑动。如果要实现水平方向滑动效果,该方法必须重写。因为它的默认值为0,即不发生滑动。
         * 通常情况下,只需返回left即可,当但需要更加精确地计算padding等属性的时候,就需要对left进行处理,
         * 并返回合适大小的值。
         * @param child
         * @param left 水平方向上child移动的距离
         * @param dx 比较前一次的增量
         * @return
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        /**
         * 该方法拖动结束后调用,通过重写该方法,可以非常简单地实现当手指离开屏幕后要实现的操作,这个方法内部是
         * 通过Scroller类来实现的,这也是重写computeScroll()方法的原因
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            //手指抬起后缓慢移动到指定位置
            if(mMainView.getLeft() < 500){
                //关闭菜单
                //相当于Scroller的startScroll方法
                mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
            }else{
                //打开菜单
                mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
                ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
            }
        }
    };
最后完善

在自定义ViewGroup的onFinishInflate()方法中,按顺序将子View分别定义成MenuView与MainView,并在onSizeChanged()方法中获得View的宽度。当需要根据View的宽度来处理滑动后的效果,就可以使用这个值来进行判断,代码如下:

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mMainView = getChildAt(1);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = mMenuView.getMeasuredWidth();
    }

代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值