美文网首页
3.Android View滑动冲突的完美解决方案 上

3.Android View滑动冲突的完美解决方案 上

作者: 鹏城十八少 | 来源:发表于2021-04-09 16:51 被阅读0次

    笔者是面霸,面试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<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

    相关文章

      网友评论

          本文标题:3.Android View滑动冲突的完美解决方案 上

          本文链接:https://www.haomeiwen.com/subject/psqwkltx.html