ViewDragHelper源码解析总结

对于整个触摸事件传递过程,我画了简要的流程图,方便日后快速回顾。
单点触摸,没有考虑边缘滑动检测的最简流程图
单点触摸,考虑了边缘滑动检测的流程图

多点触摸情况我就没研究了,在这里忽略~
三个开启自动滚动的方法:
settleCapturedViewAt(int finalLeft, int finalTop)以松手前的滑动速度为初速动,让捕获到的View自动滚动到指定位置。只能在Callback的onViewReleased()中调用。
flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop)以松手前的滑动速度为初速动,让捕获到的View在指定范围内fling。只能在Callback的onViewReleased()中调用。
smoothSlideViewTo(View child, int finalLeft, int finalTop)指定某个View自动滚动到指定的位置,初速度为0,可在任何地方调用。

Callback的各个方法总结:
void onViewDragStateChanged(int state)拖动状态改变时会调用此方法,状态state有STATE_IDLE、STATE_DRAGGING、STATE_SETTLING三种取值。它在setDragState()里被调用,而setDragState()被调用的地方有tryCaptureViewForDrag()成功捕获到子View时shouldInterceptTouchEvent()的ACTION_DOWN部分捕获到
shouldInterceptTouchEvent()的ACTION_MOVE部分捕获到
processTouchEvent()的ACTION_MOVE部分捕获到

调用settleCapturedViewAt()、smoothSlideViewTo()、flingCapturedView()时
拖动View松手时(processTouchEvent()的ACTION_UP、ACTION_CANCEL)
自动滚动停止时(continueSettling()里检测到滚动结束时)
外部调用abort()时

void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)正在被拖动的View或者自动滚动的View的位置改变时会调用此方法。在dragTo()里被调用(正在被拖动时)
在continueSettling()里被调用(自动滚动时)
外部调用abort()时被调用

void onViewCaptured(View capturedChild, int activePointerId)tryCaptureViewForDrag()成功捕获到子View时会调用此方法。在shouldInterceptTouchEvent()的ACTION_DOWN里成功捕获
在shouldInterceptTouchEvent()的ACTION_MOVE里成功捕获
在processTouchEvent()的ACTION_MOVE里成功捕获
手动调用captureChildView()

void onViewReleased(View releasedChild, float xvel, float yvel)拖动View松手时(processTouchEvent()的ACTION_UP)或被父View拦截事件时(processTouchEvent()的ACTION_CANCEL)会调用此方法。
void onEdgeTouched(int edgeFlags, int pointerId)ACTION_DOWN或ACTION_POINTER_DOWN事件发生时如果触摸到监听的边缘会调用此方法。edgeFlags的取值为EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM的组合。
boolean onEdgeLock(int edgeFlags)返回true表示锁定edgeFlags对应的边缘,锁定后的那些边缘就不会在onEdgeDragStarted()被通知了,默认返回false不锁定给定的边缘,edgeFlags的取值为EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM其中之一。
void onEdgeDragStarted(int edgeFlags, int pointerId)ACTION_MOVE事件发生时,检测到开始在某些边缘有拖动的手势,也没有锁定边缘,会调用此方法。edgeFlags取值为EDGE_LEFT、EDGE_TOP、EDGE_RIGHT、EDGE_BOTTOM的组合。可在此手动调用captureChildView()触发从边缘拖动子View的效果。
int getOrderedChildIndex(int index)在寻找当前触摸点下的子View时会调用此方法,寻找到的View会提供给tryCaptureViewForDrag()来尝试捕获。如果需要改变子View的遍历查询顺序可改写此方法,例如让下层的View优先于上层的View被选中。
int getViewHorizontalDragRange(View child)、int getViewVerticalDragRange(View child)返回给定的child在相应的方向上可以被拖动的最远距离,默认返回0。ACTION_DOWN发生时,若触摸点处的child消费了事件,并且想要在某个方向上可以被拖动,就要在对应方法里返回大于0的数。被调用的地方有三处:在checkTouchSlop()中被调用,返回值大于0才会去检查mTouchSlop。在ACTION_MOVE里调用tryCaptureViewForDrag()之前会调用checkTouchSlop()。如果checkTouchSlop()失败,就不会去捕获View了。
如果ACTION_DOWN发生时,触摸点处有子View消费事件,在shouldInterceptTouchEvent()的ACTION_MOVE里会被调用。如果两个方向上的range都是0(两个方法都返回0),就不会去捕获View了。
在调用smoothSlideViewTo()时被调用,用于计算自动滚动要滚动多长时间,这个时间计算出来后,如果超过最大值,最终时间就取最大值,所以不用担心在getView[Horizontal|Vertical]DragRange里返回了不合适的数导致计算的时间有问题,只要返回大于0的数就行了。

boolean tryCaptureView(View child, int pointerId)在tryCaptureViewForDrag()中被调用,返回true表示捕获给定的child。tryCaptureViewForDrag()被调用的地方有shouldInterceptTouchEvent()的ACTION_DOWN里
shouldInterceptTouchEvent()的ACTION_MOVE里
processTouchEvent()的ACTION_MOVE里

int clampViewPositionHorizontal(View child, int left, int dx)、int clampViewPositionVertical(View child, int top, int dy)child在某方向上被拖动时会调用对应方法,返回值是child移动过后的坐标位置,clampViewPositionHorizontal()返回child移动过后的left值,clampViewPositionVertical()返回child移动过后的top值。两个方法被调用的地方有两处:在dragTo()中被调用,dragTo()在processTouchEvent()的ACTION_MOVE里被调用。用来获取被拖动的View要移动到的位置。
如果ACTION_DOWN发生时,触摸点处有子View消费事件,在shouldInterceptTouchEvent()的ACTION_MOVE里会被调用。如果两个方向上返回的还是原来的left和top值,就不会去捕获View了。

案例参考
在这里列举一部分对ViewDragHelper的应用案例,大家自己剖析它们的源码来实践巩固。
YoutubeLayout,这是最简单的Demo
QQ5.x侧滑菜单ResideLayout
SwipeBackLayoutSwipeBack
SlidingUpPanel
DrawerLayout

其他关于ViewDragHelper的分析文章
Each Navigation Drawer Hides a ViewDragHelper,文中的源码就是上面的YoutubeLayout
ViewDragHelper详解,这是上面文章的简略中文版

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容