android 事件传递和处理机制

引用博客:https://www.cnblogs.com/fuly550871915/p/4983682.html

其实android中的事件传递与处理机制跟我们生活中的事件处理是一样的。这里有一个生活中的例子,很能说明这个问题。阐述如下:

你是一个公司的员工,你的上头有一个主管,主管上头呢还有一个经理。为了简单,你们这个团队就有这三个人。那么如果上头安排一件事下来要处理,流程是怎样的呢?
显然应该是由你的经理将这件事安排给你的主管来处理,你的主管再将这件事安排给你来处理。等你把这件事办好了,你就应该给你的主管报告,再由你的主管来向你的经理
报告。显然,你的主管和经理也有处理这件事的权限,如果他们觉得事情很复杂,你办不了,或者他们比较照顾下级,可能就自己把这件事给办了,这个时候这件事就不会再
传递给下一级来处理了。这个事件处理的过程,是不是太容易理解了!

比如你在ViewGroupA中嵌套了一个VewiGroupB,然后又在ViewGroupB中嵌套了一个MyView。那么一个触摸事件传递过来,会发生什么情况呢?类比上面的公司员工的处理事件,显然会发生下面的过程:

触摸事件传递过来后,ViewGroupA一看自己里面还有一个员工可以利用,就是ViewGroupB,那不用白不用,就会把这个事件传递给ViewGroupB,告诉他,你给我把这个事件处理了!
ViewGroupB呢一看,我不怕,我里面也有一个员工就是MyView,它得给我干活,于是又会把这个事件传递给MyView,让它来处理。MyView一看,没办法啊,我手底下没有员工了,那
怎么办,我只能自己处理了(前提是它有处理这个事件的能力),所以就把这个触摸事件给处理了。处理完成后呢?MyView就是给ViewGroupB报告,我已经把事情办好了,你来审核一下
,看看办理的咋样。ViewGroupB一审核,觉得不错,就再将结果呈现给ViewGroupA。ViewGroupA再审核,通过了才算通过。在这个过程中,也可能出现几种情况:
(1)MyView说,完蛋了,这事我的能力办不好啊,于是就向VeiwGroupB报告,我没有处理,请你来处理,你是我上司,能力比我强。于是ViewGroupB就会来帮忙处理。当然了,
如果ViewGroupB也没能力处理,那就只能反馈给VeiwGroupA,让它来消化这个事件。
(2)也可能MyView处理非常完美,向ViewGroupB一报告,ViewGroupB一开心就说不用再交给ViewGroupA审核了,我担保通过,于是事件到此直接终止。

实际过程中,android已经把这几个方法完美封装了。
ViewGroup

1)dispatchTouchEvent     该方法用来分发事件,一般不会重写这个方法
(2)onInterceptTouchEvent  拦截事件,返回true表示拦截将触发onTouchEvent,返回false表示继续向下传递
(3)onTouchEvent           处理事件,返回true表示处理完成,不需要继续上层传递
  

View

由于View没有子控件,所以不需要拦截事件,直接触发onTouchEvent           
(1)dispatchTouchEvent     该方法用来分发事件,一般不会重写这个方法
(2)onTouchEvent           用来处理事件,这个方法应该大家很常见了吧

下面我们将模拟上述VeiwGroupA->VeiwGroupB->MyView的过程
VeiwGroupA.java

package com.example.testmotionevent;

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

public class ViewGroupA extends LinearLayout{

    public ViewGroupA(Context context) {
        super(context);
    }
    public ViewGroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public ViewGroupA(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    
    
    

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        
        Log.d("付勇焜----->","ViewGroupA dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        Log.d("付勇焜----->","ViewGroupA onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("付勇焜----->","ViewGroupA onTouchEvent");
        return super.onTouchEvent(event);
    }
}

VeiwGroupB.java

package com.example.testmotionevent;

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

public class ViewGroupB extends LinearLayout{

    public ViewGroupB(Context context) {
        super(context);
    }
    public ViewGroupB(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public ViewGroupB(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    
    
    

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        
        Log.d("付勇焜----->","ViewGroupB dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        Log.d("付勇焜----->","ViewGroupB onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("付勇焜----->","ViewGroupB onTouchEvent");
        return super.onTouchEvent(event);
    }
}

MyView.java

package com.example.testmotionevent;

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{

    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 dispatchTouchEvent(MotionEvent event) {
        Log.d("付勇焜---->","MyView dispatchTouchEvent ");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        
        Log.d("付勇焜---->","MyView onTouchEvent ");
        return super.onTouchEvent(event);
    }
}

activity_main.xml

<LinearLayout 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"
   >

    <com.example.testmotionevent.ViewGroupA
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:background="#ff0033">
        <com.example.testmotionevent.ViewGroupB
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:gravity="center"
        android:background="#336699">
         <com.example.testmotionevent.MyView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:clickable="true"
        android:background="#ffff00"/>
        
            
        </com.example.testmotionevent.ViewGroupB>
        
        
    </com.example.testmotionevent.ViewGroupA>
       

</LinearLayout>

效果如下图:
在这里插入图片描述

红色的就是ViewGroupA,蓝色的就是ViewGroupB,黄色就是MyView。现在来点击中间黄色的MyView,观察下打印结果,如下:
在这里插入图片描述
从打印的结果,我们可以很清楚到看到事件的流程:
首先ViewGroupA得到了事件,由它的dispatchTouchEvent方法来分发事件,由于它的onInterceptTouchEvent方法没有做出拦截,因此事件传递给了ViewGroupB,而同样由于ViewGroupB的onInterceptTouchEvent方法在它的dispatchTouchEvent方法分发事件时没有做出拦截,故而事件最终被传递给MyView,MyView就来处理这个事件了。

下面我们来做实验吧,我们让MyView没有处理这个事件,会是上面我们所说的由ViewGrouPB来处理吗?修改MyView的onTouchEvent事件,如下:

public boolean onTouchEvent(MotionEvent event) {
        
        Log.d("付勇焜---->","MyView onTouchEvent ");
        return false;
//        return super.onTouchEvent(event);
    }

我们return了false,表示MyView没有成功处理这个事件。现在来再运行下程序,打印结果如下:
在这里插入图片描述
由于MyView的onTouchEvent返回false,因此事件就交给了它的上级ViewGroupB来处理,于是ViewGroupB的onTouchEvent就来消化这个事件了。所以从打印的结果来看正是这个情况(即红色线条标注的部分)。但是由于ViewGroupB的onTouchEvent也没有成功处理这个事件所以又传递ViewGroupA的onToucEvent来处理这个事件(即红色线最下面还有ViewGroupA的标志)。不管ViewGroupA能不能成功处理,我们的程序中它是终极boss,不会再由其他对象来处理该事件了。

再来在上一步的基础上继续做实验。当MyView没有成功处理事件,传递给ViewGroupB来处理时,当ViewGroupB处理完,我们强制告知程序,ViewGroupB已经成功处理该事件了,看看会出现什么情况!修改ViewGroupB的onTouchEvent代码,如下:

public boolean onTouchEvent(MotionEvent event) {
        Log.d("付勇焜----->","ViewGroupB onTouchEvent");
        
        return true;  //表示事件已经成功处理
//        return super.onTouchEvent(event);
    }

再来运行程序,观察打印结果如下:在这里插入图片描述
观察红线部分,此时只剩下ViewGroupB的onTouchEvent了。因为ViewGroupB已经成功处理这事件了,那就不必再劳烦ViewGroupA来吃处理了。

现在我们在上一个实验的基础上再来实验。比如,当事件传递到ViewGroupB的时候,ViewGroupB比较好心,心想干脆我来处理这个事情吧,就不必再让MyView加班了,于是他对这个事件进行了拦截!修改ViewGroupB的onInterceptTouchEvent代码,如下:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        Log.d("付勇焜----->","ViewGroupB onInterceptTouchEvent");
        return true;
//        return super.onInterceptTouchEvent(ev);
    }

再次运行程序,观察打印结果,如下:
在这里插入图片描述

我们发现事件传递到ViewGroupB的地方直接终止了,然后就是ViewGroupB的onTouchEvent事件来处理了,当然了因为之前我们强制修改ViewGroupB的onTouchEvent为处理成功,因此也不会再返回给ViewGroupA的onTouchEvent来处理了。此时MyView压根就不知道有个触摸事件在它的上层传递呢!所以在打印结果中,我们连MyView的影子都见不到!

总结:

事件先后顺序:dispatchTouchEvent->onInterceptTouchEvent如果返回true则调用当前onTouchEvent,否则调用子View的 dispatchTouchEvent->onInterceptTouchEvent。依次类推。

  1. onInterceptTouchEvent 返回为true,表示拦截,不再往下传递,为false,不拦截,继续往下传递。
  2. onTouchEvent 返回结果为true,表示拦截,不再往上传递,返回结果为false(没有成功处理事件),继续向上传递。

扩展:ACTION_MOVE事件传递机制
onInterceptTouchEvent的ACTION_MOVE事件不执行

ACTION_CANCEL,ACTION_MOVE,ACTION_UP事件机制

Android事件传递之onInterceptTouchEvent()和requestDisallowInterceptTouchEvent()方法的使用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值