Android_onTouch onTouchEvent 与 onClick的区别和先后调用关系

我们会经常在代码中看到触屏事件的处理,有时候使用onTouch,有时候使用了onTouchEvent,还有的时候使用了onClick,那么我们有没有对这三个方法进行过思考:

  1. 三者有什么区别呢?
  2. 要是三者都在代码中出现,应该是怎样的调用关系呢?

首先,我先把结论给出来:

  1. onTouch, onTouchEvent, onClick,三个方法的调用顺序是:
    onTouch ----> onTouchEvent -----> onclick
  2. onTouch,onClick需要实现相应的接口;onTouchEvent是View的成员方法【onTouch()的使用需要实现View.OnTouchListener,onclick()要实现View.OnClickListener】

然后,答案还是要从源码里面找:
我们之前有对Android_activity事件分发流程分析,我们已经对Android的事件分发有了整体上的了解,事件处理中主要是在dispatchTouchEvent()方法中进行处理的;接下来我们就再来看看这个dispatchTouchEvent()方法,并寻找我们需要的答案:
/frameworks/base/core/java/android/view/View.java


public boolean dispatchTouchEvent(MotionEvent event) {
    ...
   // ①验证符不符合安全策略
    if (onFilterTouchEventForSecurity(event)) {
       ...
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                // ②调用OnTouchListener的ontouch()方法
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        // ③调用ontouchEvent()方法
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    ...
}
1 onTouch()

我们在dispatchTouchEvent()方法中,看到,当当前事件满足安全策略后,就会进入处理事件的过程中,首先,我们看到有li.mOnTouchListener.onTouch的调用。在这里我们需要来看看这个mOnTouchListener是什么?

1.1 OnTouchListener
    /**
     * Interface definition for a callback to be invoked when a touch event is
     * dispatched to this view. The callback will be invoked before the touch
     * event is given to the view.
     */
    public interface OnTouchListener {
        /**
         * Called when a touch event is dispatched to a view. This allows listeners to
         * get a chance to respond before the target view.
         *
         * @param v The view the touch event has been dispatched to.
         * @param event The MotionEvent object containing full information about
         *        the event.
         * @return True if the listener has consumed the event, false otherwise.
         */
        boolean onTouch(View v, MotionEvent event);
    }

如上所示,我们可以看到。OnTouchListener是在将触摸事件分派到此视图时要调用的回调的接口定义。在将touch事件提供给视图之前将被回调。如果我们要能够回调这个onTouch()方法,就需要实现OnTouchListener接口。当我们自己在开发中实现了该接口,就在onTouch()方法中对事件进行处理。并且当前onTouch()是最早别调用的。

2 onTouchEvent()

当前面的OnTouchListener.onTouch()方法未被调用或者没有实现OnTouchListener接口,代码就会走到下面的onTouchEvent()方法中:

    public boolean onTouchEvent(MotionEvent event) {
        ...
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    ...
                    // 使用Runnable并发布此消息,而不是直接调用performClick。
                    // 这允许在单击操作开始之前更新视图的其他可视状态
                    if (mPerformClick == null) {
                        mPerformClick = new PerformClick();
                    }
                    if (!post(mPerformClick)) {
                        performClickInternal();
                    }
                    ...
                case MotionEvent.ACTION_DOWN:
                    ...
        return false;
    }

在OnTouchEvent()方法中我们看到。在action为MotionEvent.ACTION_UP时,下面有段代码:

     if (!post(mPerformClick)) {
            performClickInternal();
      }

我们先在这里给出一个小结果:post(mPerformClick)方法最后会走到onClick()方法,所以onClick()方法是在按键抬起后才执行的,而onTouchEvent()方法会分派事件时就会被执行,所有我们这里明显可以看到。onTouchEventHub()方法要在onClick()方法之前被执行;

3. onClick()

我们在上面代码中看到有post(mPerformClick)的执行,这个post方法我们在Android_UI_update(子线程更新UI的原理和源码梳理)讨论过,这里就不细说了,我们只要知道它将会在主线程中被执行,我们就直接到mPerformClick的定义出看看:

mPerformClick = new PerformClick();
...
    private final class PerformClick implements Runnable {
        @Override
        public void run() {
            performClickInternal();
        }
    }

mPerfromClick是PerformClick实例,它继承了Runable接口。然后我们在它的run()方法看到有performClickInternal()方法,这就是mPerformClick需要做的事,我们继续接着看:

    private boolean performClickInternal() {
    	...
        return performClick();
    }
....
    public boolean performClick() {
		 ...
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
			...
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
		...
        return result;
    }

顺着performClickInternal()方法一直往下看,最后我们回来到performClick()方法中,我们就主要关注一下这个方法:在performClick()方法中,会有以下的判断:

     if (li != null && li.mOnClickListener != null) {

以上判断就是确认我们当前是不是有注册监听,监听中有没有OnClickListener?如果成立的话,就会调用onClick()方法来处理当前事件。

小结

经过以上代码的梳理,我们对刚开始提出的问题和给出答案进行了源码层次的验证。再次给出三者的区别和调用关系:

  1. onTouch, onTouchEvent, onClick,三个方法的调用顺序是:
    onTouch ----> onTouchEvent -----> onclick
  2. onTouch,onClick需要实现相应的接口;onTouchEvent是View的成员方法【onTouch()的使用需要实现View.OnTouchListener,onclick()要实现View.OnClickListener
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值