看了大神们对android事件分发机制的解析,为了方便自己理解和记忆,特意写一篇博客。

目录

方法执行顺序

各方法简单说明

getParent().requestDisallowInterceptTouchEvent(true)


方法执行顺序

boolean dispatchTouchEvent(MotionEvent ev):事件分发,Activity类,View类都有的方法
boolean onInterceptTouchEvent(MotionEvent ev):事件拦截,只有ViewGroup类才有的方法
boolean onTouch(View v, MotionEvent event):这不是View自带的方法,要通过setOnTouchListener()来添加OnTouchListener然后进入的一个方法
boolean onTouchEvent(MotionEvent event):事件消费,Activity类,View类都有的方法

例子布局

                                

布局很简单,就一个RelativeLayout里面放个自定义的LinearLayout,LinearLayout里面再放个自定义的Button。自定义了两个控件,因为要重写这两个控件里面的一些方法。 

大家都知道事件分发机制分为拦截、传递、消费。根据上面的布局,用文字表达,如一个按下的事件MotionEvent.ACTION_DOWN发生的时候,是RelativeLayout先拿到,然后看这个布局R君要不要拦截,不拦截就传递给LinearLayout布局的L君,L君不拦截就传递到B君,B君如果消费掉这次事件(就是处理了这次事件),那这个按下的事件就到此结束了。如果B君不消费,那这个事件就会回到L君,L君消费,就在L君那结束。L君不消费,事件就回到R君那里处理。

文字表达有点乱,下下面会有流程图。

Activity里就为各控件添加onTouch方法和打印日志。(以下是Activity部分代码,代码用的是ButterKnife)

    @OnTouch(R.id.btn_test1)    public boolean onTouch1(View v, MotionEvent event)    {        switch (event.getAction())        {            case MotionEvent.ACTION_DOWN:                //btnTest1.getParent().requestDisallowInterceptTouchEvent(true);                // 手指按下                Log.e("ousyxx", "downTest1");                break;            case MotionEvent.ACTION_MOVE:                // 手指移动                Log.e("ousyxx", "moveTest1");                break;            case MotionEvent.ACTION_UP:                // 手指抬起                Log.e("ousyxx", "upTest1");                break;            case MotionEvent.ACTION_CANCEL:                // 事件被拦截                Log.e("ousyxx", "cancelTest1");                break;        }        return false;    }    @OnTouch(R.id.ll_test1)    public boolean onLlTouch(View v, MotionEvent event)    {        switch (event.getAction())        {            case MotionEvent.ACTION_DOWN:                // 手指按下                Log.e("ousyxx", "downLlTest");                break;            case MotionEvent.ACTION_MOVE:                // 手指移动                Log.e("ousyxx", "moveLlTest");                break;            case MotionEvent.ACTION_UP:                // 手指抬起                Log.e("ousyxx", "upLlTest");                break;            case MotionEvent.ACTION_CANCEL:                // 事件被拦截                Log.e("ousyxx", "cancelLlTest");                break;        }        return false;    }    @OnTouch(R.id.rl_test1)    public boolean onRlTouch(View v, MotionEvent event)    {        switch (event.getAction())        {            case MotionEvent.ACTION_DOWN:                // 手指按下                Log.e("ousyxx", "downRlTest");                break;            case MotionEvent.ACTION_MOVE:                // 手指移动                Log.e("ousyxx", "moveRlTest");                break;            case MotionEvent.ACTION_UP:                // 手指抬起                Log.e("ousyxx", "upRlTest");                break;            case MotionEvent.ACTION_CANCEL:                // 事件被拦截                Log.e("ousyxx", "cancelRlTest");                break;        }        return false;    }

各方法简单说明

说明之前,大家先看看,假如你没重写过任何方法,就按照上面的布局,你点击那个MyButton控件,大概的事件传递流程如下

 

dispatchTouchEvent:有事件发生的时候,先进入这个方法。

return false: 事件不会被再进行分发。事件会被传递回上一层的view的onTouch方法、onTouchEvent方法。如果view没有添加onTouchListener,那事件就会直接到onTouchEvent;

return true:该事件就停在这方法里处理,不会继续传递。

举个例子1:加入你在Activity类里重写了dispatchTouchEvent这个方法如下,返回super.dispatchTouchEvent(ev),就是执行你没重写原本Activity类自己原本的dispatchTouchEvent方法,当mTouchType为true时就会进入return true,这时这个Activity上就什么事件都不会发生,什么点击滑动都没有,因为事件在该Activity你重写dispatchTouchEvent里已经结束了这次事件。

    @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        if (mTouchType)        {            return true;        }        return super.dispatchTouchEvent(ev);    }

举个例子2:下面是我自定义LinearLayout的代码,在dispatchTouchEvent里直接return false,那么事件就会回到R君那里,流程图大概如下

public class MyLinearLayout extends LinearLayout{    // 是否拦截    private boolean mIsIntercept = false;    public MyLinearLayout(Context context, @Nullable AttributeSet attrs)    {        super(context, attrs);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        return false;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev)    {        if (mIsIntercept)            return true;        return super.onInterceptTouchEvent(ev);    }}

onInterceptTouchEvent:所有ViewGroup类正常的情况下经过上面的方法后都会进入此拦截方法。return false:不拦截,事件传递给子viewreturn true: 拦截,事件会在该层处理上面的代码,我重新写了这个方法,假如上面的代码我没有重写dispatchTouchEvent,而是重写onInterceptTouchEvent,让它return true,流程图就如下

onTouch:控件自己setOnTouchListener添加的方法

return false: 不消费此事件,事件会传到该控件的onTouchEvent方法

return true:消费此事件,事件不会传到该控件的onTouchEvent方法,事件就此结束

onTouchEvent:要不要消费此事件的方法

return false: 不消费此事件,事件就会返回到自己的父View的onTouch继续处理

return true:消费此事件,事件就此结束

举个例子:假如你重写了Button类的onTouchEvent,让它return false,那么流程图大概如下

 

getParent().requestDisallowInterceptTouchEvent(true)

 这个方法在关于事件分发的开发中可能会见到,所以也记录一下。一般大家可能看到L君的onInterceptTouchEvent的拦截方法的重写是如下这样。可以看到,L君只在ACTION_MOVE处才return true,就是说但你按下B君控件时,按下的事件是进入了B君的onTouch1。而当你按下、滑动的时候,滑动事件就被L君拦截,那么滑动事件就会进入L君的onLlTouch。

但从打印的日志可以看到,同时也有个事件进入了B君的onTouch1,那就是MotionEvent.ACTION_CANCEL。没错当B君的事件中途被父View拦截后,就会有ACTION_CANCEL事件进入B君的onTouch1。

@Override    public boolean onInterceptTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {            case MotionEvent.ACTION_DOWN:                return false;            case MotionEvent.ACTION_MOVE:                return true;            case MotionEvent.ACTION_UP:                return false;            default:                break;        }        return super.onInterceptTouchEvent(ev);    }

这时如果B君不想这移动的事件被拦截的话,就可以使用getParent().requestDisallowInterceptTouchEvent(true)请求父View即L君不要拦截B君接下来的事件。可以看看代码如下:

@OnTouch(R.id.btn_test1)    public boolean onTouch1(View v, MotionEvent event)    {        switch (event.getAction())        {            case MotionEvent.ACTION_DOWN:                btnTest1.getParent().requestDisallowInterceptTouchEvent(true);                // 手指按下                Log.e("ousyxx", "downTest1");                break;            case MotionEvent.ACTION_MOVE:                // 手指移动                Log.e("ousyxx", "moveTest1");                break;            case MotionEvent.ACTION_UP:                // 手指抬起                Log.e("ousyxx", "upTest1");                break;            case MotionEvent.ACTION_CANCEL:                // 事件被拦截                Log.e("ousyxx", "cancelTest1");                break;        }        return false;    }

如1代码所示,在B君接收到ACTION_DOWN按下事件时执行了请求,那么接下来移动的事件还是会进入到B君的onTouch1了。

完!

更多相关文章

  1. Android(安卓)ViewDragHelper使用介绍
  2. Android事件处理(6)
  3. Android(安卓)Dialog大全
  4. Android网络编程
  5. android 屏保锁中屏掉按键和HOME键的方法
  6. android content provider概述
  7. Android刘海屏全面屏底部导航栏的适配
  8. SuperITGirl李小扣 air for android做的flash客户端,退出程序的方
  9. android检查手机和无线是否连接的方法

随机推荐

  1. Git:【Git】基础常用命令,【Linux】常用命
  2. PHP:【商城后台管理系统】部署友情链接,网
  3. 17.)PHPWeb开发框架~Laravel中模型操作使
  4. 16.)PHPWeb开发框架~Laravel中CSRF攻击原
  5. 15.)PHPWeb开发框架~Laravel中视图创建及
  6. 14.)PHPWeb开发框架~Laravel操作数据库DB
  7. 13.)PHPWeb开发框架~Laravel接收用户的输
  8. 12.)PHPWeb开发框架~Laravel入门控制器Con
  9. 11.)PHPWeb开发框架~Laravel入门路由配置
  10. 7.【商城后台管理系统】基于TP6开发友情