ViewDragHelper使用简介

xiaoxiao2021-02-28  125

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; } }

注意事项:

如果第一子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

转载请注明原文地址: https://www.6miu.com/read-37708.html

最新回复(0)