Android触摸事件派发机制源码分析之Activity

上篇分析

ViewGroup分析

贴上代码
MainActivity中的代码如下

package com.sparkhuu.testevent;

import android.nfc.Tag;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;

import junit.framework.Test;

import java.io.Serializable;

public class MainActivity extends AppCompatActivity implements View.OnTouchListener, View.OnClickListener {
    public static final String TAG = "test";
    RelativeLayout rl_layout;
    TestButton btn_view;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rl_layout = (RelativeLayout) this.findViewById(R.id.rl_layout);
        btn_view = (TestButton) this.findViewById(R.id.btn_view);

        btn_view.setOnTouchListener(this);
        rl_layout.setOnTouchListener(this);


        btn_view.setOnClickListener(this);
        rl_layout.setOnClickListener(this);
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        Log.i(TAG, "OnTouchListener----onTouch----action" + motionEvent.getAction() + "-----" + view);
        return false;
    }

    @Override
    public void onClick(View view) {
        Log.i(TAG, "OnClickListener---onClick---" + view);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(TAG, "MainActivity ----- dispatchTouchEvent --- action" + ev.getAction());
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public void onUserInteraction() {
        Log.i(TAG, "MainActivity --- onUserInteraction ");
        super.onUserInteraction();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "MainActivity --- onTouchEvent --- action" + event.getAction());
        return super.onTouchEvent(event);
    }
}

自定义Button和Relativelayout的代码如下

package com.sparkhuu.testevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;

/**
 * author:sparkhuu
 * email:sparkhuu@gmail.com
 */

public class TestButton extends Button {
    public TestButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(MainActivity.TAG, "TestButton ----dispatchTouchEvent -- action" + event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(MainActivity.TAG, "TestButton ----onTouchEvent -- action" + event.getAction());
        return super.onTouchEvent(event);
    }
}
package com.sparkhuu.testevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;

/**
 * author:sparkhuu
 * email:sparkhuu@gmail.com
 */

public class TestRelatvieLayout extends RelativeLayout {
    public TestRelatvieLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(MainActivity.TAG, "TestRelatvieLayout -- onInterceptTouchEvent----action" + ev.getAction());
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(MainActivity.TAG, "TestRelatvieLayout -- dispatchTouchEvent----action" + ev.getAction());

        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(MainActivity.TAG, "TestRelatvieLayout -- onTouchEvent----action" + event.getAction());

        return super.onTouchEvent(event);
    }
}

运行截图如下
这里写图片描述
可以发现,除了Activity中新添加的几个方法以外,其他的方法和之前分析的View和ViewGroup完全一致,对于Activity来说,Action_Down首先会触发dispatchTouchEvent,然后出发onUserInteraction,再次onTouchEvent,接着Action_Up事件触发dispatchTouchEvent后直接触发onTouchEvent也就说比down事件少触发了onInterceptaction事件。
点击Button外其他区域,日志如下
这里写图片描述
显而易见,和上面Button的结果类似
照样,我们从Activity的dispatchTouchEvent分析,源码走起

/**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

源码虽少,五脏俱全啊,显而易见,当触发事件是Action_down时,会调用onUserInteraction这就很好的解释了上面的困惑。下面分析下if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}这个if判断,通过activity的attach方法可以发现这里的getWindow返回的就是PhoneWindow对象(PhoneWindow是Window的实现类),也就相当于调用PhoneWindow的superDispatchTouchEvent方法,我们先看下他的抽象类Window中的抽象方法如下

   /**
     * Used by custom windows, such as Dialog, to pass the touch screen event
     * further down the view hierarchy. Application developers should
     * not need to implement or call this.
     *
     */
    public abstract boolean superDispatchTouchEvent(MotionEvent event);

从注释可以看出,用户不需要实现的方法,实际上也不行,在Activity中没有提供重写的机会,因为Window是以组合模式和Activity建立关系的,我们在看下PhoneWindow中的实现

public boolean superDispatchTouchEvent(MotionEvent event){
        return mDecor.superDispatchTouchEvent(event);
    };

我们可以发现PhoneWindow的superDispatchTouchEvent方法内又调用了
mDecor.superDispatchTouchEvent,那么mDecor是什么呢?我们在PhoneWindow中可以发现mDecor是DecorView,DecorView本身是一个PhoneWindow的内部类,同时继承了FrameLayout实现了RootViewSurfaceTaker,他是一个真正的Activity的rootview,怎么验证它是不是rootview呢?我们可以通过Hierarchy Viewer来查看如下
这里写图片描述
可以看出,我们上面例子中setContentView,在xml中放入一个Relativelayout,这个Relativelayout中包含一个Button,上面显示这个RelatviLayout放在一个id为content的Framelayout中,我们还记得上面PhoneWindow的superDispatchTouchEvent返回了DecorView的superDispatchTouchEvent,而DecorView又是Framelayout的子类,Framelayout又是ViewGroup的子类,我们接下来看下DecorView的superDispatchTouchEvent方法

public boolean superDispatchTouchEvent(MotionEvent event){
        return super.dispatchTouchEvent(event);
    };

搞了半天Activity中的dispatchTouchEvent的 if (getWindow().superDispatchTouchEvent(ev)) 本质上是ViewGroup的dispatchTouchEvent方法(这个ViewGroup是activity持有的root view 也就是id为content的FrameLayout)
总结:
1,首先会触发Activity的dispatchTouchEvent
2,dispatchTouchEvent如果是action_down会调用onUserInteraction
3,接着dispatchTouchEvent方法会通过Activity的root view(ID为content的framelayout)实质上是viewgroup,通过 super.dispatchTouchEvent(event)把touchevent派发给我们通过setcontentview设置的view
4,若activity下面的view拦截了touchevent(返回true),则Activity.onTouchEvent
不会执行
下面我们来看下onUserInteraction,代码如下

 /**
     * Called whenever a key, touch, or trackball event is dispatched to the
     * activity.  Implement this method if you wish to know that the user has
     * interacted with the device in some way while your activity is running.
     * This callback and {@link #onUserLeaveHint} are intended to help
     * activities manage status bar notifications intelligently; specifically,
     * for helping activities determine the proper time to cancel a notfication.
     *
     * <p>All calls to your activity's {@link #onUserLeaveHint} callback will
     * be accompanied by calls to {@link #onUserInteraction}.  This
     * ensures that your activity will be told of relevant user activity such
     * as pulling down the notification pane and touching an item there.
     *
     * <p>Note that this callback will be invoked for the touch down action
     * that begins a touch gesture, but may not be invoked for the touch-moved
     * and touch-up actions that follow.
     *
     * @see #onUserLeaveHint()
     */
    public void onUserInteraction() {
    }

这个方法是activity的一个方法,这个方法是空方法,那么在什么时候会调用呢?触屏点击home,menu,back键都会触发此方法,下拉statubar,旋转屏幕,锁屏不会触发,所以他会用在屏保应用上,因为当你触摸机器,马上就会触发一个事件,而这个事件又不确定是什么,正好屏保可以满足此需求,或者对于一个activity,控制多长时间没有用户相应的时候,自己消失
下面再来看下onTouchEvent

 /**
     * Called when a touch screen event was not handled by any of the views
     * under it.  This is most useful to process touch events that happen
     * outside of your window bounds, where there is no view to receive it.
     *
     * @param event The touch screen event being processed.
     *
     * @return Return true if you have consumed the event, false if you haven't.
     * The default implementation always returns false.
     */
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

如果一个锁屏事件没有被这个activity下任何view处理,activity的ontouchevent将会调用,这对于处理window边界之外的touch事件非常有用,因为通常是因为没有view会接受到他们,返回true表示已经消费事件,反之,则没有消费。从中看出重点就这句代码 if (mWindow.shouldCloseOnTouch(this, event))整理的mwindow就是上面的dispatchtouchevent中的getwindow对象,所以直接到window抽象类和phonewindow子类查看,发现PhoneWindow没有重写Window的shouldCloseOnTouch方法,看下Window中的shouldCloseOnTouch方法代码如下

 /** @hide */
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
                && isOutOfBounds(context, event) && peekDecorView() != null) {
            return true;
        }
        return false;
    }

其实就是一个判断,判断mCloseOnTouchOutside标记是否为action_down事件,同时判断x,y坐标是不是超过Bounds,然后检查framelayout的id为content的DecorView是否为空

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值