ViewDragHelper之手势操作神器
在Android中避免不了自己设置ViewGroup,来实现我们原生控件所不能满足的需求。尤其是复杂的ViewGroup实现,手势的解决是避免不了的。我们要针对不同的ViewGroup来实现不同的onInterceptTouchEvent与onTouchEvent事件等。
那么有没有什么简便的方法呢?答案是一定的,ViewDragHelper可以帮助我们处理负责的手势操作。它是官方所提供的一个专门为自己设置ViewGroup解决拖拽的手势类。下面是官方的原文引用说明
ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
Purpose
通过这篇文章你将会掌握以下几个知识点:
- ViewDragHelper的简单入门
- ViewDragHelper的关键API用途
- 使用ViewDragHelper实现view的拖拽
use
首先需要构建ViewDragHelper的实例,通过它的静态create方法生成
mViewDragHelper = ViewDragHelper.create(this, object : ViewDragHelper.Callback() { override fun tryCaptureView(child: View?, pointerId: Int): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }})主要参数为ViewGroup与ViewDragHelper.Callback。Callback是对view操作的回调,绝对多数手势操作都是在这个回调中完成。tryCaptureView方法是它唯一的笼统方法,默认需要实现。根据参数child判断客户触摸的view能否可以进行后续操作。
为了让ViewDragHelper帮助我们简化手势操作,所以还需为它传入相关的MotionEvent。
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { return mViewDragHelper.shouldInterceptTouchEvent(ev)} override fun onTouchEvent(event: MotionEvent?): Boolean { mViewDragHelper.processTouchEvent(event) return true}分别调用ViewDragHelper的shouldInterceptTouchEvent与processTouchEvent来简化手势的操作判断。将手势操作一律交由ViewDragHelper来实现。
假如要解决惯性滑动,再重写computeScroll方法
override fun computeScroll() { if (mViewDragHelper.continueSettling(true)) { invalidate() }}ViewDragHelper的基本使用就是这么多,算了一下也就十几行代码。相对于自己实现其中的细节,减少了许多代码。所以假如你想快速简便的实现手势操作,ViewDragHelper是不二之选。
Demo
下面通过一个实例来对ViewDragHelper的主要Api的使用进行分析。首先来看下要实现的初步效果。

有三个view,分别可以进行水平、竖直与任意位置滑动。而要实现这种效果,需要用到的就是ViewDragHelper.Callback中的回调方法。
tryCaptureView
该方法返回布尔值来判断当前操作的view能否可以进行捕获。demo中需要这三个view都能被捕获到,所以很简单只要与参数的child做比照就可。
override fun tryCaptureView(child: View?, pointerId: Int): Boolean { if (mLeft == 0 || mTop == 0){ mLeft = mFlexibleView.left mTop = mFlexibleView.top } return child == mHorizontalView || child == mVerticalView || child == mFlexibleView}初始化了任意滑动view的初始left与top,以便后续使用。
clampViewPositionHorizontal
有了view的捕获判断,接下来对水平方向的操作进行判断。
override fun clampViewPositionHorizontal(child: View?, left: Int, dx: Int): Int { if (child != mVerticalView) { return left } return child.left}它的各个参数与返回值
- child:当前操作的view
- left: 将要到达的水平方向的距离
- dx: 相对于当前位置的偏移量
- return:所处的水平距离
因为只有竖直方向的view不能随便移动,所以当捕获的view为竖直方向时就直接返回child.left原来的位置;反之返回left。
clampViewPositionVertical
对于竖直方向的操作判断与水平方向同理,看下代码就可。
override fun clampViewPositionVertical(child: View?, top: Int, dy: Int): Int { if (child != mHorizontalView) { return top } return child.top}此时运行项目,该demo的功能基本完成,三个view都能预期拖动。只是要到达任意view拖动之后回到初始位置还需重写接下来的方法。
onViewReleased
override fun onViewReleased(releasedChild: View?, xvel: Float, yvel: Float) { if (releasedChild == mFlexibleView) { mViewDragHelper.settleCapturedViewAt(mLeft, mTop) invalidate() } else { super.onViewReleased(releasedChild, xvel, yvel) }}这是对view释放后的回调。假如要对view释放后的轨迹做改变可以在这方法中实现。
- releaseChild: 当前释放的view
- xvel: 水平方向的速度
- yvel: 竖直方向的速度
这里还使用到了settleCapturedViewAt方法,该方法的作用是将当前view定位到所给的坐标位置。内部会回到continueSettling方法,来实现滑动动画。
xvel与yvel可以用来实现释放view后的惯性移动操作。
从头到尾只使用了ViewDragHelper.Callback中的四个回调方法,就实现了demo中的拖拽效果。相对于自己实现,简单程度不言而喻。所以熟练使用ViewDragHelper不仅能提高我们的实现效率与代码质量还能减少出错率。
对于其它的Api都是些状态改变的回调,在实际中也用的少,手势的操作逻辑都不会在这些Api中实现,所以这里就不多详情。上面的demo view拖拽超出边界,假如要固定边界,只要在clampViewPositionHorizontal与clampViewPositionVertical中边界判断就可,具体实现留给读者思考。
下面有一个注意的点:假如使用Button或者者TextView设置了clickable=true,你会发现上面的Demo中的view不能操作了。主要起因是,假如view不消费触摸事件,则触摸事件将直接进入onTouchEvent,在Down事件中就已经确定了捕获的view;假如消费事件,会进入onInterceptTouchEvent判断能否可以捕获,而判断的过程会去调用getViewHorizontalDragRange与getViewVerticalDragRange,只有这两个回调方法返回大于0的值才能被捕获。所以要处理拖拽不动的问题,只要重写这两个方法。
override fun getViewHorizontalDragRange(child: View?): Int { return measuredWidth - (child?.measuredWidth?:0)} override fun getViewVerticalDragRange(child: View?): Int { return measuredHeight - (child?.measuredHeight?:0)}finally
ok,到这里ViewDragHelper已经完全入门了,并且关键的Api也已经了然于胸。下面是实际项目中使用ViewDragHelper的效果图,与饿了么的商品介绍界面效果相似。

上面的手势动画使用的就是ViewDragHelper,而用到的Api也是一律是文章中提到的。希望通过文章中的demo效果能够加深ViewDragHelper对大家的影响与对它的期望度,也希望在日常开发中能够帮助大家轻松处理手势问题。
文章中的代码都可以在Github获取到。使用时请将分支切换到feat_viewdraghelper_dev
精选文章
Bitmap的图片压缩汇总
Android Architecture Components Part2:LiveData
Android高仿微信之mvp实现(一)
ConstraintLayout使用汇总
关注

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » ViewDragHelper之手势操作神器