ViewDragHelper使用简介

ViewDragHelper

使用ViewDragHelper可以轻松的实现拖动效果

简单Demo

这里写图片描述

XML中定义

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--这里考虑了Padding的情况-->
    <com.jacob.viewdraghelper.tianrui.Practice1View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">

        <TextView
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="#00ff00"/>
    </com.jacob.viewdraghelper.tianrui.Practice1View>
</RelativeLayout>

ViewDragHelper的编写

在onInterceptTouchEvent()和onTouchEvent()中使用ViewDragHelper接受事件

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // 取消拖动
                mViewDragHelper.cancel();
                break;
        }
        // 由ViewDragHelper决定是否拦截事件
        return mViewDragHelper.shouldInterceptTouchEvent(event);
    }

     @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

这些都是模板代码,要使用ViewDragHelper的话必须这么做.

编写CallBack代码

ViewDragHelper将MotionEvent帮我们处理成CallBack,我们只需处理CallBack中的逻辑就可以了,不用编写重复的拖动代码了.
最基本的有三个回调方法,使用这三个回调方法,能够处理大多数拖拽逻辑了


    /**
     * ViewDragHelper必备的回调方法
     */
    private class ViewDragCallBack extends ViewDragHelper.Callback {


        /**
         * 判断当前需要拖动哪个View
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            Log.d(TAG, "tryCaptureView: " + child.getClass().getSimpleName());
            return child == mTvTarget;
        }


        /**
         * 在水平方向上,对拖动的位置进行限制
         *
         * @param child 进行拖动的View
         * @param left  水平方向上的移动距离
         * @param dx    水平方向上的移动增量
         * @return 实际需要移动的值
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            final int leftBound = getLeft() + getPaddingLeft();
            if (left < leftBound) {
                return leftBound;
            }
            final int rightBound = getRight() - mTvTarget.getWidth() - getPaddingRight();
            if (left > rightBound) {
                return rightBound;
            }
            return left;
        }


        /**
         * 处理竖直方向上的拖动
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int topBound = getTop() + getPaddingTop();
            if (top < topBound) {
                return topBound;
            }
            final int bottomBound = getBottom() - mTvTarget.getHeight() - getPaddingBottom();
            if (top > bottomBound) {
                return bottomBound;
            }

            return top;
        }
    }

注意事项:

  1. 如果第一子View不是TextView而是一个Button,那么就会出现无法拖动的情况,原因就是子View默认属性带有Clickable,会消耗掉这次事件序列,所以必须在OnInterceptTouchEvent()中进行拦截,使用ViewDragHelper中已经带了默认方法帮我们进行处理
/**
 * 该方法用来描述View需要被拖动的距离,用此来适配子View具有Clickable属性的情况
 *
 * @param child 要拖动的View
 * @return View在该方向上拖动的距离
 */
@Override
public int getViewHorizontalDragRange(View child) {
    Log.d(TAG, "getViewHorizontalDragRange: ");
    // 这里返回整个水平区域
    return getMeasuredWidth() - child.getMeasuredWidth();
}

@Override
public int getViewVerticalDragRange(View child) {
    return getMeasuredHeight() - child.getMeasuredHeight();
}

Demo1完整代码


/**
 * Created by yangtianrui on 17-8-3.
 * ViewDragHelper的基本使用
 */

public class Practice1View extends LinearLayout {

    private static final String TAG = "tianrui";

    // 需要拖动的目标View
    private TextView mTvTarget;

    private ViewDragHelper mViewDragHelper;


    public Practice1View(Context context) {
        this(context, null);
    }

    public Practice1View(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Practice1View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mViewDragHelper = ViewDragHelper.create(this, 1F, new ViewDragCallBack());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        Log.d(TAG, "onFinishInflate: ");
        mTvTarget = (TextView) getChildAt(0);
        if (mTvTarget == null) {
            throw new IllegalStateException("first child must be TextView.");
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // 取消拖动
                mViewDragHelper.cancel();
                break;
        }
        // 由ViewDragHelper决定是否拦截事件
        return mViewDragHelper.shouldInterceptTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    /**
     * ViewDragHelper必备的回调方法
     */
    private class ViewDragCallBack extends ViewDragHelper.Callback {


        /**
         * 判断当前需要拖动哪个View
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            Log.d(TAG, "tryCaptureView: " + child.getClass().getSimpleName());
            return child == mTvTarget;
        }


        /**
         * 在水平方向上,对拖动的位置进行限制
         *
         * @param child 进行拖动的View
         * @param left  水平方向上的移动距离
         * @param dx    水平方向上的移动增量
         * @return 实际需要移动的值
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            final int leftBound = getLeft() + getPaddingLeft();
            if (left < leftBound) {
                return leftBound;
            }
            final int rightBound = getRight() - mTvTarget.getWidth() - getPaddingRight();
            if (left > rightBound) {
                return rightBound;
            }
            return left;
        }


        /**
         * 处理竖直方向上的拖动
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int topBound = getTop() + getPaddingTop();
            if (top < topBound) {
                return topBound;
            }
            final int bottomBound = getBottom() - mTvTarget.getHeight() - getPaddingBottom();
            if (top > bottomBound) {
                return bottomBound;
            }

            return top;
        }


        /**
         * 该方法用来描述View需要被拖动的距离,用此来适配子View具有Clickable属性的情况
         *
         * @param child 要拖动的View
         * @return View在该方向上拖动的距离
         */
        @Override
        public int getViewHorizontalDragRange(View child) {
            Log.d(TAG, "getViewHorizontalDragRange: ");
            // 这里返回整个水平区域
            return getMeasuredWidth() - child.getMeasuredWidth();
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            return getMeasuredHeight() - child.getMeasuredHeight();
        }
    }


    @Override
    public void computeScroll() {
        super.computeScroll();
        // 如果需要ViewDragHelper进行惯性滑动的话需要使用
        // 下面代码仍然是模板代码
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

}

Demo2 使用ViewDragHelper实现惯性滑动

ViewDragHelper内部使用Scroller实现滑动,所以需要重写View的ComputeScroll()方法,

该方法的重写方式如下:



    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

在onViewRelease()中实现惯性滑动逻辑

使用 mViewDragHelper.settleCapturedViewAt( left, top)将一个View进行惯性滑动,同时记得调用invalidate()进行重绘


        /**
         * 手指抬起时回调,一般用于实现惯性滑动
         *
         * @param releasedChild 拖动的View
         * @param xvel          x方向的速度
         * @param yvel          y方向的速度
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            // 定位到top/middle/bottom三个位置
            final int top = releasedChild.getTop();
            Log.d(TAG, "onViewReleased: " + String.format("mTop=%d, mMiddle=%d, mBottom=%d,yVel=%f, height=%d", mTop, mMiddle, mBottom, yvel, releasedChild.getHeight()));
            // 向下滑动
            if (yvel > 0) {
                if (top < mMiddle) {
                    mViewDragHelper.settleCapturedViewAt(0, mMiddle);
                } else if (top < mBottom) {
                    mViewDragHelper.settleCapturedViewAt(0, mBottom);
                }
            } else if (yvel < 0) {
                // 向上滑动
                if (top < mMiddle) {
                    mViewDragHelper.settleCapturedViewAt(0, mTop);
                } else if (top < mBottom) {
                    mViewDragHelper.settleCapturedViewAt(0, mMiddle);
                }
            }


            invalidate();
        }
    }

Demo2完整代码


/**
 * Created by yangtianrui on 17-8-5.
 * ViewDragHelper实现惯性滑动
 */

public class Practice2View extends LinearLayout {

    private static final String TAG = "tianrui";

    private ViewDragHelper mViewDragHelper;
    private View mTarget;

    private int mTop;
    private int mMiddle;
    private int mBottom;


    public Practice2View(Context context) {
        this(context, null);
    }

    public Practice2View(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Practice2View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mViewDragHelper = ViewDragHelper.create(this, 1F, new ViewDragHelperCallBack());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTarget = getChildAt(0);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mTop = getTop();
        mMiddle = (getBottom() - getTop()) / 2 - mTarget.getMeasuredHeight();
        mBottom = getBottom() - mTarget.getMeasuredHeight();
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mViewDragHelper.cancel();
                break;
        }
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }


    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    private class ViewDragHelperCallBack extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mTarget;
        }


        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if (top < getTop()) {
                return getTop();
            }
            if (top > (getBottom() - child.getHeight())) {
                return getBottom() - child.getHeight();
            }

            return top;
        }


        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (left < getLeft()) {
                return getLeft();
            }
            if (left > (getRight() - child.getWidth())) {
                return getRight() - child.getWidth();
            }

            return left;
        }


        /**
         * 手指抬起时回调,一般用于实现惯性滑动
         *
         * @param releasedChild 拖动的View
         * @param xvel          x方向的速度
         * @param yvel          y方向的速度
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            // 定位到top/middle/bottom三个位置
            final int top = releasedChild.getTop();
            Log.d(TAG, "onViewReleased: " + String.format("mTop=%d, mMiddle=%d, mBottom=%d,yVel=%f, height=%d", mTop, mMiddle, mBottom, yvel, releasedChild.getHeight()));
            // 向下滑动
            if (yvel > 0) {
                if (top < mMiddle) {
                    mViewDragHelper.settleCapturedViewAt(0, mMiddle);
                } else if (top < mBottom) {
                    mViewDragHelper.settleCapturedViewAt(0, mBottom);
                }
            } else if (yvel < 0) {
                // 向上滑动
                if (top < mMiddle) {
                    mViewDragHelper.settleCapturedViewAt(0, mTop);
                } else if (top < mBottom) {
                    mViewDragHelper.settleCapturedViewAt(0, mMiddle);
                }
            }


            invalidate();
        }
    }

}

参考http://blog.csdn.net/lmj623565791/article/details/46858663

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值