• Touch事件分发机制


    Android的Touch事件分发机制简单探析

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

    Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。

    View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

    ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

    先分析ViewGroup的处理流程:首先得有个结构模型概念:ViewGroup和View组成了一棵树形结构,最顶层为Activity的ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。如图:

    当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。

    多点触控

     

    前言

      Android中关于触摸事件的分发传递是一个很值得研究的东西。曾不见你引入了一个ListView的滑动功能,ListView就不听你手指的指唤来滚动了;也不知道为啥Button设置了onClick和onTouch,其中谁会先响应;或许你会问onTouch和onTouchEvent有什么区别,又该如何使用?这里一切的一切,只要你了解了事件分发机制,你会发现,解释这都不是事儿!

    onTouch和onTouchEvent区别

    参考:http://blog.csdn.net/huiguixian/article/details/22193977

    1. onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。
    2. 如果onTouch返回false时,会接着触发onTouchEvent
    3. 如果onTouch返回true时,onTouchEvent方法不会被调用,并且OnclickListener.onClick()也不会被调用
    4. 内置诸如click事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发。
    5. onTouchEvent()无论返回true或者false都不会影响onClick()

    onClick和onTouch区别

    参考:http://blog.csdn.net/mydreamongo/article/details/30465613

    先长按后点击

     

    相关Touch事件的方法

    1、public boolean dispatchTouchEvent(MotionEvent ev)                ————事件分发方法,分发Event所调用

    2、public boolean onInterceptTouchEvent(MotionEvent ev)    ————事件拦截方法,拦截Event所调用

    3、public boolean onTouchEvent(MotionEvent event)       ————事件响应方法,处理Event所调用

    拥有上述事件的类

    1、Activity类(Activity及其各种继承子类)

        dispatchTouchEvent()、onTouchEvent()

    2、ViewGroup类(LinearLayout、FrameLayout、ListView等.....)

        dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()

    3、View类(Button、TextView等.....)

        dispatchTouchEvent()、onTouchEvent()

    PS:需要特别注意一点就是ViewGroup中额外拥有onInterceptTouchEvent()方法,其他两个方法为这三种类所共同拥有。

    方法的简单用途解析

    我们可以发现这三个方法的返回值都为boolean类型,其实它们就是通过返回值来决定下一步的传递处理方向。

    1、dispatchTouchEvent()  ——用来分发事件所用

      该方法会将根元素的事件自上而下依次分发到内层子元素中,直到被终止或者到达最里层元素,该方法也是采用一种隧道方式来分发。在其中会调用onInterceptTouchEvent()和onTouchEvent(),一般不会去重写。

      返回false则不拦截继续往下分发,如果返回true则拦截住该事件不在向下层元素分发,在dispatchTouchEvent()方法中默认返回false。

    2、onInterceptTouchEvent()  ——用来拦截事件所用

      该方法在ViewGroup源代码中实现就是返回false不拦截事件,Touch事件就会往下传递给其子View。

      如果我们重写该方法并且将其返回true,该事件将会被拦截,并且被当前ViewGroup处理,调用该类的onTouchEvent()方法。

    3、onTouchEvent()  ——用来处理事件

      返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View)

      返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理

    实战演练

      好了,基础知识讲完了,上面的东西看不懂不要紧,现在要睁大眼睛好好看了,因为下面开始用例子讲解Touch的事件分发机制,相信能让大家更好的理解这个分发机制。

    在这个例子中,我们需要重写四个类:

    1、老板  ——>  MyActivity

    2、经理  ——>  FrameLayout

    3、组长  ——>  LineaLayout

    4、员工  ——>  TextView

    【2】、我们假设员工能处理该事件,也就是将TextView的onTouchEvent()方法设置返回true,表示其能处理该事件。

    事件传递的流程:

    【3】、我们假设员工和组长能力不足,即TextView和LinearLayout的onTouch()返回false,但是经理解决了该问题,即FrameLayout的onTouch()返回true

    事件传递的流程:

    【4】假设我们的组长在事件分发到他那里的时候,决定拦截下来不交给下面的员工,也就是onInterceptTouchEvent()返回为true,并且他也成功完成了任务,即onTouchEvent()返回值为true。

    事件传递的流程:

    做个小结

    1、很明显,这些流程就是dispatchTouchEvent()的处理结果,但是前提是我们不去完全的重新实现这个方法,也就是保证需要return super.dispatchTouchEvent(ev);来确定父类的方法有被调用。而这些事件将会由上而下的逐层传递,直到传递到最底层的View元素,此时将会调用该View的onTouchEvent()方法来处理该事件;返回true来表示对该事件已经成功处理,如果返回false则并没有成功处理事件,将会把事件逐层向上传递,交给上层View的onTouchEvent()方法处理,以此类推,直至某一View成功处理该事件,或者到顶层View处理仍然返回false则放弃对该事件处理,事件消失。

    2、如果在事件向下传递的过程中,被中途拦截,也就是View的onInterceptTouchEvent()方法返回true,那么该事件将停止向下传递,并交给该层的onTouchEvent()方法处理,无论处理成功与否,底层View将再也不会接收到该事件。PS:若处理失败,则会交由上层View的onTouchEvent()方法处理。

    3、dispatchTouchEvent()具有记忆的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那就说明下面的View都没能成功处理该事件);当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,该View直接调用自己的onTouchEvent()方法来处理该事件。

    4、记忆功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。

    附带代码

    1、MyActivity

    public class MyActivity extends ActionBarActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
        
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.i("test", "【老板】下达任务:" + Util.actionToString(ev.getAction()) + ",找个人帮我完成,任务往下分发。");
            return super.dispatchTouchEvent(ev);
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean relust = false;
            Log.i("test", "【老板】完成任务:" + Util.actionToString(event.getAction()) + ",【经理】太差劲了,以后不再找你干活了,我自来搞定!是否解决:" + Util.canDoTaskTop(relust));
            return relust;
        }
    
    }
    View Code

    2、MyFrameLayout:

    public class MyFrameLayout extends FrameLayout {
    
        public MyFrameLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.i("test", "【经理】下达任务:" + Util.actionToString(ev.getAction())  + ",找个人帮我完成,任务往下分发。");
            return super.dispatchTouchEvent(ev);
        }
        
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            boolean relust = false;
            Log.i("test", "【经理】是否拦截任务:" + Util.actionToString(ev.getAction())  + ",拦下来?" + relust);
            return relust;
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean relust = true;
            Log.i("test", "【经理】完成任务:" + Util.actionToString(event.getAction()) + ",【组长】太差劲了,以后不再找你干活了,我自来搞定!是否解决:" + Util.canDoTask(relust));
            return relust;
        }
    }
    View Code

    3、MyLinearLayout

    public class MyLinearLayout extends LinearLayout {
        
        public MyLinearLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.i("test", "【组长】下达任务:" + Util.actionToString(ev.getAction())  + ",找个人帮我完成,任务往下分发。");
            return super.dispatchTouchEvent(ev);
        }
        
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            boolean relust = true;
            Log.i("test", "【组长】是否拦截任务:" + Util.actionToString(ev.getAction())  + ",拦下来?" + relust);
            return relust;
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean relust = true;
            Log.i("test", "【组长】完成任务:" + Util.actionToString(event.getAction()) + ",【员工】太差劲了,以后不再找你干活了,我自来搞定!是否解决:" + Util.canDoTask(relust));
            return relust;
        }
    }
    View Code

    4、MyTextView

    public class MyTextView extends TextView {
    
        
        public MyTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            Log.i("test", "【员工】下达任务:" + Util.actionToString(event.getAction())  + ",我没手下了,唉~自己干吧");
            return super.dispatchTouchEvent(event);
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean relust = false;
            Log.i("test", "【员工】完成任务:" + Util.actionToString(event.getAction()) + ",【员工】现在只能靠自己了!是否解决:" + Util.canDoTask(relust));
            return relust;
        }
    }
    View Code

    5、Util(工具类)

    public class Util {
        public static String actionToString(int action){
            String result = null;
            switch(action){
            case MotionEvent.ACTION_DOWN:
                result = "ACTION_DOWN";
                break;
            case MotionEvent.ACTION_MOVE:
                result = "ACTION_MOVE";
                break;
            case MotionEvent.ACTION_UP:
                result = "ACTION_UP";
                break;
            }
            return result;
        }
        public static String canDoTask(boolean can){
            String result = null;
            if(can){
                result = "完美解决该任务!";
            }
            else{
                result = "这活搞不定,交给老大完成吧。";
            }
            return result;
        }
        public static String canDoTaskTop(boolean can){
            String result = null;
            if(can){
                result = "完美解决该任务!";
            }
            else{
                result = "这活搞不定,放弃该任务。";
            }
            return result;
        }
    }
    View Code

    附加View上的滑动事件

  • 相关阅读:
    mysql 常用的几个网址
    mysql 5.7 master/salve 切换
    oracle ebs 12.1.3 的一些日志路径
    postgresql 10.x 的命令 pg_test_fsync
    yii使用bootstrap分页样式
    Mysql让主键归0
    PHP基础知识练习
    ini_set的用法介绍
    php set_time_limit(0) 设置程序执行时间的函数
    php func_get_args
  • 原文地址:https://www.cnblogs.com/could-deng/p/5032468.html
Copyright © 2020-2023  润新知