自定义View之事件传递机制

86 阅读3分钟
  • 预备知识: Android应用中Activity的视图层级结构 每个Activity都是通过PhoneWindow来呈现View的,PhoneWindow中最顶层View是mDecor(DecorView的对象),当我们在Activity中调用setContentView()设置布局时会调用PhoneWindow的setContentView()方法生成DecorView对象mDecor。mDecor只有一个子元素为LinearLayout,而LinearLayout下包含两个FrameLayout,上面那个FrameLayout为标题栏显示界面,包含一个id为android.R.id.title的TextView,而下面那个FrameLayout的id为android.R.id.content,我们通过setContentView()设置进来的布局界面就会放在这个FrameLayout中。

  • 1> 同一个事件序列以down事件开始,中间含有数量不定的move事件,最终以up事件结束。

  • 2> 事件传递是从Activity的dispatchTouchEvent开始,到PhoneWindow分发成功结束,  否则交给Activity.onTouchEvent处理

  •    Activity.java
      public boolean dispatchTouchEvent(MotionEvent ev) {
              if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                  onUserInteraction();
              }
              if (getWindow().superDispatchTouchEvent(ev)) {
                  return true;
              }
              return onTouchEvent(ev);
          }
    
  • 3>同一个事件序列中, 没有消耗掉的事件都会交给Activity的onTouchEvent处理, 如果没有View消耗ACTION_DOWN, 那么后续事件将不再向下分发, 并且都会交给Activity的onTouchEvent处理

  • 4> 同一个事件序列中, 某个ViewGroup一旦拦截了某个事件,那么后续事件不会再调用onInterceptTouchEvent()去询问它是否要拦截了。直接交给它的onTouchEvent()

  •  5> 同一个事件序列中,某个ViewGroupA一旦拦截了某个事件(如MOVE),后续的事件将不再向下分发,而是直接交给该View或者他的父布局处理, 如果他的子View已经消耗(onTouchEvent() return true)了该事件之前的事件(如DOWN),那么ViewGroupA将向下分发一个CANCEL事件,告诉子View事件被拦截

  • 6> 同一个事件序列中, 当ACTION_DOWN事件传到它的onTouchEvent中时,如果它不消耗掉ACTION_DOWN事件,那么该事件将重新交由它的父元素的onTouchEvent(),并且后续事件将不会再分发给它,如果没有view处理ACTION_DOWN, 那么后续事件将不再向下分发, 并且都会交给Activity的onTouchEvent处理

  • 7> 如果View一旦消耗了ACTION_DOWN事件,表示它将处理后续事件(被拦截的情况除外), 父元素不拦截的话将没有机会处理后续事件, 即使它没有消耗后续事件,后续事件也不会交给父元素的onTouchEvent处理

  • 8> View的onTouchEvent的返回值取决于它是否可点击的(clickable和longClickable属性),如果这两个属性都为false的话,onTouchEvent就会返回false,其余情况都返回true。View的longClickable属性默认为flase,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认为false。注意,setOnClickListener()会自动将View的clickable属性设为true

  • 9> 通过requestDisallowInterceptTouchEvent方法可以在子View中干预父View的事件传递过程,但是ACTION_DOWN事件除外。

  • 10> ViewGroup将优先将事件分发给两个重叠的子View中上层的子View,如果上层的子View不消耗该事件的话,将继续传递给下层的子View