View的滑动冲突和解决方案

  1.滑动冲突原因:

  当有内外两层View同时可以滑动的时候,这个时候就会产生滑动冲突。

   2.常见的冲突场景:

  场景1:

   

  场景2:

     

   场景3:

      

 4.解决方法种类:

   (1)外部拦截法:

    

    针对场景1,我们可以发现外部和内部的滑动方向不一样也就是说只要判断当前dy和dx的大小,如果dy>dx,那么当前就是竖直滑动,否则就是水平滑动。明确了这个我就就可以根据当前的手势开始拦截了。从view的事件分发中我们了解点击事件的分发顺序是 通过父布局分发,如果父布局没有拦截,即onInterceptTouchEvent返回false,才会传递给子View。所以我们就可以利用onInterceptTouchEvent()这个方法来进行事件的拦截。

    

 1  public boolean onInterceptTouchEvent(MotionEvent event) {
 2         boolean intercepted = false;
 3         int x = (int) event.getX();
 4         int y = (int) event.getY();
 5 
 6         switch (event.getAction()) {
 7         case MotionEvent.ACTION_DOWN: {
 8             intercepted = false;
 9             break;
10         }
11         case MotionEvent.ACTION_MOVE: {
12             if(父容器拦截的规则){
13                 intercepted=true;
14             }else{
15                 intercepted=false;
16             }
17             break;
18         }
19         case MotionEvent.ACTION_UP: {
20             intercepted = false;
21             break;
22         }
23         default:
24             break;
25         }
26         mLastXIntercept=x;
27         mLastYIntercept=y;
28         return intercepted;
29     }

上面的代码差多就是外部拦截的通用模板了,在onInterceptTouchEvent方法中,

首先是ACTION_DOWN这个事件,父容器必须返回false,即不拦截事件,因为一旦父容器拦截了ACTION_DOWN这个事件,那么后续的ACTION_MOVE和ACTION_UP事件将直接交给父容器处理,这个时候事件没法继续传递给子元素了;

然后是ACTION_MOVE这个事件,这个事件可以根据需要决定是否拦截,如果父容器需要拦截就返回true,否则返回false;

最后是ACTION_UP这个事件,这里必须返回false,因为这个事件本身也没有太多意义。

 so场景一的外部拦截解决方法:

 

 1 public boolean onInterceptTouchEvent(MotionEvent event) {
 2         boolean intercepted = false;
 3         int x = (int) event.getX();
 4         int y = (int) event.getY();
 5 
 6         switch (event.getAction()) {
 7         case MotionEvent.ACTION_DOWN: {
 8             intercepted = false;
 9             break;
10         }
11         case MotionEvent.ACTION_MOVE: {
12             int deltaX=x-mLastXIntercept;
13             int deltaY=y=mLastYIntercept;
14             if(Math.abs(deltaX)>Math.abs(deltaY)){
15                 intercepted=true;
16             }else{
17                 intercepted=false;
18             }
19             break;
20         }
21         case MotionEvent.ACTION_UP: {
22             intercepted = false;
23             break;
24         }
25         default:
26             break;
27         }
28         mLastXIntercept=x;
29         mLastYIntercept=y;
30         return intercepted;
31     }

  场景二的外部拦截方法:

  

 1 public boolean onInterceptTouchEvent(MotionEvent event) {
 2         boolean intercepted = false;
 3         int x = (int) event.getX();
 4         int y = (int) event.getY();
 5 
 6         switch (event.getAction()) {
 7         case MotionEvent.ACTION_DOWN: {
 8             mLastYIntercept=y;
 9             intercepted = super.onInterceptTouchEvent(event);
10             break;
11         }
12         case MotionEvent.ACTION_MOVE: {
13             if(listView.getFirstVisiblePosition()==0 &&y>mLastYIntercept){
14                 intercepted=true;
15             }else if(listView.getLastVisiblePosition()==listView.getCount-1&&y                <mLastYIntercept){
16                 intercepted=false;
17              break;
18             }
19             intercepted = false;
20             break;
21         }
22         case MotionEvent.ACTION_UP: {
23             intercepted = false;
24             break;
25         }
26         default:
27             break;
28         }
29         mLastXIntercept=x;
30         mLastYIntercept=y;
31         return intercepted;
32     }

 

  

(2)内部拦截法

   内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器去处理

 (android系统中,一次点击事件是从父view传递到子view中,每一层的view可以决定是否拦截并处理点击事件或者传递到下一层,如果子view不处理点击事件,则该事件会传递会父view,由父view去决定是否处理该点击事件。在子view可以通过设置此方法去告诉父view不要拦截并处理点击事件,父view应该接受这个请求直到此次点击事件结束)需要用到方法requestDisallowInterceptTouchEvent。

 内部拦截通用模板:

  

 1 public boolean onInterceptTouchEvent(MotionEvent event) {
 2         int x = (int) event.getX();
 3         int y = (int) event.getY();
 4 
 5         switch (event.getAction()) {
 6         case MotionEvent.ACTION_DOWN: {
 7             parent.requestDisallowInterceptTouchEvent(true); //父布局不要拦截此事件
 8             break;
 9         }
10         case MotionEvent.ACTION_MOVE: {
11             int deltaX=x-mLastXIntercept;
12             int deltaY=y=mLastYIntercept;
13             if(父容器需要拦截的事件){
14                 parent.requestDisallowInterceptTouchEvent(false); //父布局需要要拦截此事件
15             }
16             break;
17         }
18         case MotionEvent.ACTION_UP: {
19             intercepted = false;
20             break;
21         }
22         default:
23             break;
24         }
25         mLastXIntercept=x;
26         mLastYIntercept=y;
27         return super.dispathTouchEvent(event);
28     }

 

  so 场景二的内部拦截解决方法:

 

 1 //拦截除了Down事件以外的其他方法:
 2 public boolean onInterceptTouchEvent(MotionEvent event) {
 3         int x = (int) event.getX();
 4         int y = (int) event.getY();
 5         int action = event.getAction();
 6          if(action == MotionEvent.ACTION_DOWN){
 7            mLastXIntercept=x;
 8            mLastYIntercept=y;
 9            return super.dispathTouchEvent(event);
10         }else{
11            return true;
12         }
13    }   
14 
15          

场景三建议使用内部拦截方法比较方便

 

 

   

    

posted @ 2017-05-24 17:12  QinXiao.Shou  阅读(629)  评论(0编辑  收藏  举报