自定义View基础(二)事件分发机制

来源:互联网 发布:喀秋莎录屏软件破解版 编辑:程序博客网 时间:2024/06/06 15:42

 消息事件传递流程

         对于一个根ViewGroup来说,当事件发生时,会调用它的dispatchTouchEvent(MotionEvent ev)方法进行事件的分发,判断根 ViewGroup的onInterceptTouchEvent(MotionEvent ev)是否为true,return true则会把事件拦截交给这个ViewGroup处理,调用该ViewGrouponTouchEvent方法,onTouchEvent()如果return false则会把事件传递给上一层的ViewGroup的onTouchEvent方法执行;onInterceptTouchEvent方法return false则会把事件传递给下一层View(ViewGroup)的dispatchTouchEvent(MotionEvent ev)直到消息最终被处理

 

     //伪代码:     @Override     public boolean dispatchTouchEvent(MotionEvent ev) {         if(onInterceptTouchEvent(ev)){            return onTouchEvent(ev);         }else{            //最底层ViewGroup时,遍历所有的子view,判断事件发生的点位于哪个子View,调用该view的onTouEvent方法,            //(如果是ViewGroup则调用子viewGroup的dispatchTouchEvent方法),如果事件发生的点没有位于任何子view,            //那么由该ViewGroup来处理该事件,调用ViewGroup的onTouchEvent()方法              return child.dispatchTouchEvent(MotionEvent ev);         }     } 

 主意:View是没有dispatchTouchEvent(MotionEvent ev)和onInterceptTouchEvent(MotionEvent ev)方法


消息传递顺序

    事件总是先传给Activity的dispatchTouchEvent(MotionEvent ev)进行事件分发,再传给Window最后Window再传给顶级View,

然后顶级View会根据事件分发机制去分发事件,如果所有元素都不去处理这个事件,即onTouchEvent方法return false,则Activity的onTouchEvent方法会被调用


OnTouchListener,onTouchEvent与OnClickListener,OnLongClickListener的关系

        当一个View需要处理事件时,会先判断是否设置onTouchListener,如果设置了就调用onTouch方法,onTouch方法

如果return true,本次事件被消耗掉;如果onTouch返回false,则回调用onTouchEvent,返回onTouchEvent的返回值

在onTouchEvent内部,如果设置了onClickListener就会去调用onClick方法
      总的来说,就是给View设置onTouchListener它的优先级比onTouchEvent要高一些,onClickListener的优先级最低;      
     

     OnLongClickListener的onLongClick方法返回值:
       如果返回值为true的话这个点击事件会被长点击独占,如果返回值为false会同时触发OnClickListener点击事件
              
    OnCLick/onLongClick要在down和up事件响应后(手指按下和抬起两个操作,我们可以理解为一次Click)
        ACTION_DOWN->ACTION_UP->onCLick/onLongClick
       -响应ACTION_DOWN事件如果返回false--那么同一序列中的其他事件 将会由它的父元素的onTouchEvent来处理
                                   true--代表View接受此按压动作,然后就是执行ACTION_UP
       -ACTION_UP:onLongClick是在ACTION_UP之前,而onClick的发生是在ACTION_UP后,因此同一次用户touch操作
       就有可能既发生onLongClick又发生onClick。我们如果在onLongClick()方法的最后return true,那么onClick事件就没有机会被触发了。



滑动冲突的解决方式

       a.外部解决法:父容器对事件判断是否拦截

           

      public boolean onInterceptTouchEvent(MotionEvent ev) {        // TODO Auto-generated method stub          boolean result=false;          switch(ev.getAction()){            case MotionEvent.ACTION_DOWN:            firstX=(int) ev.getX();            firstY=(int) ev.getY();            break;            case MotionEvent.ACTION_MOVE:            //手指在屏幕上水平移动的绝对值            int disX=(int) Math.abs(ev.getX()-firstX);            //手指在屏幕上竖直移动的绝对值            int disY=(int) Math.abs(ev.getY()-firstY);            if(disX>disY&&disX>10){            result=true;             }            break;              case MotionEvent.ACTION_UP:            break;           }         return result;        }  


       b.内部解决法:父容器不拦截任何事件,任何的事件都传递给子元素,如果子元素需要此事件就消耗掉,否则就由父容器进行处理。
       对于底层的View来说,有一种方法可以阻止父层的View截获touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true);方法。一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action。

    

       public boolean dispatchTouchEvent(MotionEvent event){         switch(event.getAction()){          case MotionEvent.ACTION_DOWN:              //父控件就不会拦截ListView的滑动事件              parent.requestDisallowInterceptTouchEvent(true);              break;          case MOtionEvent.ACTION_MOVE:             if(父容器需要此类点击事件){               parent.requestDisallowInterceptTouchEvent(false);             }             break;          }           return super.dispatchTouchEvent(event);       }   




0 0
原创粉丝点击