[android 自定义控件](3)事件传递的一些案例思考

版权声明:本文为博主原创文章,未经博主允许下请随便转载。 https://blog.csdn.net/god_wen/article/details/88791751


上一篇: [android 自定义控件](2)事件传递

案例

环境:

View类

package com.example.dispatcheventapp;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;

@SuppressLint("AppCompatCustomView")
public class MyButton extends Button {
    public  final  static  String MyButton_TAG="View";

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

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

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(MyButton_TAG,"ENTER dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(MyButton_TAG,"ENTER onTouchEvent");
        return super.onTouchEvent(event);
    }
}


ViewGoup类

package com.example.dispatcheventapp;

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

public class MyLayout extends LinearLayout {
    public  final  static  String Mylayout_TAG="ViewGroup";
    public MyLayout(Context context) {
        super(context);
    }

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

    public MyLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MyLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(Mylayout_TAG,"ENTER dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(Mylayout_TAG,"ENTER onTouchEvent");

        return super.onTouchEvent(event);
    }
}

Activity类

package com.example.dispatcheventapp;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;

public class MainActivity extends Activity {
    public  final  static  String Activity_TAG="Activity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(Activity_TAG,"ENTER dispatchTouchEvent");

        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(Activity_TAG,"ENTER onTouchEvent");

        return super.onTouchEvent(event);
    }
}

layout文件

<?xml version="1.0" encoding="utf-8"?>
<com.example.dispatcheventapp.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:id="@+id/my_layout">
    <com.example.dispatcheventapp.MyButton
        android:id="@+id/my_button"
        android:text="点我"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</com.example.dispatcheventapp.MyLayout>

案例1:无onTouch监听的默认情况

点一下按钮的结果:

03-25 10:53:33.629 7153-7153/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 10:53:33.629 7153-7153/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 10:53:33.629 7153-7153/com.example.dispatcheventapp E/View: ENTER dispatchTouchEvent
03-25 10:53:33.629 7153-7153/com.example.dispatcheventapp E/View: ENTER onTouchEvent
03-25 10:53:33.639 7153-7153/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 10:53:33.639 7153-7153/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 10:53:33.649 7153-7153/com.example.dispatcheventapp E/View: ENTER dispatchTouchEvent
03-25 10:53:33.649 7153-7153/com.example.dispatcheventapp E/View: ENTER onTouchEvent
03-25 10:53:33.679 7153-7153/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 10:53:33.679 7153-7153/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 10:53:33.679 7153-7153/com.example.dispatcheventapp E/View: ENTER dispatchTouchEvent
03-25 10:53:33.679 7153-7153/com.example.dispatcheventapp E/View: ENTER onTouchEvent

分析:
点按钮,down事件被View接受,然后因为没有onTouch监听,所以调用onTouchEvent的函数,被onTouchEvent函数处理之后,onTouchEvent返回true,View的dispatchTouchEvent返回true,ViewGroup的dispatchTouchEvent返回true,Activity的dispatchTouchEvent返回true,之后的move和up事件同理。

点一下非按钮的结果:

03-25 11:00:37.029 7153-7153/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 11:00:37.029 7153-7153/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 11:00:37.029 7153-7153/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 11:00:37.029 7153-7153/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 11:00:37.049 7153-7153/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 11:00:37.049 7153-7153/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 11:00:37.059 7153-7153/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 11:00:37.059 7153-7153/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent

分析:
因为没有View能处理down事件,所以down事件时,ViewGoup的dispatchTouchEvent函数中,mFirstTouchTarget会被初始化为null,所以事件会被传递到ViewGroup的onTouchEvent(和View的onTouchEvent一样)中,又因为viewGroup都是disclickable的所以不能处理事件返回false,ViewGoup的dispatchTouchEven 返回false,,viewGroup的上级ViewGroup也返回false…最后Activity的dispatchTouchEvent中调用onTouchEvent来处理事件,最后也返回false。这样整个ViewGroup链条中每一个ViewGroup的mFirstTouchTarget都是null。之后的move和up往下传时,第一个会被传入根View(DecorView)中,因为mFirstTouchTarget被初始化为null,所以直接调用根View的onTouchEvent函数来处理,返回false。这样这些事件会之前返回给Activity的onTouchEvent来处理。

思考1: 我只想让我自定义的ViewGroup来处理事件,怎么弄?

首先我们要弄清楚为什么会发生这样的问题。通过上面的分析我们知道

  1. 没有View能处理down事件(这个没办法解决)
  2. ViewGroup时disclickable的所以onTouchEvent只能返回false
  3. 我们的布局和系统生成布局组成了一条ViewGroup链条(最少三层),因为上层的ViewGroup的dispatchEvent返回fasle,导致底层的ViewGroup的mFirstTouchTarget被初始化为null,这样整个链条上的ViewGroup就都是mFirstTouchTarget=null,所以下一次的move和up事件就指挥传递到根View的onTouchEvent中,上面的ViewGroup就没机会接收到这些事件了。(机制问题,无法解决)

上面1和3时无法解决的,但是通过2我们就可以实现这样的目标了

//修改mylayout的代码,让他返回true,这样整个VewiGroup链条中前面的ViewGroup中mFirstTouchTarget!=null,事件也就会被传递下来
@Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(Mylayout_TAG,"ENTER onTouchEvent");

         super.onTouchEvent(event);
        return true;
    }

结果:

03-25 11:56:19.059 16517-16517/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 11:56:19.069 16517-16517/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 11:56:19.069 16517-16517/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 11:56:19.079 16517-16517/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 11:56:19.079 16517-16517/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 11:56:19.079 16517-16517/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 11:56:19.089 16517-16517/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 11:56:19.089 16517-16517/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 11:56:19.099 16517-16517/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent

案例2: onInterceptTouchEvent调用时机问题

通过之前的代码分析我们知道,onInterceptTouchEvent只有 ViewGroup中才有的方法,可以控制事件的拦截。默认是返回false,运行结果就和上面的代码一样。现在我们来看看返回true的结果

//在mylayout类中加入下面的代码
@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

点击按钮:

03-25 12:26:39.639 21782-21782/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:26:39.639 21782-21782/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 12:26:39.639 21782-21782/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 12:26:39.639 21782-21782/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 12:26:39.649 21782-21782/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:26:39.649 21782-21782/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 12:26:39.679 21782-21782/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:26:39.679 21782-21782/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent

分析:
因为onInterceptTouchEvent返回true,所以当dow事件传递到ViewGroup的dispatchTouchEvent中时,intercepted为true,所以之后就跳过寻找View的过程,mFirstTouchTarget也就时null,这样就会调用ViewGroup的onTouchEvent来处理事件。因为ViewGroup是disclickable的所以,返回False。这样整个ViewGroup链条中mFirstTouchTarget就都是null。所以剩下的事件就只能是Activity来处理。你会发现这和案例一中的点击非按钮区域是一样的。这也说明了android 提供的拦截机制还是很好的

点击非按钮:

03-25 12:30:13.059 21782-21782/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:30:13.069 21782-21782/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 12:30:13.069 21782-21782/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 12:30:13.069 21782-21782/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 12:30:13.079 21782-21782/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:30:13.079 21782-21782/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 12:30:13.099 21782-21782/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:30:13.099 21782-21782/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent

和上面一样,不分析了。

思考1: 如果我move和up拦截,down不拦截会怎么样?

//修改mylayout中的diamagnetic
@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.e(Mylayout_TAG,"ENTER onInterceptTouchEvent");

        if(ev.getAction()==MotionEvent.ACTION_DOWN)
            return false;
        return true;
    }

点击按钮结果:

03-25 12:47:17.739 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:47:17.739 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 12:47:17.739 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER onInterceptTouchEvent
03-25 12:47:17.739 24598-24598/com.example.dispatcheventapp E/View: ENTER dispatchTouchEvent
03-25 12:47:17.739 24598-24598/com.example.dispatcheventapp E/View: ENTER onTouchEvent
03-25 12:47:17.759 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:47:17.759 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 12:47:17.759 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER onInterceptTouchEvent
03-25 12:47:17.759 24598-24598/com.example.dispatcheventapp E/View: ENTER dispatchTouchEvent
03-25 12:47:17.759 24598-24598/com.example.dispatcheventapp E/View: ENTER onTouchEvent
03-25 12:47:17.769 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:47:17.779 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 12:47:17.779 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 12:47:17.779 24598-24598/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 12:47:17.789 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 12:47:17.789 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 12:47:17.789 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 12:47:17.789 24598-24598/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent

分析:
down时调用onInterceptTouchEvent,不拦截。所以mFirstTouchTarget被初始化,View的onTouchEvent处理事件,返回true。所以ViewGroup的dispatchTouchEvent返回true。Activity的dispatchTouchEvent不调用onTouchEvent且返回true。通过down事件的初始化,viewGroup链条中的mFirstTouchTarget都被初始化为!=null。

接着move 传递到ViewGroup的dispatchTouchEvent中,因为mFirstTouchTarget!=null所以调用onInterceptTouchEvent函数,这是返回false,拦截。所以传递消息之前cancelChild 会被设置成True,这样所有的View都会收到cancel消息。因为View是Clickable的所以就算是cancel,onTouchEvent也是True,但是ViewGroup会清空mFirstTouchTarget,让其=null。此时我们自定义的ViewGroup的mFirstTouchTarget被清空了,但是整个链条中的其他ViewGroup的mFirstTouchTarget还是存在的。

当后面的move或up事件再一次传递到我们自定义的ViewGroup的dispatchTouchEvent中时。mFirstTouchTarget==null,所以不会调用onInterceptTouchEvent 方法,直接使intercepted=true。之后因为mFirstTouchTarget==null,所以使用ViewGroup的onTouchEvent处理处理事件返回false.这样整个链条中的ViewGroup的dispatchTouchEvent都返回false。就会进入Activity的onTouchEvent中。

点击非按钮:

03-25 13:08:07.489 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 13:08:07.489 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER dispatchTouchEvent
03-25 13:08:07.489 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER onInterceptTouchEvent
03-25 13:08:07.489 24598-24598/com.example.dispatcheventapp E/ViewGroup: ENTER onTouchEvent
03-25 13:08:07.489 24598-24598/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 13:08:07.509 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 13:08:07.509 24598-24598/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent
03-25 13:08:07.509 24598-24598/com.example.dispatcheventapp E/Activity: ENTER dispatchTouchEvent
03-25 13:08:07.509 24598-24598/com.example.dispatcheventapp E/Activity: ENTER onTouchEvent

没有View能处理事件,就会和案例1一样结果。

思考2: 如果我down拦截,move和up不拦截又会怎么样?

能看到这说明你对事件传递已经有一个很深刻的认识了。上面的思考通过大脑也应该能想出来。我们来试试

down我们一旦拦截了,ViewGroup的mFirstTouchTarget就会等于null。这样的华以后压根就不会再调用onInterceptTouchEvent方法了。就算你设置了不拦截,结果还会是拦截。所以down事件会被ViewGroup的onTouchEvent处理,且返回false。这样整个VIewGroup的mFirstTouchTarget都会等于null。最后消息也都会由Activity来消化。

猜你喜欢

转载自blog.csdn.net/god_wen/article/details/88791751
今日推荐