View的滑动冲突深入理解

由于这部分的内容涉及的底层知识较多,没有读过源码小伙伴相对比较难以理解。许多小伙伴遇到滑动冲突的时候只是去知其然而不是去知其所以然。其实大家没必要害怕去接触复杂的内容,其实他们也都是由多个细节方面的特点堆积形成的。就此部分的内容谈一谈我自己的看法,期待各位的不吝赐教。

滑动冲突的产生

那么滑动冲突时如何产生的呢?界面中只要在内外俩层同时可以滑动的时候就会产生滑动冲突,导致内外俩层只有一层可以滑动。

场景一:外部和内部俩层滑动方向不一致


内外层的滑动效果可以通过俩种方式来实现,ViewPager + Fragment和ScrollView + Fragment。对于前者来说系统在内部已经解决了滑动冲突,而后者需要手动解决。这就需要小伙伴们了解一些基础的事件分发机制的知识。

场景二:外部和内部俩层滑动方向一致


实现方式同场景一,不同的是因为内外俩层的滑动方向一致,也就是说当手指滑动的时候,系统无法确定用户是想让那一层滑动。进而会导致要么只有一层滑动,要么内外俩层都可以滑动但是比较卡顿。

场景三:主要是针对场景一和二的嵌套


就是针对场景一和场景二的嵌套。其实也没有看起来这么复杂,可以理解为几个冲突的叠加。简单来说就是将其拆分成多个场景二或者场景一来处理。

滑动冲突的解决思路

对于场景一来说,滑动类型(水平,垂直)可以根据滑动路径与水平方向的夹角,或者是水平方向和垂直方向的距离差(dy - dx),或者是水平和垂直方向上的速度差,然后根据滑动是水平滑动还是竖直滑动进一步取决于谁来拦截当前事件。对于场景二,场景三来说比较特殊,无法根据场景一的思路来解决。一般是可以在业务找到一个突破点进行相应的处理。这里将不再赘述。


滑动冲突的解决方式

针对上述的场景一般有俩种方式去解决。分别是外部拦截内部拦截

外部拦截(父容器优先)就是由父容器首先决定是否消耗事件,然后才会传递给子元素。相比内部拦截更简单,也符合View的事件分发机制,是解决滑动冲突的优先选择。通过重写父容器的onInterceptTouchEvent方法实现。针对不同的滑动冲突,只需要修改父容器需要消耗此事件的条件即可,外部拦截的逻辑框架如下:

public boolean onInterceptTouchEvent(MotionEvent event) {

boolean intercepted = false;

int x =  (int)event.getX();

int y =  (int)event.getY();

switch(event.getAction()) {

case MotionEvent.ACTION_DOWN: {

intercepted = false;

//如果为true的话子元素将接收不到任何事件

if(!scroller.isFinished()) {

scroller.abortAnimation();

intercepted = true;

}

break;

}

caseMotionEvent.ACTION_MOVE: {

int tempX = x-lastXIntercept;

int tempY = y-lastYIntercept;

//是否是水平滑动

if(Math.abs(tempX) >Math.abs(tempY)) {

intercepted = true;

}else{

intercepted = false;

}

break;

}

case MotionEvent.ACTION_UP: {

intercepted = false;//一般不去拦截UP事件

break;

}

default:

break;

}

lastX = x;

lastY = y;

lastXIntercept = x;

lastYIntercept = y;

returnintercepted;

}

内部拦截(子元素优先)就是父容器默认不去拦截任何事件,所有的事件都传递给子元素,如果子元素需要消耗该事件就直接消耗,否则会通过重写子元素的dispatchTouchEvent方法将事件传递给父容器处理。针对不同的滑动策略只需修改对应的条件即可,内部拦截的逻辑框架如下:

public boolean dispatchTouchEvent(MotionEvent event){

int x = (int) event.getX();

int y = (int) event.getY();

switch(event.getAction()) {

case MotionEvent.ACTION_DOWN: {

horizontalScrollView.requestDisallowInterceptTouchEvent(true);

break;

}

case MotionEvent.ACTION_MOVE: {

int tempX = x - lastX;

int tempY = y - lastY;

if(Math.abs(tempX) > Math.abs(tempY)) {

horizontalScrollView.requestDisallowInterceptTouchEvent(false);

}

break;

}

case MotionEvent.ACTION_UP: {

break;

}

default:

break;

}

lastX = x;

lastY = y;

return super.dispatchTouchEvent(event);

}

“MotionEvent.ACTION_DOWN: {// 必须返回false,否则后续事件无法传递到子元素”------why?

这个问题涉及到事件分发机制的一些知识。我尽量通俗的去分析,方便大家伙儿的理解。你有没有想过,当你单击一个按钮的时候系统是如何确定被单击的组件的呢?是这样的,事件一旦被触发的话最先传递给当前的Activity,由它的dispatchTouchEvent()来完成事件的分发。具体工作由它内部的Window将事件传递给Decor View(顶级父容器),再由顶级父容器传递给子元素来实现的。对于有使用过标签优化界面的同学一定听说过最外层的那个神秘的FrameLayout。是的,它就是Decor View

对于单个View来说,由于它没有子元素无法再向下传递,所以只能自己决定是否消耗事件。如果设置了OnTouchListener()的话,onTouch()就会被调用否则onTouchEvent()被调用。如果同时提供的话onTouch()会将onTouchEvent()屏蔽掉。如果该组件决定处理事件,则会终止Down事件的分发,并将接下来的所有事件都交给该组件直接进行处理(当然前提是事件可以传递给它)。在执行UP事件的时候,如果在onTouchEvent()中设置onClickListener的话onClick()也会被触发。

对于ViewGroup来说,如果不拦截当前事件的话,就会由子组件继续进行事件的分发。当所有组件都没有消耗事件的时候也就是说所有的onTouchEvent()方法中都返回false,然后就会触发Activity的onTouchEvent()事件来消耗事件。否则,之后全部的事件都由ViewGroup来处理,不会传递给子元素。

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

推荐阅读更多精彩内容