android开发艺术探索读书笔记之-------view的事件分发机制

View的点击事件的分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生后,系统需要把这个事件传递给一个具体的View,而这个过程就是分发过程。

分发过程主要由以下3个方法共同完成:

  public boolean dispatchTouchEvent(MotionEvent event)

  用来进行事件的分发。如果事件能够传递给当前的View,那么此方法一定会被调用,返回结果受当前的View的onTouchEvent和下级View的dispatchTouchEvent方法的

影响,表示是否消耗当前事件。

  public boolean onInterceptTouchEvent(MotionEvent event)

  在上述方法方法的内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列中,此方法不会再次被调用,返回结果表示是否拦截当前事件。

  public boolean onTouchEvent(MotionEvent event)

  在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。

  三者关系用伪代码表示:

   public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (onIterceptTouchEvent(ev)) {
             consume = onTouchEvent(ev);   
        } else {
             consume = child.dispatchTouchEvent(ev);
        }
        return comsume;
    }         

  通过上面的伪代码我们可以大致了解到点击事件的传递规则:

    对于一个ViewGroup来说,点击事件产生后,首先传递给它,这是它会调用dispatchTouchEvent,如果这个ViewGroup的onIterceptTouchEvent方法返回true就表示它要拦截当前事件,

  接着事件就由这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果这个ViewGroup的onIterceptTouchEvent方法返回false就表示它不拦截当前事件,这时当前时间就会传递

  给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件最终被处理。

    当一个view需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被调用。当onTouch返回false时,当前View的onTouch方法会被调用;如果返回

  true,那么onTouchEvent方法将不会被调用。在onTouchEvent方法中,如果当前设置的有OnClickListener,那么它的onClick方法会被调用。

   优先级: OnTouchListener > onTouchEvent > OnClickListener

  下面用一个实例证明验证上面的结论:

  本例整体布局为:

      

  对于ViewGroup重写了如下三个方法:

  

   @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupA dispatchTouchEvent:" + ev.getAction());
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyViewGroupA onTouchEvent:" + event.getAction());
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupA onInterceptTouchEvent:" + ev.getAction());
        return super.onInterceptTouchEvent(ev);
    }

View的写法

  public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyView_OnTouchEvent" + event.getAction());
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyView_dispatchTouchEvent" + event.getAction());
        return super.dispatchTouchEvent(event);
    }

源码在后面会有提供!

点击View后的Log如下所示:

09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA dispatchTouchEvent:0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA onInterceptTouchEvent:0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB dispatchTouchEvent:0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onInterceptTouchEvent:0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyView_dispatchTouchEvent0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyView_OnTouchEvent0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onTouchEvent:0
09-20 23:40:10.246 1522-1522/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA onTouchEvent:0

在没有处理的情况下,点击事件的流程与伪代码中的过程一致。

我们可以把整个过程看作:ViewGroupA(总经理) ---> ViewGroupB(部长)--->  View(你自己)

事件传递是从总经理到部长在到你,首先总经理先执行dispatchTouchEvent,在执行onIterceptTouchEvent。如果不拦截然后到部长执行,同样是这两个方法,

部长也不拦截,然后事情交给你,由于你是最后一个所以无需拦截。调用dispatchTouchEvent,在调用OnTouchEvent,你发现你解决不了,返回了一个false

值(默认是false)。然后交给部长,部长调用OnTouchEvent方法,发现也处理不了,然后交给总经理处理,总经理调用onTouchEvent方法。

修改ViewGroupB中的代码如下:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupB onInterceptTouchEvent:" + ev.getAction());
        return true;
    }

打印的Log为:

09-21 07:39:23.222 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA dispatchTouchEvent:0
09-21 07:39:23.222 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA onInterceptTouchEvent:0
09-21 07:39:23.222 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB dispatchTouchEvent:0
09-21 07:39:23.222 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onInterceptTouchEvent:0
09-21 07:39:23.222 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onTouchEvent:0
09-21 07:39:23.222 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA onTouchEvent:0

这个过程可以描述为:部长拦截了该事件,进行了处理,所以不需要你了,处理完后发现无法解决,然后提交给总经理。

再次修改ViewGroupB代码:

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupB onInterceptTouchEvent:" + ev.getAction());
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyViewGroupB onTouchEvent:" + event.getAction());
        return true;
    }

打印Log为:

09-21 07:45:35.362 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA dispatchTouchEvent:0
09-21 07:45:35.362 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA onInterceptTouchEvent:0
09-21 07:45:35.362 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB dispatchTouchEvent:0
09-21 07:45:35.362 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onInterceptTouchEvent:0
09-21 07:45:35.362 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onTouchEvent:0
09-21 07:45:35.462 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA dispatchTouchEvent:1
09-21 07:45:35.462 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupA onInterceptTouchEvent:1
09-21 07:45:35.462 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB dispatchTouchEvent:1
09-21 07:45:35.462 31696-31696/com.example.huangyichun.myviewtest D/hyc: MyViewGroupB onTouchEvent:1

这个过程可以描述为:部长拦截事件,且完美处理了,该事件就此结束。点击事件是一个事件序列包括:

MotionEvent.ACTION_DOWN 和 MotionEvent.ACTION_UP两个事件。由于ViewGroupB已经拦截过了,之后的时间只要经理没有拦截,都交给部长处理。

 

源码:

package com.example.huangyichun.myviewtest;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class MyViewGroupA extends LinearLayout {
    private static final String TAG = "hyc";
    public MyViewGroupA(Context context) {
        super(context);
    }

    public MyViewGroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupA dispatchTouchEvent:" + ev.getAction());
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyViewGroupA onTouchEvent:" + event.getAction());
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupA onInterceptTouchEvent:" + ev.getAction());
        return super.onInterceptTouchEvent(ev);
    }
}
package com.example.huangyichun.myviewtest;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class MyViewGroupB extends LinearLayout {
    private static final String TAG = "hyc";
    public MyViewGroupB(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyViewGroupB(Context context) {
        super(context);
    }

    public MyViewGroupB(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupB onInterceptTouchEvent:" + ev.getAction());
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyViewGroupB onTouchEvent:" + event.getAction());
        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "MyViewGroupB dispatchTouchEvent:" + ev.getAction());
        return super.dispatchTouchEvent(ev);
    }
}
package com.example.huangyichun.myviewtest;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View {

    private static final String TAG = "hyc";

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyView_OnTouchEvent" + event.getAction());
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "MyView_dispatchTouchEvent" + event.getAction());
        return super.dispatchTouchEvent(event);
    }

}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.huangyichun.myviewtest.MainActivity">

    <com.example.huangyichun.myviewtest.MyViewGroupA
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@color/colorPrimaryDark">
        <com.example.huangyichun.myviewtest.MyViewGroupB
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="@android:color/holo_green_dark">
            <com.example.huangyichun.myviewtest.MyView
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:background="@android:color/holo_red_dark"/>
        </com.example.huangyichun.myviewtest.MyViewGroupB>
    </com.example.huangyichun.myviewtest.MyViewGroupA>

</RelativeLayout>

  

时间: 2024-10-23 23:19:48

android开发艺术探索读书笔记之-------view的事件分发机制的相关文章

Android开发艺术探索读书笔记——进程间通信

1. 多进程使用场景 1) 应用某些模块因为特殊需求需要运行在单独进程中.如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会因为应用程序进程crash而受影响. 2) 为加大一个应用可使用的内存,需要多进程来获取多份内存空间. 2. 如何开启多进程 给四大组件(Activity.Service.Receiver.ContentProvider)在AndroidMainfest中指定android:process属性指定. 如果进程以":"开头的进程,代表应用的私有进程,其

Android开发艺术探索(研读笔记)——03-Android中的IPC机制(一)

Android开发艺术探索(研读笔记) 作者:Dimon 微博:@Dimon-喰 GitHub:@Dimon94 LOFTER:@Dimon. 03-Android中的IPC机制(一) 1.Android IPC 简介 IPC(Inter-Process-Communication):含义为进程间通信,指两个进程之间进行数据交换的过程. 什么是进程:一般指一个执行单元,在PC和移动设备上的一个程序或者一个应用. 什么是线程:线程是CPU调度的最小单元,是一种有限的系统资源. 而一个进程可以包含多

Android开发艺术探索——第四章View的工作原理

Android开发艺术探索--第四章View的工作原理 4.1 (一)初识ViewToot和DecorView 基本概念 ViewRoot对应于ViewRootImpl类,是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的.在ActivityThread中,当Activity对象被创建完成后,会将DecorView添加到View中.同时,会创建ViewRootImpl对象,并将ViewTootImpl对象和DecorView建立关联.

Android开发艺术探索读书(三)-View的事件体系

移动手持客户端作为目前最受欢迎的智能设备,拥有着最为广大的体验用户群体.因此,作为软件开发商,要紧紧抓住用户的胃口,不仅要向用户提供合适的服务项目,也应该更为注重与用户的交互体验.而作为感觉型的用户,应用操作是否流畅,界面内容是不是足够精致,是判断该应用是不是一个好应用的硬性标准.那么,要如何去强化与用户的交互体验呢?这就涉及了本章所讲的内容:View的事件体系 提纲: 一.什么是View 二.View的位置参数 三.几个相关的View知识点 四.View的滑动深入 五.view的事件分发 六.

《Android 开发艺术探索》笔记——(3)View 的事件体系

View 基础知识 View 是 Android 中所有控件的基类,ViewGroup 也继承了 View. Android 中,x 轴和 y 轴的正方向分别为右和下. 位置参数: (left , top ): View 左上角原始坐标 (right, bottom): View 右下角原始坐标 (x , y ): View 左上角最终坐标 translationX: View 左上角横向偏移量 translationY: View 左上角纵向偏移量 x = left + translation

Android动画(开发艺术探索读书笔记)

Android动画可以分为3类:View动画,帧动画,属性动画 1.View动画 可以通过XML来创建,也可以通过代码来创建 1.1通过XML创建 在res/anim下创建filename.xml文件,语法如下所示 <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" andro

Android开发艺术探索学习笔记(十一)

第十一章  Android的线程和线程池 从用途上来说,线程分为子线程和主线程,主线程主要处理和界面相关的事情,而子线程往往用于执行耗时的操作.AsyncTask,IntentService,HandlerThread都可以扮演线程的角色. AsyncTask封装了线程池和Handler,主要是为了方便开发者在主线程中更新UI. HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler. IntentService是一个服务,系统对其进行了封装,使其可以更方便的执行后

【读书笔记】【Android 开发艺术探索】第4章 View 的工作原理

一.基础知识 1.ViewRoot 和 DecorView ViewRoot 对应 ViewRootImpl 类,它是连接 WindowManager 和 DecorView 的纽带,View 的三大流程都是通过 ViewRoot 来完成的.在ActivityThread 中,当 Activity 对象被创建完毕后,会将 DecorView 添加到 Window 中,同时会创建 ViewRoot 对象. DecorView 添加到窗口 Window 的过程. 图片来自https://yq.ali

《android开发艺术探索》读书笔记(五)--动画

接上篇<android开发艺术探索>读书笔记(五)--Drawable No1: 自定义动画:派生一种新动画只需要继承Animation这个抽象类,然后重写它的initialize和applyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程. No2: 属性动画PropertyAnimation 补间动画TweenAnimation 帧动画Frame