您的位置:首页 > 移动开发 > Android开发

深入浅出 Android Touch 事件分发拦截相应机制

2015-08-20 11:56 651 查看

主要讲ViewGroup的 Touch机制

原创地址:/article/1973052.html

欢迎大家访问我的Github开源库,这里有好玩的App源码,想和大家分享。https://github.com/ChoicesWang

Android中的Touch分为三个层面。Activity层,ViewGroup层,View层。今天主要讲ViewGroup。

先看下面的:

[code]public class CusFrameLayout extends FrameLayout {

    public CusFrameLayout(Context context) {
        super(context);
    }

    public CusFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CusFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 获取当前Layout的名字
     *
     * @return name
     */
    private String getName() {
        switch (getId()) {
            case R.id.red:
                return "Red_Layout";
            case R.id.blue:
                return "Blue_Layout";
            case R.id.yellow:
                return "Yellow_Layout";
            default:
                return "00000";
        }
    }

    private void showLog(String method, String action) {
        Log.d("WoW", getName() + " ---- " + method + " ---- " + action);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                showLog("dispatchTouchEvent()", "ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                showLog("dispatchTouchEvent()", "ACTION_MOVE");
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                showLog("dispatchTouchEvent()", "ACTION_CANCEL & ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                showLog("onInterceptTouchEvent()", "ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                showLog("onInterceptTouchEvent()", "ACTION_MOVE");
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                showLog("onInterceptTouchEvent()", "ACTION_CANCEL & ACTION_UP");
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                showLog("onTouchEvent()", "ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                showLog("onTouchEvent()", "ACTION_MOVE");
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                showLog("onTouchEvent()", "ACTION_CANCEL & ACTION_UP");
                break;
        }

        return super.onTouchEvent(ev);
    }
}


这是一个自定义的Layout。其中我们Override了父类的三个有关Touch的方法:

方法解释
dispatchTouchEvent()分发
onInterceptTouchEvent()拦截
onTouchEvent()响应
这其中最常见的 事件 都是那些呢 :

事件Action解释
ACTION_DOWN手指头按下屏幕
ACTION_MOVE手指头开始移动
ACTION_CANCEL & ACTION_UP 手指头离开屏幕 (抬起)

然后我们看一下XML吧



[code]    <com.choices.touchevent.CusFrameLayout
        android:id="@+id/red"
        android:layout_width="300dp"
        android:layout_height="360dp"
        android:background="#FFFF1744">

        <com.choices.touchevent.CusFrameLayout
            android:id="@+id/blue"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:background="#FF81D4FA">

            <com.choices.touchevent.CusFrameLayout
                android:id="@+id/yellow"
                android:layout_width="120dp"
                android:layout_height="120dp"
                android:layout_gravity="center"
                android:background="#FFFFB300" />
        </com.choices.touchevent.CusFrameLayout>

    </com.choices.touchevent.CusFrameLayout>


简单的说一下结构吧:

最下层是 红色 的 FrameLayout . 然后他里面包括了两个子Layout。蓝色 和 红色.

中间层是 蓝色.

最上层是 ***.

这个时候,我们要看点击屏幕的时候,事件到底是怎么传递的

细心的同学已经发现,我加了Log。那怎么就看看log是怎么样的吧。

当我们点击上图中箭头的地方:



在log中我们看到。Action_Down 完整的一个过程如下:

红色 —— 蓝色 —— *** —— 蓝色 —— 红色

这是因为,Down事件一直没有被相应,处理的方法是:

红色(分发 拦截)——蓝色(分发 拦截)——***(分发 拦截 响应)—— 蓝色(响应)—— 红色 ( 响应 )

妈蛋,为什么只有Action_Down? Move 和 Up去哪了?

这位同学问的好,你平时一定是一位聪明善良,善解人意的人。我正要讲这个呢。

我们都知道,手指头触摸屏幕一般是这个过程:

按下 ——> 移动 ——> 抬起

只有你在 Action_Down 的时候 return true 。他才能知道你接下来还要干什么。

让我们试试吧。



我们在dispatchTouchEvent() 方法中,在Action_Down,并且是红色Layout下,return true;

我们看看Log:



因为我们在 红色 Layout的 分发Down时,return true。这时候,只有红色能够接受到这些事件,其他View的事件都没有被分发。并且,不会执行红色Layout的onInterceptTouchEvent()方法。除了Down之外的其他Action都执行了 分发 和 响应 方法。

大家可以下载我的源码,然后去试试

https://github.com/ChoicesWang/TouchEvent 源码地址

总结

Target View 指的是,注册了onClickListener,或者 onTouchEvent 和 返回 true的View。就是你想让事件所响应的View。

Android的TouchEvent通常包含三个动作,ACTION_DOWN,ACTION_MOVE与ACTION_UP。发出的顺序是DOWN->MOVE->MOVE->…->UP(注意MOVE事件是否能够被触发取决于操作手势里面是否包含了移动的动作)。

消息分发流程,从上到下,从父到子:Activity->ViewGroup1->ViewGroup1的子ViewGroup2->…->Target View

消息响应流程,从下到上,从子到父:Target View->…->ViewGroup1的子ViewGroup2->ViewGroup1->Activity

public boolean dispatchTouchEvent(MotionEvent ev);

事件分发处理函数,通常会在Activity层根据UI的显示情况,把事件传递给相应的ViewGroup。上面的演示代码中,为了方便模拟,会直接return true,只有这样,才会接受到 Move 事件。

public boolean onInterceptTouchEvent(MotionEvent ev);

对分发的事件进行拦截,注意拦截ACION_DOWN与其他ACTION的差异。

第1种情况:如果ACTION_DOWN的事件没有被拦截,顺利找到了TargetView,那么后续的MOVE与UP都能够下发。如果后续的MOVE与UP下发时还有继续拦截的话,事件只能传递到拦截层,并且发出ACTION_CANCEL。

第2种情况:如果ACITON_DOWN的事件下发时被拦截,导致没有找到TargetView,那么后续的MOVE与UP都无法向下派发。

public boolean onTouchEvent(MotionEvent ev);

响应处理函数,如果有设置对应listener的话,这里还会与onTouch,onClick,onLongClick有关联。具体执行顺序是onTouch()->onTouchEvent()->onClick()->onLongClick()。是否能够顺序执行,取决于每个方法的返回值是true还是false

强关注点:dispatch与intercept的差异,ACTION_DOWN与其他ACITON会对寻找target组件带来差异,而是否寻找到Target组件对整个流程有着重大的的影响。

原创地址:/article/1973052.html

欢迎大家访问我的Github开源库,这里有好玩的App源码,想和大家分享。https://github.com/ChoicesWang
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: