前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android TouchEvent事件传递机制

Android TouchEvent事件传递机制

作者头像
提莫队长
发布2019-02-21 15:43:08
3420
发布2019-02-21 15:43:08
举报
文章被收录于专栏:刘晓杰刘晓杰

本文从http://blog.csdn.net/morgan_xww/article/details/9372285http://www.cnblogs.com/linjzong/p/4191891.html整理而来

跟touch事件相关的3个方法: public boolean dispatchTouchEvent(MotionEvent ev); //用来分派event public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event public boolean onTouchEvent(MotionEvent ev); //用来处理event

三个方法的用法: dispatchTouchEvent() 用来分派事件。 其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法 onInterceptTouchEvent() 用来拦截事件。 ViewGroup类中的源码实现就是{return false;}表示不拦截该事件, 事件将向下传递(传递给其子View); 若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递, 事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法 onTouchEvent() 用来处理事件。 返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View); 返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理

先上一段代码演示: TestScrollView.java

代码语言:javascript
复制
package com.example.dispatchevent;

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

public class TestScrollView extends ScrollView {

    public TestScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("dispatchTouchEvent", "TestScrollView");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("onInterceptTouchEvent", "TestScrollView");
        return super.onInterceptTouchEvent(ev);
        //return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        Log.i("onTouchEvent", "TestScrollView");
        return super.onTouchEvent(ev);
    }
}

MyButton.java

代码语言:javascript
复制
package com.example.dispatchevent;

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

public class MyButton extends Button {
    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("dispatchTouchEvent", "MyButton");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        Log.i("onTouchEvent", "MyButton");
        return super.onTouchEvent(ev);
    }
}

TestActivity.java

代码语言:javascript
复制
package com.example.dispatchevent;

import android.app.Activity;
import android.os.Bundle;

public class TestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
    }
}

test.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.dispatchevent.TestScrollView
        android:id="@+id/testScrollView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <com.example.dispatchevent.MyButton
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>
    </com.example.dispatchevent.TestScrollView>

</LinearLayout>

当TestScrollView 的onInterceptTouchEvent返回true时,表示被拦截,那么调用自身的onTouchEvent

这里写图片描述
这里写图片描述

当返回默认值(false)时,表示不拦截,那么往下传到button

这里写图片描述
这里写图片描述

演示完毕。接下来要解释两个地方

1.onInterceptTouchEvent返回默认值(false)? 跟踪源码

代码语言:javascript
复制
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onMotionEvent will be called and we do the actual
         * scrolling there.
         */

        /*
        * Shortcut the most recurring case: the user is in the dragging
        * state and he is moving his finger.  We want to intercept this
        * motion.
        */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }

        /*
         * Don't try to intercept touch if we can't scroll anyway.
         */
        if (getScrollY() == 0 && !canScrollVertically(1)) {
            return false;
        }

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                /*
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                 * whether the user has moved far enough from his original down touch.
                 */

                /*
                * Locally do absolute value. mLastMotionY is set to the y value
                * of the down event.
                */
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    // If we don't have a valid id, the touch down wasn't on content.
                    break;
                }

                final int pointerIndex = ev.findPointerIndex(activePointerId);
                if (pointerIndex == -1) {
                    Log.e(TAG, "Invalid pointerId=" + activePointerId
                            + " in onInterceptTouchEvent");
                    break;
                }

                final int y = (int) ev.getY(pointerIndex);
                final int yDiff = Math.abs(y - mLastMotionY);
                if (yDiff > mTouchSlop) {
                    mIsBeingDragged = true;
                    mLastMotionY = y;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    if (mScrollStrictSpan == null) {
                        mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                    }
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }

            case MotionEvent.ACTION_DOWN: {
                final int y = (int) ev.getY();
                if (!inChild((int) ev.getX(), (int) y)) {
                    mIsBeingDragged = false;
                    recycleVelocityTracker();
                    break;
                }

                /*
                 * Remember location of down touch.
                 * ACTION_DOWN always refers to pointer index 0.
                 */
                mLastMotionY = y;
                mActivePointerId = ev.getPointerId(0);

                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);
                /*
                * If being flinged and user touches the screen, initiate drag;
                * otherwise don't.  mScroller.isFinished should be false when
                * being flinged.
                */
                mIsBeingDragged = !mScroller.isFinished();
                if (mIsBeingDragged && mScrollStrictSpan == null) {
                    mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                }
                break;
            }

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                /* Release the drag */
                mIsBeingDragged = false;//AAAAAAAAAAAAAAAA
                mActivePointerId = INVALID_POINTER;
                recycleVelocityTracker();
                if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
                    postInvalidateOnAnimation();
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }

        /*
        * The only time we want to intercept motion events is if we are in the
        * drag mode.
        */
        return mIsBeingDragged;
    }

MotionEvent一共有多种值,但是最后的落脚点一定是ACTION_UP,(看标注了AAAAAA的那一行),里面mIsBeingDragged = false;最后return mIsBeingDragged表示默认值是false

2.演示的图片里面为什么有两份调用 因为我点击了一次,依次点击包含两种ACTION:ACTION_DOWN和ACTION_UP,所以有两份。

3.演示图片中第二份为什么会少了一个onInterceptTouchEvent调用 dispatchTouchEvent()方法中还有“记忆”的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?如果该事件由我的onTouchEvent()来处理,那就说明被拦截);当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,该View直接调用自己的onTouchEvent()方法来处理该事件。 当然,“记忆”功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年04月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档