首页 > 其他 > 详细

控件手势冲突

时间:2014-02-20 22:59:32      阅读:533      评论:0      收藏:0      [点我收藏+]

在android界面开发中,经常可以遇到一些多层控件嵌套的情况,如果父子控件都有对应的手势操作(如scrollview中嵌套pageview),那么他们都手势操作就有可能相互干扰,影响界面的流畅性和体验。

  

首先,要谈一下android父子控件之间事件的分发,对于事件的分发有几个原则需要了解。

     (1) android事件分发是从父控件向子控件逐级分发传递的。

     (2) 每一层控件都可能消费这个事件,消费后不再向下传递(这也是父子控件对于手势操作冲突的主要原因)。          

     (3) android系统中的每个ViewGroup的子类都具有下面三个和事件分发处理密切相关的方法:

              1.public boolean dispatchTouchEvent(MotionEvent ev)            这个方法用来分发TouchEvent

              2.public boolean onInterceptTouchEvent(MotionEvent ev)         这个方法用来拦截TouchEvent

              3.public boolean onTouchEvent(MotionEvent ev)                  这个方法用来处理TouchEvent

      (4) android的触摸是由一个ACTION_DOWN(按下),多个ACTION_MOVE(移动),一个ACTION_UP(抬起)组成。

 

父子控件间的事件传递,用一张图可以更好的体现:

bubuko.com,布布扣1

(1)、distachTouchEvent用于分发事件,true直接消费事件并不在分发,false向intercertTouchEvent分发

(2)、internceptTouchEvent用于拦截事件,true直接分发事件至自己view的onTouchEvent方法经行处理,false继续分发

         到子view的dispathcTouchEvent。

(3)、onTouchEevent作为事件处理的部分,响应由多个touchevent所组成的手势。

 

     下面我们用这些内容来处理一个具体的问题,在竖直滑动的scrollview中嵌套水平滑动的viewpager的问题。viewpager中嵌套listview也可以用类似的方法解决。

     由于scrollview处于布局的外层,来自系统的触摸事件会先传递至外层的scrollview,scrolliew对于事件的消将直接影响事件继续分发至内层的viewpager。

先来看一下scrollview的distachTouchEvent源码:

[java] view plaincopy
 
  1.    @Override  
  2. 361     public boolean More ...onInterceptTouchEvent(MotionEvent ev) {  
  3. 362         /* 
  4. 363          * This method JUST determines whether we want to intercept the motion. 
  5. 364          * If we return true, onMotionEvent will be called and we do the actual 
  6. 365          * scrolling there. 
  7. 366          */  
  8. 367   
  9. 368         /* 
  10. 369         * Shortcut the most recurring case: the user is in the dragging 
  11. 370         * state and he is moving his finger.  We want to intercept this 
  12. 371         * motion. 
  13. 372         */  
  14. 373         final int action = ev.getAction();  
  15. 374         if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {  
  16. 375             return true;  
  17. 376         }  
  18. 377   
  19. 378         if (!canScroll()) {  
  20. 379             mIsBeingDragged = false;  
  21. 380             return false;  
  22. 381         }  
  23. 382   
  24. 383         final float y = ev.getY();  
  25. 384   
  26. 385         switch (action) {  
  27. 386             case MotionEvent.ACTION_MOVE:  
  28. 387                 /* 
  29. 388                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 
  30. 389                  * whether the user has moved far enough from his original down touch. 
  31. 390                  */  
  32. 391   
  33. 392                 /* 
  34. 393                 * Locally do absolute value. mLastMotionY is set to the y value 
  35. 394                 * of the down event. 
  36. 395                 */  
  37. 396                 final int yDiff = (int) Math.abs(y - mLastMotionY);  
  38. 397                 if (yDiff > mTouchSlop) {  
  39. 398                     mIsBeingDragged = true;  
  40. 399                 }  
  41. 400                 break;  
  42. 401   
  43. 402             case MotionEvent.ACTION_DOWN:  
  44. 403                 /* Remember location of down touch */  
  45. 404                 mLastMotionY = y;  
  46. 405   
  47. 406                 /* 
  48. 407                 * If being flinged and user touches the screen, initiate drag; 
  49. 408                 * otherwise don‘t.  mScroller.isFinished should be false when 
  50. 409                 * being flinged. 
  51. 410                 */  
  52. 411                 mIsBeingDragged = !mScroller.isFinished();  
  53. 412                 break;  
  54. 413   
  55. 414             case MotionEvent.ACTION_CANCEL:  
  56. 415             case MotionEvent.ACTION_UP:  
  57. 416                 /* Release the drag */  
  58. 417                 mIsBeingDragged = false;  
  59. 418                 break;  
  60. 419         }  
  61. 420   
  62. 421         /* 
  63. 422         * The only time we want to intercept motion events is if we are in the 
  64. 423         * drag mode. 
  65. 424         */  
  66. 425         return mIsBeingDragged;  
  67. 426     }  
  68. 427   

 

  397-399可以看到,在scrollview中,当其捕捉到手指上下滑动的距离大于mTouchSlop时,onInterceptTouchEvent方法返回true,拦截事件至ontouchEvent中并消费,内层的viewpager将得不到触摸事件的传递。

    虽然onscroll对事件是否拦截有一定的处理,但是有时仅仅这种效果不能满足需要。这种处理策略意味着,我们水平动viewpager时,必须保持上下滑动的距离小于mTouchSlop,否则,触摸事件会被上层的scrollview直接消费。为了解决这种冲突,个人想法是上层的scrollview不仅要限制竖直方向的TouchSlop,同时要限制水平方向的TouchSlop,避免viewpager的冲突。这样需要我们重写scrollview的onInterceptTouchEvent()方法。

具体的实现方法如下:

 

[java] view plaincopy
 
  1. <span style="font-size: 18px;">public class MScrollView extends ScrollView {  
  2.     private float FistXLocation;  
  3.     private float FistYlocation;  
  4.     private boolean Istrigger = false;  
  5.     public Animation animationUp;  
  6.     public Animation animationDown;  
  7. </span>    private final int TRIGER_LENTH = 50;  
  8.        private final int HORIZOTAL_LENTH = 20;<span style="font-size: 18px;">  
  9.     private TMHDItemsGridsAvtivity faAvtivity;  
  10.     public TMHDScrollView(Context context, AttributeSet attrs) {  
  11.         super(context, attrs);  
  12.         // TODO Auto-generated constructor stub  
  13.     }  
  14.   
  15.     @Override  
  16.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  17.         // TODO Auto-generated method stub  
  18.           
  19.           
  20.         int deltaX = 0;  
  21.         int deltaY = 0;  
  22.   
  23.         final float x = ev.getX();  
  24.         final float y = ev.getY();  
  25.   
  26.         switch (ev.getAction()) {  
  27.         case MotionEvent.ACTION_MOVE:  
  28.             deltaX = (int)(FistXLocation - x);  
  29.             deltaY = (int)(FistYlocation - y);  
  30.         if (Math.abs(deltaY) > TRIGER_LENTH  
  31.                     && Math.abs(deltaX) < HORIZOTAL_LENTH) {  
  32.               
  33.                 Istrigger = true;  
  34.                 return super.onInterceptTouchEvent(ev);  
  35.             //拦截这个手势剩下的部分  ,使他不会响应viewpager的相关手势  
  36.             }  
  37.   
  38.             return false;//没有触发拦截条件,不拦截事件,继续分发至viewpager  
  39.   
  40.         case MotionEvent.ACTION_DOWN:  
  41.             FistXLocation = x;  
  42.             FistYlocation = y;  
  43.             if(getScaleY()<-400){  
  44.                 System.out.println(getScaleY());  
  45.             }  
  46.             requestDisallowInterceptTouchEvent(false);   
  47.             return  super.onInterceptTouchEvent(ev);  
  48.   
  49.         case MotionEvent.ACTION_CANCEL:  
  50.         case MotionEvent.ACTION_UP:  
  51.             if (Istrigger) {  
  52.   
  53.                 Istrigger = false;  
  54.                 return  super.onInterceptTouchEvent(ev);  
  55.             }  
  56.   
  57.             break;  
  58.         }  
  59.         return super.onInterceptTouchEvent(ev);  
  60.           
  61.     }  
  62. }</span>  

 

下面这部分就是自己定义的水平和垂直的事件拦截触发条件。

 

[java] view plaincopy
 
  1. private final int TRIGER_LENTH = 50;  
  2. private final int HORIZOTAL_LENTH = 20;  

 

重写的MScrollView,自定义了onInterceptTouchEvent()方法,从而自定义了拦截的条件。解决了父子两个控件之间相互冲突的问题。

控件手势冲突

原文:http://www.cnblogs.com/starblogs/p/3557297.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!