【滑动冲突】常见情形及解决方案

安卓开发过程中滑动冲突的情形主要有2类:
  • 父view与子view的滑动方向不同,如:父view左右滑动,子view上下滑动或相反(ViewPage里面嵌套ListView)。这种情形是比较简单的,只需要根据不同的滑动动作进行相应的拦截与处理即可。
  • 父view与子view的滑动方向相同,即,父view左右,子view也左右(ViewPage里面嵌套可以缩放、移动的ImageView)。这种情形需要根据具体情况来进行拦截处理,比如父View在出现子View滑动到边缘的情况才进行拦截处理

滑动冲突解决策略的理论基础为安卓的 事件分发机制, 针对滑动冲突的解决策略有以下两种:
  • 一种是外部拦截法:即当事件满足滑动条件,通过父View的【onInterceptTouchEvent】方法对其进行拦截,拦截之后将直接进入父View的onTouchEvent进行事件消费,不会再传入下级view。
  • 二种是内部拦截法:即通过子View的【dispatchTouchEvent】方法接收到down事件,然后获取父View的requestDisallowInterceptTouchEvent方法禁止其onInterceptTouchEvent拦截,当满足父View滑动条件的时候才允许。这种方法需要父View不拦截down事件,因为拦截了down事件后所有子元素的触摸事件都会失效。
建议采用第一种方法,易于理解,不容易出错。


ViewPager里面嵌套ListView

实际测试后发现:原生根本就不会产生滑动冲突!
1、 左右滑动(ViewPager处理触摸事件)
795730-20170310155630373-401274373.png
  • 当ACTION_DOWN = 0,按下动作时,先是父ViewPager判断是否拦截,由于此时父ViewPager还不能判断是否要进行拦截,所以onInterceptTouchEvent返回false,即不拦截,所以ACTION_DOWN事件可以(必须、务必、一定)传到子ListView。
  • 当ACTION_MOVE = 2,移动动作时,在父ViewPager尚未识别出此手势是左右滑动item的动作之前(一般是根据滑动的速度和累计距离判断)onInterceptTouchEvent同样也返回false,即同样不拦截,所以最初的这些ACTION_MOVE事件同样也可以传到ListView。
  • 一旦父ViewPager识别出此手势是左右滑动item的动作(比如水平方向移动了足够长的距离)
    • 1、那么onInterceptTouchEvent将返回true(红色框位置),即开始拦截,所以之后的这些ACTION_MOVE事件将无法传到ListView;
    • 2、并且父ViewPager会在此时传给子View一个ACTION_CANCEL= 3的"取消动作",当子ListView收到此事件后便取消了之前对此手势的预处理;
    • 3、并且此后父ViewPager将不再调用onInterceptTouchEvent判断是否需要拦截(因为它认为此后的事件都是自己需要的)
  • 此后所有的ACTION_MOVE、ACTION_UP=1事件都交由父ViewPager的onTouchEvent处理了。

2、上下滑动( ListView 处理触摸事件)
795730-20170310155630998-1110889701.png
可以发现,父ViewPager的onInterceptTouchEvent始终返回false,即从不会拦截触摸事件 ,所以 上下滑动时的事件完全由 ListView处理(这是最原始的状态)。

PS:
  • 父ViewPager的onInterceptTouchEvent在事件分发过程中(即dispatchTouchEvent方法调用时)并非是每次都会被调用的,一旦父ViewPager识别出此手势是自己需要的或不是自己需要的,之后都将不会再调用onInterceptTouchEvent判断是否需要拦截。
  • 所以判断判断某个View是否拦截了手势,只需看其最后一次调用onInterceptTouchEvent时的返回值即可。
  • 并且,很容易理解,其最后一次调用之前调用onInterceptTouchEvent时的返回值肯定都是false。

ListView里面嵌套ViewPager

1、上下滑动( ListView 处理触摸事件)
795730-20170310155631701-191109432.png
 情况和上面的一样
2、左右滑动(ViewPager处理触摸事件)
795730-20170310155632092-73938155.png
情况同样和上面一样

ScrollView里面嵌套ListView

1、当ScrollView的内容没有超出屏幕时(也即ScrollView不需要上下滑动),不会产生滑动冲突(也即内部的ListView能正常滑动) ,所有 触摸事件都由 ListView处理。
 795730-20170310155632498-745133201.png

2、当ScrollView的内容超出屏幕时(也即ScrollView需要上下滑动),由于ListView也需要上下滑动,所以会产生滑动冲突(也即内部的ListView讲不能正常滑动) ,所有 触摸事件都由 ScrollView 处理
795730-20170310155633529-1231770528.png

SV嵌套LV滑动冲突 之 外部拦截法

外部拦截法即:重写父容器的onInterceptTouchEvent方法,当自己需要的时候就拦截,否则不拦截。 可以很容易理解,因为这和android自身的事件处理机制 逻辑是一模一样的。
795730-20170310155634654-3106321.png
核心代码为:
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercepted = false;
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN://down事件肯定不能拦截,拦截了后面的就收不到了
            intercepted = false;
            break;
        case MotionEvent.ACTION_MOVE:
            if (true) intercepted = false;//如果确定拦截了,就去自己的onTouchEvent里处理拦截之后的操作即可
            break;
        case MotionEvent.ACTION_UP:
            //up事件我们一般都是返回false的,一般父容器都不会拦截他, 因为up是事件的最后一步,这里返回true也没啥意义。
            //唯一的意义就是,如果父元素把up拦截了,将导致子元素收不到up事件,那子元素就肯定没有onClick事件触发了,这里的小细节 要想明白
            intercepted = false;
            break;
        default:
            break;
        }
        return intercepted;
    }

SV嵌套LV滑动冲突 之 内部拦截法

内部拦截法即: 父容器不做处理,在子View中调用getParent().requestDisallowInterceptTouchEvent(true),作用是:告诉父view,我的触摸事件由我自行处理,不要阻碍我
不过前提是:要保证父亲容器不能拦截down事件。
795730-20170310155634982-1601844654.png
 核心代码为:
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_MOVE:
            //这里指定在什么条件下,才会要求父View把触摸事件交给自己处理。注意这里是调用的【getParent().getParent()】
            if (true) getParent().getParent().requestDisallowInterceptTouchEvent(true);//这句话的作用是告诉父view,我的触摸事件由我自行处理,不要阻碍我
            break;
        case MotionEvent.ACTION_UP:
            getParent().getParent().requestDisallowInterceptTouchEvent(false);//个人感觉这行代码没啥用,因为父view本来就不会拦截
            break;
        }
        return super.dispatchTouchEvent(event);
    }

附件列表

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值