事件分发传递

View和ViewGroup
Android的UI界面都是由View和ViewGroup及其派生类组合而成的。其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的,也就是说ViewGroup的父类就是View。
我们常见的UI控件Button,TextView,ImageView等控件都是继承父类View来实现的。RelativeLayout、LinearLayout、FrameLayout等布局都是继承父类ViewGroup来实现的。

事件传递机制
并且View只有dispatchTouchEvent()和onTouchEvent();
ViewGroup有dispatchTouchEvent(),onInterceptTouchEvent()和onTouchEvent();
为了验证时间传递过程,我自定义了一个布局(ViewGroup),里面放一个自定义控件(View),直接上代码:
Activity类:

/**
  * Created by zjp on 2016/5/12 09:53.
  */
  public class Test2Activity extends AppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
               setContentView(R.layout.test2_layout);
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
               switch (ev.getAction()) {
                   case MotionEvent.ACTION_DOWN:
                         System.out.println("Activity---dispatchTouchEvent---DOWN");
                        break;
                  case MotionEvent.ACTION_MOVE:
                         System.out.println("Activity---dispatchTouchEvent---MOVE"); 
                        break; 
                  case MotionEvent.ACTION_UP:           
                         System.out.println("Activity---dispatchTouchEvent---UP");   
                       break;
               }
               return super.dispatchTouchEvent(ev);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                         System.out.println("Activity---onTouchEvent---DOWN");
                        break;
                  case MotionEvent.ACTION_MOVE:
                         System.out.println("Activity---onTouchEvent---MOVE"); 
                        break; 
                  case MotionEvent.ACTION_UP:           
                         System.out.println("Activity---onTouchEvent---UP");   
                       break;
               }
               return super.onTouchEvent(event);
        }
  }

自定义CustomLayout类:

/**
  * Created by zjp on 2016/5/12 09:48.
  */
  public class CustomLayout extends RelativeLayout {
        @Override
        public CustomLayout(Context context) {    
             this(context, null);
        }
         @Override
        public CustomLayout(Context context, AttributeSet attrs) {    
             this(context, attrs, 0);
        }
         @Override
        public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {    
             super(context, attrs, defStyleAttr);
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
               switch (ev.getAction()) {
                   case MotionEvent.ACTION_DOWN:
                         System.out.println("CustomLayout---dispatchTouchEvent---DOWN");
                        break;
                  case MotionEvent.ACTION_MOVE:
                         System.out.println("CustomLayout---dispatchTouchEvent---MOVE"); 
                        break; 
                  case MotionEvent.ACTION_UP:           
                         System.out.println("CustomLayout---dispatchTouchEvent---UP");   
                       break;
               }
               return super.dispatchTouchEvent(ev);
        }

  /**
    * 因为ViewGroup有onInterceptTouchEvent()方法,所以我对他进行重写
    */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {     
            System.out.println("CustomLayout--- onInterceptTouchEvent---");    
            return false;
      }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                         System.out.println("CustomLayout---onTouchEvent---DOWN");
                        break;
                  case MotionEvent.ACTION_MOVE:
                         System.out.println("CustomLayout---onTouchEvent---MOVE"); 
                        break; 
                  case MotionEvent.ACTION_UP:           
                         System.out.println("CustomLayout---onTouchEvent---UP");   
                       break;
               }
               return super.onTouchEvent(event);
        }
  }

自定义CustomView类:

/**
  * Created by zjp on 2016/5/12 09:57.
  */
  public class CustomView extends Button {
        @Override
        public CustomView (Context context) {    
             this(context, null);
        }
         @Override
        public CustomView (Context context, AttributeSet attrs) {    
             this(context, attrs, 0);
        }
         @Override
        public CustomView (Context context, AttributeSet attrs, int defStyleAttr) {    
             super(context, attrs, defStyleAttr);
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
               switch (ev.getAction()) {
                   case MotionEvent.ACTION_DOWN:
                         System.out.println("CustomView---dispatchTouchEvent---DOWN");
                        break;
                  case MotionEvent.ACTION_MOVE:
                         System.out.println("CustomView---dispatchTouchEvent---MOVE"); 
                        break; 
                  case MotionEvent.ACTION_UP:           
                         System.out.println("CustomView---dispatchTouchEvent---UP");   
                       break;
               }
               return super.dispatchTouchEvent(ev);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                         System.out.println("CustomView---onTouchEvent---DOWN");
                        break;
                  case MotionEvent.ACTION_MOVE:
                         System.out.println("CustomView---onTouchEvent---MOVE"); 
                        break; 
                  case MotionEvent.ACTION_UP:           
                         System.out.println("CustomView---onTouchEvent---UP");   
                       break;
               }
               return super.onTouchEvent(event);
        }
  }

接下来是布局了
Activity的布局:test2_layout.xml如下:

   <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
                android:layout_width="match_parent"    
                android:layout_height="match_parent"    
                android:background="@android:color/holo_purple"    
                android:orientation="vertical">    
      <app.application.view.test2.CustomLayout        
                    android:layout_width="300dp"        
                    android:layout_height="400dp"        
                    android:background="@android:color/holo_blue_bright">        
             <app.application.view.test2.CustomView            
                        android:layout_width="200dp"            
                        android:layout_height="200dp"            
                        android:text="我是Button"
                        android:gravity="center"
                        android:textColor="@color/bg_white"
                        android:background="@android:color/holo_red_dark" />    
      </app.application.view.test2.CustomLayout>
   </LinearLayout>

效果图:


效果图.png

当我们点击Button的时候,log日志如下:

log日志.png

你会发现,事件从Activity传递到子View,再从子View返回到Activity:
①从Activity传递到子View:

Activity:dispatchTouchEvent --> CustomLayout:dispatchTouchEvent -->  CustomLayout:onInterceptTouchEvent --> CustomView:dispatchTouchEvent

②从子View传递到Activity:

CustomView:onTouchEvent --> CustomLayout :onTouchEvent--> Activity: onTouchEvent

细心的朋友可能会发现,自定义布局中onInterceptTouchEvent()返回值是false。
如果我把值改为true。又是什么情况呢?


效果图.png

①从Activity传递到子View:

Activity:dispatchTouchEvent --> CustomLayout:dispatchTouchEvent -->  CustomLayout:onInterceptTouchEvent

②从子View传递到Activity:

CustomLayout :onTouchEvent--> Activity: onTouchEvent

你会发现,子View的事件没走,这是因为,事件从Activity传递到 CustomLayout 的onInterceptTouchEvent事件中,由于onInterceptTouchEvent返回true,被自身消费了,就不再往下传递。

先来看整体的事件传递过程:

L:LinearLayout R:RealativeLayout B:Button

效果图.png

当手指点击按钮B时,事件传递的顺序是从底向上传递的,也就是按照L->R->B的顺序由下往上逐层传递,响应正好相反,是自上而下。
L首先接收到点击事件,L的父类是ViewGroup类,并将事件传递给dispatchTouchEvent方法,dispatchTouchEvent函数中判断该控件L是否重载了onInterceptTouchEvent方法进行事件拦截,onInterceptTouchEvent默认返回false不拦截,那么dispatchTouchEvent方法将事件传递给R去处理(进入第2流程处理),如果返回true表示当前L控件拦截了事件向其它控件的传递,交给它自己父类View的dispatchTouchEvent去处理,在父方法的dispatchTouchEvent中,将会按照前面讲的View的事件处理机制去判断,比如判断L是否重载了onTouch方法,是否可点击,是否做了监听等事件。
R也是ViewGroup的子类,因此与第1流程基本相似,如果onInterceptTouchEvent返回了false,表示事件将不拦截继续传递给B。
B是View的子类,它没有onInterceptTouchEvent方法,直接交给自己父类View的dispatchTouchEvent去处理,流程同不再敷述。

今天手写了一个图,更容易 理解

流程图.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容