3.Android View滑动冲突的完美解决方案 上百次项目实践沉淀 RecyclewView各种滑动冲突解决


面试五连环

1.滑动冲突的几种情况

2.滑动冲突解决方案和源码分析

3.滑动冲突的几种解决方案的使用场景?什么时候用内部拦截?

4.webview滑动冲突如何解决?x5 webview如何拦截?

5.滑动冲突实践

1.滑动冲突的几种情况

1).点击时间和滑动事件(比如悬浮球)

2).外层与内层滑动方向不一致,外层ViewGroup是可以横向滑动的,内层View是可以竖向滑动的(类似ViewPager,每个页面里面是ListView)

3).外层与内层滑动方向一致,外层ViewGroup是可以竖向滑动的,内层View同样也是竖向滑动的(类似ScrollView包裹ListView)

2.滑动冲突解决方案和源码分析

针对上面第一种场景,onTouch和onTouchEvent事件,即可

针对上面第二种场景,由于外部与内部的滑动方向不一致,那么我们可以根据当前滑动方向,水平还是垂直来判断这个事件到底该交给谁来处理。至于如何获得滑动方向,我们可以得到滑动过程中的两个点的坐标。如竖直距离与横向距离的大小比较;

针对第三种场景,由于外部与内部的滑动方向一致,那么不能根据滑动角度、距离差或者速度差来判断。这种情况下必需通过业务逻辑来进行判断。比较常见ScrollView嵌套了ListView。

套路一 外部拦截法:

即父View根据需要对事件进行拦截。逻辑处理放在父View的onInterceptTouchEvent方法中。我们只需要重写父View的onInterceptTouchEvent方法,并根据逻辑需要做相应的拦截即可。

publicbooleanonInterceptTouchEvent(MotionEvent event){booleanintercepted =false;intx = (int) event.getX();inty = (int) event.getY();switch(event.getAction()) {caseMotionEvent.ACTION_DOWN: {                intercepted =false;break;            }caseMotionEvent.ACTION_MOVE: {if(满足父容器的拦截要求) {                    intercepted =true;                }else{                    intercepted =false;                }break;            }caseMotionEvent.ACTION_UP: {                intercepted =false;break;            }default:break;        }        mLastXIntercept = x;        mLastYIntercept = y;returnintercepted;    }

上面伪代码表示外部拦截法的处理思路,需要注意下面几点(down和up一样不拦截,move根据条件拦截)

ACTION_DOWN 一定返回false,不要拦截它,否则根据View事件分发机制,后续ACTION_MOVE 与 ACTION_UP事件都将默认交给父View去处理!

在ACTION_MOVE方法中进行判断,根据业务逻辑需要,如果需要父View处理则返回true,否则返回false,事件分发给子View去处理。

原则上ACTION_UP也需要返回false,如果返回true,并且滑动事件交给子View处理,那么子View将接收不到ACTION_UP事件,子View的onClick事件也无法触发。而父View不一样,如果父View在ACTION_MOVE中开始拦截事件,那么后续ACTION_UP也将默认交给父View处理!

套路二 内部拦截法:

即父View不拦截任何事件,所有事件都传递给子View,子View根据需要决定是自己消费事件还是给父View处理。这需要子View使用requestDisallowInterceptTouchEvent方法才能正常工作。下面是子View的dispatchTouchEvent方法的伪代码:

父View需要重写onInterceptTouchEvent方法:

为什么要重写父类的onInterceptTouchEvent?因为默认不拦截,你需要的是拦截它

publicbooleanonInterceptTouchEvent(MotionEvent event){intaction = event.getAction();if(action == MotionEvent.ACTION_DOWN) {//down不拦截,给子类returnfalse;        }else{//拦截returntrue;        }    }

重写子类的dispatchTouchEvent()方法

  public boolean dispatchTouchEvent(MotionEvent event) {intx = (int) event.getX();inty = (int) event.getY();switch(event.getAction()) {caseMotionEvent.ACTION_DOWN: {                parent.requestDisallowInterceptTouchEvent(true);break;            }caseMotionEvent.ACTION_MOVE: {intdeltaX = x - mLastX;intdeltaY = y - mLastY;if(父容器需要此类点击事件) {                    parent.requestDisallowInterceptTouchEvent(false);                }break;            }caseMotionEvent.ACTION_UP: {break;            }default:break;        }        mLastX = x;        mLastY = y;returnsuper.dispatchTouchEvent(event);    }

内部拦截法要求父View不能拦截ACTION_DOWN事件,由于ACTION_DOWN不受FLAG_DISALLOW_INTERCEPT标志位控制,一旦父容器拦截ACTION_DOWN那么所有的事件都不会传递给子View。

滑动策略的逻辑放在子View的dispatchTouchEvent方法的ACTION_MOVE中,如果父容器需要获取点击事件则调用 parent.requestDisallowInterceptTouchEvent(false)方法,让父容器去拦截事件。

 你好,内部拦截法中父容器 onInterceptTouchEvent 方法 ACTION_MOVE、ACTION_UP返回true,子view 的dispatchTouchEvent 方法中ACTION_MOVE、ACTION_UP事件还能接受到吗,新手,这里不是很懂,求指教?

原理分析:

   内部拦截法也叫View分发反向制约的方法?

   拦截不拦截,由2个东西决定的。一个是requestDisllowIntercepter和onInterceptTouchEvent()2个决定的。

源码如下。

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

if (mInputEventConsistencyVerifier !=null) {

mInputEventConsistencyVerifier.onTouchEvent(ev, 1);

    }

// If the event targets the accessibility focused view and this is it, start

// normal event dispatch. Maybe a descendant is what will handle the click.

    if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {

ev.setTargetAccessibilityFocus(false);

    }

if (actionMasked == MotionEvent.ACTION_DOWN

        ||mFirstTouchTarget !=null) {

final boolean disallowIntercept = (mGroupFlags &FLAG_DISALLOW_INTERCEPT) !=0;

    if (!disallowIntercept) {

intercepted = onInterceptTouchEvent(ev);

        ev.setAction(action); // restore action in case it was changed

    }else {

intercepted =false;

    }

原因:但是子元素可以通过requestDisallowInterceptTouchEvent来干预父元素的分发过程,但是down事件除外(因为down事件方法里,会清除所有的标志位)。

3.滑动冲突的几种解决方案的使用场景

1).onTouch和绘制不管。只管事件的拦截和分发,所以重要的方法是:VIewGourp的onInterceptTouchEvent和View的dispatchTouchEvent

2.down,move ,Up,都是否需要拦截?

ACTION_DOWN,都不要拦截子类

在这里,首先down事件父容器必须返回false ,因为若是返回true,也就是拦截了down事件,

那么后续的move和up事件就都会传递给父容器,子元素就没有机会处理事件了。其次是up事件也返回了false,一是因为up事件对父容器没什么意义,其次是因为若事件是子元素处理的,却没有收到up事件会让子元素的onClick事件无法触发。

3.要在MotionEvent.ACTION_MOVE根据情况,是父类滑动还是子类滑动

4.2种方式都要重写父View需要重写onInterceptTouchEvent方法:

可以看出外部拦截法实现起来更加简单,而且也符合View的正常事件分发机制,所以推荐使用外部拦截法(重写父View的onInterceptTouchEvent,父View决定是否拦截)来处理滑动冲突

外部拦截下面列子:

1.down 不拦截,否则up收不到,点击事件也会没有

2.move 更加业务判定。得到子view,滑动的距离和item的位置决定

3.up     不拦截

外部拦截法代码:

@OverridepublicbooleanonInterceptTouchEvent(MotionEventevent){boolean intercepted=false;inty=(int)event.getY();switch(event.getAction()){caseMotionEvent.ACTION_DOWN:{nowY=y;intercepted=super.onInterceptTouchEvent(event);break;}caseMotionEvent.ACTION_MOVE:{if(mListView.getFirstVisiblePosition()==0&&y>nowY){intercepted=true;break;}elseif(mListView.getLastVisiblePosition()==mListView.getCount()-1&&y<nowY){intercepted=true;break;}intercepted=false;break;}caseMotionEvent.ACTION_UP:{intercepted=false;break;}default:break;}returnintercepted;}

什么时候用内部拦截?   主要用内部拦截,系统里面的,horscorrlview和,比如recyleview.

什么时候用外部拦截?   主要看你哪个View是你自己的

面试有一个人问道:

一个ScrowView(父类)和一个RecycleView(子类)

他说重写子类的onIntecepter方法,让子类拦截,消费掉

5.滑动冲突实践

实战案例一:

ListView下拉刷新,需要ListView自身滑动,

但是当滑动到头部时需要ListView和Header一起滑动,也就是整个父容器的滑动。如果不处理好滑动冲突,就会出现各种意想不到情况。

实战案例二:

自定义ViewGroup实现瀑布流效果

https://www.cnblogs.com/qhyuan1992/p/5385335.html

实战案例三:

.ViewPager中嵌套ViewPager怎么处理滑动冲突?

1.重写canScroll()方法

2.自己手写

https://blog.csdn.net/weixin_43917449/article/details/86519726

实战案例四:

一个scorview和一个日期选择器

自己通过实践,进行重绘写的

private void doMove(MotionEvent event)

{

mMoveLen += (event.getY() -mLastDownY);

    if (mMoveLen >MARGIN_ALPHA *mMinTextSize /2)

{

// 往下滑超过离开距离

        moveTailToHead();

        mMoveLen =mMoveLen -MARGIN_ALPHA *mMinTextSize;

    }else if (mMoveLen < -MARGIN_ALPHA *mMinTextSize /2)

{

// 往上滑超过离开距离

        moveHeadToTail();

        mMoveLen =mMoveLen +MARGIN_ALPHA *mMinTextSize;

    }

mLastDownY = event.getY();

    invalidate();

}

参考:

https://www.jianshu.com/p/057832528bdd

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

推荐阅读更多精彩内容