ViewDragHelper使用简介

来源:互联网 发布:网络电视遥控怎么开 编辑:程序博客网 时间:2024/05/21 11:31

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在该方向上拖动的距离 */@Overridepublic int getViewHorizontalDragRange(View child) {    Log.d(TAG, "getViewHorizontalDragRange: ");    // 这里返回整个水平区域    return getMeasuredWidth() - child.getMeasuredWidth();}@Overridepublic 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