• android事件分发机制


    首先我们要明确三个方法的作用

    public boolean dispatchTouchEvent(MotionEvent ev)这个方法的作用是对事件进行分发

    public boolean onInterceptTouchEvent(MotionEvent ev)对事件进行拦截

    public boolean onTouchEvent(MotionEvent event) 处理事件

    为什么要进行事件分发呢?

    分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

    onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

    这三个方法会同时出现在viewGroup里,但是在view里少了一个onInterceptTouchEvent方法,这个也不难理解,肯定是因为touch一个View才产生的事件,View是最底层

    所以没必要进行拦截了肯定要进行处理了。

    了解了这些,那么想事件究竟怎么分发呢?我觉得分发分为三个步骤,   开始---》寻找处理事件的view---》处理

    让我们来看下事件究竟从哪里开始?

    重写两个ViewGroup继承LinearLayout(这个随便继承),分别是GroupA,和GroupB,然后重写事件有关的三个方法,并进行log的打印。

    package com.example.view;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.LinearLayout;
    
    public class GroupA extends LinearLayout {
    
        public GroupA(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
            setWillNotDraw(false);
        }
        //事件分发
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            Log.v("xys","GroupA-----dispatchTouchEvent"+ev.getAction());
            return super.dispatchTouchEvent(ev);
        }
        //事件拦截
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            Log.v("xys","GroupA-----onInterceptTouchEvent"+ev.getAction());
            //如果返回true说明进行拦截,从而不会往下传递而是自己进行处理
            return super.onInterceptTouchEvent(ev);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            Log.v("xys","GroupA-----onTouchEvent"+event.getAction());
            return super.onTouchEvent(event);
        }
    }
    package com.example.view;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.LinearLayout;
    
    public class GroupB extends LinearLayout {
    
        public GroupB(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
            setWillNotDraw(false);
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            getParent().requestDisallowInterceptTouchEvent(true);
            Log.v("xys","GroupB-----dispatchTouchEvent"+ev.getAction());
            //请求父控件不拦截事件
            return super.dispatchTouchEvent(ev);
        }
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            Log.v("xys","GroupB-----onInterceptTouchEvent"+ev.getAction());
            return super.onInterceptTouchEvent(ev);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            Log.v("xys","GroupB-----onTouchEvent"+event.getAction());
            return super.onTouchEvent(event);
        }
    }

    最后一个是View,它没有拦截的方法

    package com.example.view;
    
    import com.example.study.R;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.TextView;
    
    public class MyView extends TextView {
    
        public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            // TODO Auto-generated constructor stub
        }
    
        public MyView(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
        }
    
        public MyView(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            Log.v("xys","MyView-----onTouchEvent"+event.getAction());
            return super.onTouchEvent(event);
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            Log.v("xys","MyView-----dispatchTouchEvent"+event.getAction());
            return super.dispatchTouchEvent(event);
        }
    }

    然后在布局文件里进行布局

    <com.example.view.GroupA
        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:orientation="vertical"
        android:background="#345"
        >
    <com.example.view.GroupB 
         android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="vertical"
        android:background="#f9f"
        >
        <com.example.view.MyView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="aaaaa"
            />
        
    </com.example.view.GroupB>
    </com.example.view.GroupA>

    在这里我们可以看见最外层的布局是A,其次是B,最后是我们的View

    运行打印的log是这样的。

    我点击了下View,可以明显看出布局是从A开始进行传递,

    开始先执行分发方法,然后进行拦截,

    然后传递给B布局的分发方法,B的拦截方法,

    最后传递给View,View没有拦截方法,

    只能进行处理。

    然后开始进行回传。

    这个是没有一个事件进行处理。只是让一个事件放肆的传递。

    但事实上我们并不需要这样,我们既然点击了View肯定是要帮我们做一些事情,我们让view对事件进行处理,看下log的打印情况

    @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            Log.v("xys","MyView-----onTouchEvent"+event.getAction());
            return true;
        }

    我点击了View,可以很明显看见我们的View处理了事件,并且没有回传,那为什么会打印了这么多的Log呢?

    因为我是点击了,点击由两个事件组成,down和up。所以会响应事件两次。

    如果我们事件不在View,进行处理,而是在B里进行处理会有什么样的效果呢?  可以大胆猜想下,既然处理了就会终止事件传递,

    那么肯定不会调用View的分发方法和处理方法了。我们打印log来验证。

    点击了B,结果和我们想的一样,没有往下传递。

    我们至此就明白了onTouchEvent()的返回值作用。

    那我们现在设想一个场景,ViewPager可以左右滑动,然后有个左侧有个slideMenu,这时候ViewPager右滑动,就会发生滑动冲突,

    怎么来解决呢?

    首先我们来分析,我本来想让这个布局响应事件,而现在并没有响应,而是父布局消费了事件。

    这时候我们要重写Viewpager的dispatchTouchEvent方法,并加上这样一句代码。

    @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            //请求父控件不拦截事件        
            getParent().requestDisallowInterceptTouchEvent(true);
            Log.v("xys","GroupB-----dispatchTouchEvent"+ev.getAction());
            
            return super.dispatchTouchEvent(ev);
        }

    这样就可以进行拦截了。

    为此我们做个验证,在A进行事件处理。然后滑动B,会不会调用B的onTouch方法?

    可以看出B并没有处理这个事件。这时候我们加上那句代码再试下。

    可以看出我们进行处理之后就会调用自己的ontouch方法了。

  • 相关阅读:
    位运算02 零基础入门学习C语言65(完)
    PE格式详细讲解4 系统篇04|解密系列
    PE格式详细讲解4 系统篇04|解密系列
    int指令02 零基础入门学习汇编语言65
    Tabhost漂亮的自定义实现(背景随着选项卡滑动改变)
    Android API Guide 之 User Interface笔记
    java程序员菜鸟进阶(九)windows下搭建SVN服务器及配置myeclipse SVN客户端
    android 左右页面滑动(滑屏)增加layout文件 而不是drawable(还有activity)
    android 最简单的九宫格实现
    ViewPager
  • 原文地址:https://www.cnblogs.com/84126858jmz/p/5443721.html
Copyright © 2020-2023  润新知