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

笔者是面霸,面试200+场       当过考官:面过别人300+场     去过500强,也呆过初创公司。

关注我就能达到大师级水平,这话我终于敢说了, 年薪60万不是梦!

斩获腾讯、华为、字节跳动,蚂蚁金服,oppo,VIVO,安卓岗offer!我有一套速通大厂技巧分享给你!

请问的格式怎么排版??每次写完都变格式了,非常影响阅读。非常苦恼


面试五连环

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

什么时候用内部拦截?   主要用内部拦截,系统里面的,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

你可能感兴趣的:(3.Android View滑动冲突的完美解决方案 上百次项目实践沉淀 RecyclewView各种滑动冲突解决)