滑动冲突问题的简单解决思路





http://blog.csdn.net/chunqiuwei/article/details/50232873

http://blog.csdn.net/chunqiuwei/article/details/50232873

http://blog.csdn.net/chunqiuwei/article/details/50232873




滑动冲突问题的简单解决思路

  439人阅读  评论(2)  收藏  举报
  分类:
 

其实之所以《从源码角度分析android事件分发处理机制》这篇博客,是因为在此之前一个Android群友遇到一个滑动冲突问题,然后帮助其解决过后才想起来要仔细分析研究,并完成了文章开头索索的那篇博客。。

该群友的应用问题场景是:一个FrameLayout,里面嵌套一个ListView.通过手指左右的滑动来显示和关闭FrameLayout。他滑动打开/关闭FrameLayout的效果是实现了,但是点击ListView的某一个item的时候,onItemClick事件始终不会执行。

该群友当时的处理方法:重写FrameLayout的onTouchEvent,使之返回true:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean onTouchEvent(MotionEvent event) {          
  2.         switch (event.getAction()) {  
  3.         case MotionEvent.ACTION_DOWN:  
  4.             ....  
  5.             break;  
  6.         case MotionEvent.ACTION_MOVE:  
  7.             if (向左滑动) {  
  8.                 //关闭FrameLayout  
  9.                 closeDraw();  
  10.             } else if (向右滑动) {  
  11.                 //打开FrameLayout  
  12.                 openDraw();  
  13.             }  
  14.             break;  
  15.         case MotionEvent.ACTION_UP:  
  16.             break;  
  17.   
  18.         }         
  19.         return true;  
  20. }  
  21.   
  22. public boolean onInterceptTouchEvent(MotionEvent event) {  
  23.        return true;  
  24. }  

很明显这样做的错误很明显,让FrameLayout直接返回true(在解决问题之前,这个是我的失误,我当初说你把事件拦截了,onInterceptTouchEvent返回true试试,刚开始貌似是解决关闭/打开FrameLayout的问题),这样的话事件都被FrameLayout这个父View来拦截处理了,这样ListView这个childView是肯定获取不到这个事件了,所以更无从谈起处理onItemClick了。所以解决问题的核心思路很简单: 处理左右滑动的时候让父类拦截和处理事件,当处理点击事件的时候让ListView获取事件即可。简而言之就是父类FrameLayout什么时候拦截什么时候不拦截事件的逻辑。

在《从源码角度分析android事件分发处理机制》这篇博客中详细的说明了android的事件分发拦截处理机制,下面略作总结,为本篇博客所用:

1)事件:从手指触摸手机屏幕开始到抬起会发起产生一系列列事件,每个事件单独去分析看待,它的传递顺序仍然是由parentView 通过调用dispatchTouchEvent进行分发给childView的顺序进行。

2)parentView在处理down事件的时候会遍历它的childrenView来寻找能处理事件的那个childView,即targetView.

3)找到targetView之后,down事件之后的事件序列都交给targetView进行处理。

接着上面的问题继续说明,既然每个事件都会由parentView分发给childView这样的顺序执行,这就说明parentView有优先选择是否拦截和处理事件的权利,所以按照上面的说明,解决该群友的问题我用到了如下方法:重写FrameLayout的onInterceptTouchEvent,来处理什么时候需要拦截什么时候不需要拦截:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean onInterceptTouchEvent(MotionEvent event) {  
  2.         boolean result = false;   
  3.         switch (event.getAction()) {  
  4.         case MotionEvent.ACTION_DOWN:  
  5.                         //DOWN事件千万不能拦截  
  6.                         result = false;  
  7.             break;  
  8.         case MotionEvent.ACTION_MOVE:  
  9.             if (向左滑动手指||向右滑动手指) {  
  10.                 result = true;  
  11.             }else{  
  12.                 result = false;  
  13.             }  
  14.             break;  
  15.         case MotionEvent.ACTION_UP:  
  16.             result = false;  
  17.             break;  
  18.   
  19.         }  
  20.         return result;  
  21.   
  22.     }  
  23. }  

上面让parentView决定是否拦截事件的思路也很简单:当左右滑动手指的时候parentView对事件进行拦截和处理,否则就不拦截,将事件分发给childView,让childView决定是否处理该事件。就需要注意的是在处理ACTION_DOWN的时候,不能拦截,因为DOWN事件是一系列事件的开始事件,如果拦截的ParentView的down事件,那么后续move事件,up事件都会交给parentView处理。

上面的思路说白了就是:所有的事件都需要parentViewView进行拦截处理,由parentView决定是自己是否需要拦截该事件,不需要的话就分发给childrenView.这个思路是让父类优先选择对事件的拦截与否,其实通过《从源码角度分析android事件分发处理机制》对源码的分析发现有两处代码可以提供我们解决问题的另一种思路:

在ViewGroup进行事件分发的时候(处理ACTION_DOWN的时候)会进行如下判断:

if(disallowIntercept || !onInterceptTouchEvent(ev))这句很简单,但是代表的内容却很丰富,它说明了两种情况可以进入if条件的代码里面让ViewGroup的childrenView来分发处理事件:

1)当disallowIntercept==true的时候,即不允许当前的ViewGroup对该事件进行拦截

2)允许当前ViewGroup对事件进行拦截,也就是disallowIntercept==false,但是ViewGroup并未ACTION_DOWN事件拦截成功,也就是ViewGroup的onInterceptTouchEvent 返回了false。

至于disallowIntercept这个属性怎么设置呢,查阅ViewGroup的源码可以发下下面一个方法requestDisallowInterceptTouchEvent,该方法是public的也就是说childView也可以调用这个方法如:childView.getParent().requestDisallowInterceptTouchEvent(true or false)来干扰parentView对事件的分发。

前面说过当parentView寻找到到target的时候,后续事件一直都会让target来处理,但后续事件还会进入parentView的dispatchTouchEvent方法里面去执行,也就是说说通过requestDisallowInterceptTouchEvent可以让parentView继续对后续事件进行拦截和处理,从源代码上也可以说明:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean dispatchTouchEvent(MotionEvent ev) {     
  2.        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
  3. //处理down事件,主要目的是寻找target  
  4.        if (action == MotionEvent.ACTION_DOWN) {  
  5.            if (disallowIntercept || !onInterceptTouchEvent(ev)) {      
  6.                for (int i = count - 1; i >= 0; i--) {  
  7.                    final View child = children[i];  
  8.                           //找到了target  
  9.                            if (child.dispatchTouchEvent(ev))  {  
  10.                                mMotionTarget = child;  
  11.                                return true;  
  12.                            }  
  13.                          
  14.                        }  
  15.                    }  
  16.                }  
  17.            }  
  18.        }  
  19.        
  20.        //如果允许对后续事件进行拦截,并且拦截成功的话  
  21.        //通过这段代码可以知道,在childView中可以调用  
  22.      if (!disallowIntercept && onInterceptTouchEvent(ev)) {         
  23.            if (!target.dispatchTouchEvent(ev)) {  
  24.                // target didn't handle ACTION_CANCEL. not much we can do  
  25.                // but they should have.  
  26.            }  
  27.            mMotionTarget = null;  
  28.           
  29.            return true;  
  30.        }  
  31.   
  32.      
  33.        //后续事件:ACTION_MOVE系列事件和up事件都交给target处理  
  34.        return target.dispatchTouchEvent(ev);  
  35.    }  

根据if(!disallowIntercept&&onInterceptTouchEvent(ev))我们知道,可以再target这个childView中在合适的时机或者符合某个业务逻辑的情况下,执行如下调用:

childView.getParent().requestDisallowInterceptTouchEvent(false):允许父类对后续事件进行拦截,并且在某个合适的时机或者符合某个业务逻辑的时候

让parentView的onInterceptEvent方法返回true,这样后续的ACTION_MOVE事件就可以又交给parentView来进行处理了,通过这种childView干预parentView对时间进行拦截的方法也是解决滑动事件冲突的有一种思路,相比直接让第一种方式,这种方式较为麻烦点。


0
0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值