Android面试——事件的传递机制

本文详细探讨了Android中事件传递的机制,包括dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个关键方法的作用。通过实例分析了事件从最外层到最内层的传递顺序,并解释了为何Activity和View没有onInterceptTouchEvent方法。最后总结了事件处理的流程和特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android事件的传递机制

前言

最近面试了很多公司,大多数公司都问到了这么一个问题,就是Android事件的传递机制,那Android事件的传递机制到底是怎么一回事?今天我们来探讨探讨!

正文

Android中三个方法是关于事件传递的,分别是dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,那这三个方法分别有什么用呢?其实我们可以从字面上的意思可以理解分别是分配触摸事件、截断触摸事件以及响应触摸事件,那这三个方法到底在事件的传递机制中扮演着什么角色?我们一起可以来看看源码!在看源码前,我们先明确事件的基本传递顺序和哪些类中有哪些触摸事件的方法!(后面会具体证明)传递顺序是从最外层传递到最里层,例如:Activity - - > LinearLayout - - > TextView,下面是哪些类中有哪些事件方法

相关子类方法
Activity类Activity……dispatchTouchEvent(); onTouchEvent();
View容器(ViewGroup的子类)FrameLayout、LinearLayout、ListView、ScrollVIew……dispatchTouchEvent(); onInterceptTouchEvent(); onTouchEvent();
View控件(非ViewGroup子类)Button、TextView、EditText……dispatchTouchEvent(); onTouchEvent();

其中onInterceptTouchEvent只有ViewGroup有,为什么ViewGroup才有onInterceptTouchEvent方法,因为他是截断事件,而截断事件不可能存在Activity中与View中,在Activity中,那你截取触摸事件干什么呢?在Activity中将事件截取了,那Activity中的布局控件就获取不到触摸事件,那相当于没有布局上控件的什么事!所以Activity中Google官方没有给Activity设置onInterceptTouchEvent方法,那View中为什么没有该方法呢?那是因为View本来就是最底层了,View没有下一层的子控件了,不需要向下传递事件,你截取事件与不截取事件都是一样的,所以也没有onInterceptTouchEvent的方法!

下面我们重写三个类,分别是FrameLayout、LinearLayout、TextView

FrameLayout

public class MyFrameLayout extends FrameLayout{

    public MyFrameLayout(@NonNull Context context) {
        super(context);
    }

    public MyFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Logger.e("Leezp", "MyFrameLayout调用dispatchTouchEvent分配任务");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Logger.e("Leezp", "MyFrameLayout调用onInterceptTouchEvent是否阻止任务?");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Logger.e("Leezp", "MyFrameLayout调用onTouchEvent是否处理了任务?");
        return super.onTouchEvent(event);
    }

}

LinearLayout

public class MyLinearLayout extends LinearLayout{

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

    public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Logger.e("Leezp", "MyLinearLayout调用dispatchTouchEvent分配任务");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Logger.e("Leezp", "MyLinearLayout调用onInterceptTouchEvent是否阻止任务?");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Logger.e("Leezp", "MyLinearLayout调用onTouchEvent是否处理了任务?");
        return super.onTouchEvent(event);
    }

}

TextView

public class MyTextView extends android.support.v7.widget.AppCompatTextView{

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Logger.e("Leezp", "MyTextView调用dispatchTouchEvent分配任务");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Logger.e("Leezp", "MyTextView调用onTouchEvent是否处理了任务?");
        return super.onTouchEvent(event);
    }

}

然后在MainActivity中将其所对用的dispatchTouchEvent、onTouchEvent两个方法重写一下

 @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Logger.e("Leezp", "MainActivity调用dispatchTouchEvent分配任务");
    return super.dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Logger.e("Leezp", "MainActivity调用onTouchEvent是否处理了处理任务?");
    return super.onTouchEvent(event);
}

这儿的Logger是我自己写的一个Log日志工具,你们可以直接用Log代替!

然后在activity_main布局中

<?xml version="1.0" encoding="utf-8"?>
<com.android.leezp.toucheventtransfer.utils.MyFrameLayout 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:background="@color/colorAccent"
    tools:context="com.android.leezp.toucheventtransfer.activities.MainActivity">


    <com.android.leezp.toucheventtransfer.utils.MyLinearLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:orientation="vertical">

        <com.android.leezp.toucheventtransfer.utils.MyTextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="@color/colorBlack"
            android:gravity="center"
            android:text="事件传递"
            android:textColor="#ffffff"
            android:textSize="15sp" />

    </com.android.leezp.toucheventtransfer.utils.MyLinearLayout>

</com.android.leezp.toucheventtransfer.utils.MyFrameLayout>

展示图:
这里写图片描述

最外层红色的是个FrameLayout,蓝色的是LinearLayout,黑色的是TextView

然后我们运行该程序,点击其中黑色区域

这里写图片描述

我们可以看见事件的经过,但是事件怎么调用了两次MainActivity的dispatchTouchEvent的方法呢?

其实我们的事件分三种,一种是down,手指接触屏幕,一种是move,手指在屏幕上移动,一种是up,手指离开屏幕,而我们点击事件只有down与up,而在down的过程中,事件会依次去按照传递过程中的步骤从最外层到最里层,然后获取到他这个事件在哪一层被处理了,然后下一次的up事件就只传到该层处理就行了,不需要继续传递,因为这次down事件所有的类都没处理,所以up事件就传到最外层的MainActivity中,然后进行判断是否处理该事件就结束了,这就是一次点击事件怎么调用了两次MainActivity的dispatchTouchEvent的方法

然后我们来看一下Activity中dispatchTouchEvent方法的源码

    /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

我们可以看到,他会先去判断事件的动作是否是down,如果是down的话,我们可以通过重写onUserInteraction中写入自己的一些界面交互的提示之类的信息,下面是Activity中自带的onUserInteraction方法

    /**
     * Called whenever a key, touch, or trackball event is dispatched to the
     * activity.  Implement this method if you wish to know that the user has
     * interacted with the device in some way while your activity is running.
     * This callback and {@link #onUserLeaveHint} are intended to help
     * activities manage status bar notifications intelligently; specifically,
     * for helping activities determine the proper time to cancel a notfication.
     *
     * <p>All calls to your activity's {@link #onUserLeaveHint} callback will
     * be accompanied by calls to {@link #onUserInteraction}.  This
     * ensures that your activity will be told of relevant user activity such
     * as pulling down the notification pane and touching an item there.
     *
     * <p>Note that this callback will be invoked for the touch down action
     * that begins a touch gesture, but may not be invoked for the touch-moved
     * and touch-up actions that follow.
     *
     * @see #onUserLeaveHint()
     */
    public void onUserInteraction() {
    }

然后就是他会通过superDispatchTouchEvent方法去Activity布局中分配事件,也就是调用子控件的dispatchTouchEvent方法,如果有控件处理了这个事件,他就会返回true,如果没有,它会调用自己的onTouchEvent方法,这就是Activity中的事件处理机制

ViewGroup中的事件处理机制大体差不多,不过代码有点多,如果想深入研究的,也可以看看,也就是父级控件先去调用ViewGroup的dispatchTouchEvent方法,然后判断onInterceptTouchEvent是否截断该事件,如果截断不往下传递,然后调用自己的onTouchEvent方法,如果没有截取该事件,那就继续往下传!

View就更不用说了,也是父级控件调用他的dispatchTouchEvent方法,在dispatchTouchEvent中调用自己的onTouchEvent方法!

总结

  1. dispatchTouchEvent方法就是分配任务的一个方法,如果是Activity,他会先去调用子级的dispatchTouchEvent,如果是ViewGroup,他会先调用自己的onInterceptTouchEvent方法,然后再调用子级的dispatchTouchEvent,不过Activity、ViewGroup、View在这个方法中都有个共同的特征,如果子级没处理,都会调用自己的onTouchEvent方法

  2. onInterceptTouchEvent方法就是ViewGroup进行截断事件向下传递的方法

  3. onTouchEvent方法就是类对事件处理的方法

  4. 事件传递机制是从父级到子级一层一层往下传,截断了该事件就不会往下传了,然后会一层一层的返回是否已经处理了该事件了,如果处理了,需返回true,没处理会返回false,所以面试官有时会问你如果截取了事件,是否往下传,这当然是不会往下传了,还可能问你那我截取了事件并处理了该事件,那还会返回父级吗?当然会返回,因为父级要确认是否子级已经处理该事件!

参考:

http://blog.youkuaiyun.com/morgan_xww/article/details/9372285/

http://www.cnblogs.com/linjzong/p/4191891.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值