Android 事件的传递机制

博客转自:http://blog.csdn.net/to_be_designer/article/details/48718667

我们先通过一个例子,来了解一下什么是事件的传递:

总统对省长说:我要吃红烧鱼
省长对市长说:你做个红烧鱼
市长对县长说:你做个红烧鱼
县长对农民说:你做个红烧鱼
……(农民做呀做,没做出来)
农民说:我尽力了,但真心不会做呀,饶了我吧
县长说:你个笨蛋,下次不找你了,看我来做
……(县长做呀做,没做出来)
县长对市长说:我尽力了,非常抱歉,我不会做
市长说:你个废物,要你何用,只能我自己来做了
……(市长做呀做,做成功了)
市长对省长说:红烧鱼做好了
省长说:不错,下次有事还找你
省长对总统说:红烧鱼做好了
总统说:不错,下次有事还找你

总统对省长说:我要吃水煮鱼
省长对市长说:你做个水煮鱼
市长说:县长连红烧鱼都搞不定,这次就不找他了,我自己亲自来做
……(市长做呀做,又成功了)
市长对省长说:水煮鱼做好了
省长说:不错,下次有事还找你
省长对总统说:水煮鱼做好了
总统说:不错,下次有事还找你
             ——本例引用于 @morgan_xww的《Android TouchEvent事件传递机制 》
  

我们可以把上级分发下来的任务作为事件,当有事件发生时,事件会一级一级的向下传递。通过上面的例子我们可以了解到上级可以允许事件向下传递也可以自己拦截事件进行处理。在Android中,当屏幕接收到点击事件之后,屏幕会通过底层硬件层层的处理,将事件传递到我们当前正在运行的应用程序中,应用程序首先将事件交给当前正在运行的Activity,然后Activity可以决定将事件传递给当前的布局中的ViewGroup,ViewGroup可以决定将事件拦截或者是继续传递给其内部的View控件。就这样事件通过层层的处理和传递形成了事件的传递机制。

事件传递相关API

这里写图片描述

涉及事件传递的三个方法:

public boolean dispatchTouchEvent(MotionEvent ev): 

  分发事件的方法。当接收到事件时,通过调用此方法来决定是否分发,当该方法返回true时,事件不再进行分发且已经在自身事件的分发中被消费了;当返回false时,也不对事件进行分发。也就是说当该方法不论是返回true还是返回false都不会对事件进行分发,只有当返回系统默认的 super.dispatchTouchEvent(ev)时,才会将事件分发给本层的拦截事件onInterceptTouchEvent(MotionEvent ev)去处理,如果没有onInterceptTouchEvent(MotionEvent ev)方法,则直接分发给子View中的onInterceptTouchEvent方法处理。

public boolean onInterceptTouchEvent(MotionEvent ev): 

  拦截事件的方法,从上图中我们可以看出,该方法只有在布局或者ViewGroup中才会有。通过调用该方法来决定是否对事件进行拦截。当方法返回true时,进行拦截,将事件进行分发onTouchEvent方法处理;当返回false时,不对事件进行拦截,传递给下一级处理。

public boolean onTouchEvent(MotionEvent ev): 

  处理事件。当对事件进行了拦截时,将会调用onTouchEvent方法来对事件进行处理。该方法返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View);返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理。

事件的传递机制

我们通过一个实例来验证学习一下事件的传递机制:
这里写图片描述
  如上图所示,我们来模拟Activity——>ViewGroup——>View的事件传递。首先自定义一个ViewGroup,这里我们自定义一个MyLinearLayout继承LinearLayout;然后自定义一个MyButton继承Button。

//MyLinearLayout

public class MyLinearLayout extends LinearLayout {
    public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MyLinearLayout(Context context) {
        super(context);
    }
    /*
    事件的分发
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(ev.getAction()==MotionEvent.ACTION_DOWN){
            Log.d("data", "【LinearLayout】——dispatchTouchEvent:"+ev.getAction()+"        "+super.dispatchTouchEvent(ev));
        }
        return super.dispatchTouchEvent(ev);
    }
    /*
    事件的拦截
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(ev.getAction()==MotionEvent.ACTION_DOWN){
            Log.d("data", "【LinearLayout】——onInterceptTouchEvent:"+ev.getAction()+"        "+super.onInterceptTouchEvent(ev));
        }
        return super.onInterceptTouchEvent(ev);
    }
    /*
    事件的处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction()==MotionEvent.ACTION_DOWN) {
            Log.d("data", "【LinearLayout】——onTouchEvent:"+event.getAction()+"        "+super.onTouchEvent(event));
        }
        return super.onTouchEvent(event);
    }
}

//MyButton 注意View不包含拦截事件的功能

public class MyButton extends Button {
    public MyButton(Context context) {
        super(context);
    }
    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    /*
    事件的分发
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if(event.getAction()==MotionEvent.ACTION_DOWN){
            Log.d("data", "【Button】——dispatchTouchEvent:"+event.getAction()+"        "+super.dispatchTouchEvent(event));
        }
        return super.dispatchTouchEvent(event);
    }
    /*
    事件的处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction()==MotionEvent.ACTION_DOWN){
            Log.d("data", "【Button】——onTouchEvent:"+event.getAction()+"        "+super.onTouchEvent(event));
        }
        return super.onTouchEvent(event);
    }
}

//Activity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    /*
    事件的分发
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            Log.d("data", "【Activity】——dispatchTouchEvent:" + ev.getAction() + "        " + super.dispatchTouchEvent(ev));
        }
        return super.dispatchTouchEvent(ev);
    }
    /*
    事件的处理
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.d("data", "【Activity】——onTouchEvent:" + event.getAction() + "        " + super.onTouchEvent(event));
        }
        return super.onTouchEvent(event);
    }
}

  注意为了更好的看日志打印,我们只对ACTION_DOWN事件处理。

点击LinearLayout

首先我们点击一下LinearLayout布局的空白处(非Button处),形成如下日志:
这里写图片描述

  Activity接收到点击事件,然后将其传递给LinearLayout,LinearLayout的拦截返回的是false,也就是不对事件进行拦截,此时需要分发,但是LinearLayout发现自己下面没有可以进行处理的View,只能交给自己的onTouchEvent处理,然后发现自己也处理不了,所以又提交给Activity的onTouchEvent处理,但是Activity的onTouchEvent返回的也是false,所以又交给更上一层进行处理。

点击Button

然后我们点击一下我们的Button按钮,形成如下日志:

这里写图片描述

  Activity接收到点击事件,然后将其传递给LinearLayout,LinearLayout的拦截返回的是false,也就是不对事件进行拦截,此时需要分发,LinearLayout将事件分发给Button,Button首先判断自己能否进行处理和分发,返回的都是true,但是因为Button下没有子View所以不进行分发,自己onTouchEvent处理。

  Android中的事件在表现形式上有很多,如onTouch、onClick和onLongClick等,在具体微观上的表现形式有action_down、action_move和action_up等。但是不管是哪种表现形式,都是基于事件的传递。

猜你喜欢

转载自blog.csdn.net/qq_28946307/article/details/51473731