AndroidView的事件分发机制

Android的事件分发机制
      主要方法:dispathTouchEvent(事件分发) onInterceptTouchEvent(事件拦截) onTouchEvent(事件处理)
      PS:Activity和View只有分发和处理两个方法,只有ViewGroup有三个方法,多一个拦截方法。
   (1)onTouch是优先于onClick执行,事件传递的顺序是先经过onTouch,再传递到onClick。
   (2)Android的事件响应机制是“由外到内”分发,“由内到外”处理的形式实现的。
   (3)点击事件产生后的传递顺序:activity-window-viewGroup-view,若所有元素都未处理则顺序相反。(即:回传机制)
       1.事件传递的方向:父控件→子控件
      2.事件响应的方向:子控件→父控件
   (4)事件的处理:冒泡式消费
只执行onTouchEvent方法(view没有拦截方法,事件传递过来时一定会执行这个方法,当clickable和longclickable都为true时如button默认消费事件) 
true,处理了,不用处理了 
false,没处理,给上级处理

流程图:

实例代码+解析:

1.ViewGroupA

package com.yhy.touchevent;

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;

/**
 * Created by ${yinhaiyang} on 2018/8/1.
 */

public class ViewGroupA extends LinearLayout {
    public ViewGroupA(Context context) {
        super(context);
        Log.i("OnTouchEvent", "ViewGroupA==+1参构造");
    }

    public ViewGroupA(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i("OnTouchEvent", "ViewGroupA==+2参构造");
    }

    public ViewGroupA(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.i("OnTouchEvent", "ViewGroupA==+3参构造");
    }

    /**
     * 分发
     *
     * @param ev
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ActionUtiles.ProcessEvent(ev,"ViewGroupA+分发+dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
//        return true;
    }

    /**
     * 拦截
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        ActionUtiles.ProcessEvent(ev,"ViewGroupA+拦截+onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 处理
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        ActionUtiles.ProcessEvent(event,"ViewGroupA+处理+onTouchEvent");
        return super.onTouchEvent(event);
//        return true;
    }
}

2.ViewGroupB

package com.yhy.touchevent;

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;

/**
 * Created by ${yinhaiyang} on 2018/8/1.
 * PS:事件不往下分发了,意味着后面的方法不会被执行了。
 * 默认值情况下,“被点击的控件”无论是view还是ViewGroup,它的onTouchEvent方法都会被执行。
 * 总结:
 * (前体条件,未经特殊设置过的方法全部返回的是默认值情况。)
 * ViewGroup的dispathceptTouchEvent()方法,return true,事件分发拦截,事件回传拦截,不往下(子控件)(不分发)、不往上传递(不回传)。
 * return false,事件拦截,事件回传,自己不处理,回传给父控件的onTouchEevent()来处理。
 * return super.dispathceptTouchEvent(ev);默认值,事件出给自己的onTouchEvent(),看其是否处理,若返回true(处理),则回自己的onTouchEvent()消费掉事件(不回传),
 * 否则返回false或者默认值(不处理),继续回传,回传给父控件onTouchEvent()来处理,若都不处理,直至传到最外层消失。
 * onInterceptTouchEvent返回true时:问自己的onInterceptTouchEvent(),是否拦截。
 * return true(拦截),由自己的onTouchEvent消费(不回传)
 * return false或者是默认值(不拦截),事件继续传递,且回传,回传给父控件onTouchEvent()来处理,若都不处理,直至传到最外层消失。
 */

public class ViewGroupB extends LinearLayout {
    public ViewGroupB(Context context) {
        super(context);
        Log.i("OnTouchEvent", "ViewGroupB==+1参构造");
    }

    public ViewGroupB(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i("OnTouchEvent", "ViewGroupB==+2参构造");
    }

    public ViewGroupB(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.i("OnTouchEvent", "ViewGroupB==+3参构造");
    }

    /**
     * 分发
     *
     * @param ev
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ActionUtiles.ProcessEvent(ev,"ViewGroupB+分发+dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);//返回的是默认值情况,事件继续分发,自己的onInterceptOntouchEvent()会被执行。
//        return true;//dispatchTouchEvent()返回true,事件被拦截,不会往下传递也不会回传,事件在此处消失。
//        return false;//dispatchTouchEvent()返回false,表示事件不做分发,自己oninterceptTouchEvent()方法不会执行,所以它的onTouchEvent方法也不会被执行,事件会回传给父控件的OnTouchEvent方法处理,如果父控件返回的是默认值则一直往上传递,最终事件消失。PS:回传的过程中如果有OntouchEvent方法返回true的,则事件被处理,事件就也此终止。
    }

    /**
     * 拦截
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        ActionUtiles.ProcessEvent(ev,"ViewGroupB+拦截+onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);//返回的默认值情况,事件继续分发,如果点击的是自己,那么自己的onTouchEvent()方法会被执行,如果点击的是子View,事件继续传递,回传机制。
//        return true;//onInterceptTouchEvent()返回true,表示拦截事件,则事件交给自己的onTouchEvent方法处理,看onTouchEvent是否处理了,如果不处理继续执行回传机制。
//        return false;//onInterceptTouchEvent()返回false,效果和默认值情况相同。
    }

    /**
     * 处理
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        ActionUtiles.ProcessEvent(event,"ViewGroupB+处理+onTouchEvent");
        return super.onTouchEvent(event);//返回的是默认值。
         //return false;//ViewGroup的onTouchEvent()方法返回false,和默认情况一样。
//        return true;// ViewGroup的onTouchEvent()方法返回true,表示调用了我之后,我就做事件处理了。由于ViewGroup的OnInterceptTouchEvent返回值为默认的false,不会立即调用自己的OnTouchEvent处理事件。当我们按下的时候,事件还是继续分发给孩子组件,孩子组件没有处理事件,就回传给自己的onTouchEvent,看看是否处理事件,如果返回的是true,则处理事件,事件在此消失。否则的话继续向上级回传。UP事件则是由自己的onTouchEvent处理了。
    }
}

3.MyView

package com.yhy.touchevent;

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

/**
 * Created by ${yinhaiyang} on 2018/8/1.
 * 总结:
 * View没有子控件
 * dispathTouchEvent(MotionEvent event)
 * return true,不再分发事件,表示拦截事件拦截掉事件不会回传,事件在此处消失。
 * return false,不做分发事件,事件被拦截,回传给父控件的处理方法。
 * return super.dispathTouchEvent();默认返回值,根据事件传递的机制,此时会传给自己的OnTouchEvent方法。
 */

public class MyView extends View {
    public MyView(Context context) {
        super(context);
        Log.i("OnTouchEvent", "MyView==+1参构造");
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i("OnTouchEvent", "MyView==+2参构造");
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.i("OnTouchEvent", "MyView==+3参构造");
    }

    /**
     * 分发
     * @param event
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        ActionUtiles.ProcessEvent(event,"MyView+分发+dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
//        return false;//返回值为false,表示该View本身不做事件的分发了,所以自己后续的OnTouchEvent方法不会被执行了,直接回传给父控件的OnTouchEvent方法去处理。如果父控件的方法都是默认的情况,则一直传到Activity,事件消失。
//        return true;//返回值为true,表示不再分发事件也可以说是拦截事件,此时该View的OnTouchEvent方法不会被调用,事件也不会回传。
    }

    /**
     * 处理
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        ActionUtiles.ProcessEvent(event,"MyView+处理+onTouchEvent");
        return super.onTouchEvent(event);
//        return false;//返回false,说明事件并没有被消费掉,效果与默认值相同,事件会回传。
//        return true;//返回true,说明事件被该View消费了,事件到此结束不再回传了。
    }
}

4.布局main_activity.xml

<?xml version="1.0" encoding="utf-8"?>

<com.yhy.touchevent.ViewGroupA xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_red_dark">


    <com.yhy.touchevent.ViewGroupB
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@android:color/holo_green_dark">

        <com.yhy.touchevent.MyView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="@android:color/holo_blue_bright" />
    </com.yhy.touchevent.ViewGroupB>
</com.yhy.touchevent.ViewGroupA>

5.效果图

扫描二维码关注公众号,回复: 2533075 查看本文章

简单总结:

dispathTouchEvent -->true:拦截,事件到此为止,不回传

                               -->false,不分发(即后面的控件则接不到任何事件了),回传机制

                               -->默认值,分发且回传

onInterceptTouchEvent-->true,拦截,不回传,调用自己onTouchEvvent方法处理,回传机制

                                    -->false,-->默认值,两者中情况相同

(一旦onInterceptTouchEvent拦截了Down事件,那么后续的Move、Up事件则都由当前的ViewGroup来处理,不再向下传递。 )

onTouchEvent  -->true,回传机制,若子控件都不处理,则传给自己的onTouchEvent方法处理,事件就此消失

                        -->false, -->默认值,两者情况相同

ps:该总结中没有涉及到onClick方法的情况。前提条件都是在没有特殊设置返回值的方法都返回默认值。

本文是自己学习后的总结笔记,详细讲解看:博客:https://blog.csdn.net/qq_32059827/article/details/52577017#commentBox

https://blog.csdn.net/m0_37700275/article/details/77947974                          

有onClick的情况,给三个控件都添加onclick监听事件。

(1)所有方法都设置默认值的情况下,点击那个控件默认后会由哪个控件处理该事件,无回传机制。(PS:这与前面总结的不同)。

(2)在这里要强调View的OnTouchListener。如果View设置了该监听,那么OnTouch()将会回调。 * 如果返回为true那么该View的OnTouchEvent将不会在执行 这是因为设置的OnTouchListener执行时的优先级要比onTouchEvent高。 * 优先级:OnTouchListener > onTouchEvent > onClickListener。

猜你喜欢

转载自blog.csdn.net/yhy123456q/article/details/81357529
今日推荐