美文网首页
android 事件分发

android 事件分发

作者: 放肆滴微笑 | 来源:发表于2020-01-28 14:49 被阅读0次

事件分发的核心方法

组件 dispatchTouchEvent onTouchEvent onInterceptTouchEvent
Activity 存在 存在 不存在
ViewGroup 存在 存在 存在
View 存在 存在 不存在

事件分发流程图

image.png

源码分析

1、事件从点击屏幕时触发,由手指点击屏幕,硬件通知底层,再调用java层,最后调用Activity的 public boolean dispatchTouchEvent(MotionEvent ev) 方法

/**
 * 当一个点击操作发生时,事件传递顺序 Acivity - > Window ->DecorView DecorView 一般
 * 就是当前界面的底层容器(即setContentView 所设置的View的 父容器),一个点击操作要是没有被Activity
 * 下的任何View处理,即顶层DecorView的dispatchTouchEvent()方法返回false的话
 * 则Activity的onTouchEvent()方法会调用
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 如果是down,说明是一个新的事件
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction(); // 空方法 添加自己的业务
    }
    //注释1 调用Window的superDispatchTouchEvent方法,吧事件从Activity分离到DecorView
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;  //如果返回true
    }
    return onTouchEvent(ev);
}

注释1中调用Window的superDispatchTouchEvent,也就是调用PhoneWindow的superDispatchTouchEvent,而PhoneWindow的superDispatchTouchEvent又是调用DecorView的superDispatchTouchEvent,DecorView本身就是ViewGroup的实现类,相当于调用了ViewGroup的dispatchTouchEvent

## PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

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

滑动冲突的解决方法

1、制定合适的滑动策略
2、按滑动策略分发事件
制定合适的滑动策略
如何判断水平滑动还是竖直滑动?1、可以根据水平方向或者竖直方向的距离差来判断。2、可以根据滑动路径和水平方向的夹角来判断,小于30°则为水平方向,大于60°为竖直方向,30到60之间不处理,或者根据具体业务需求

image.png
按滑动策略分发事件
  • 外部拦截法
    外部拦截法一般用在容器中,父容器拦截子容器的事件,子容器没有响应。
public boolean onInterceptTouchEvent(MotionEvent event) {
    boolean intercepted = false;
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            intercepted = false; // 必须false,否则后续的MOVE和UP不再传递给子View
            break;
        case MotionEvent.ACTION_MOVE:
            if (父容器需要当前点击事件) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        case MotionEvent.ACTION_UP:
            intercepted = false; // 必须false,会影响子View的onClick是否被触发
            break;
    }
    return intercepted;
}
  • 内部拦截法
    内部拦截法一般在子容器中实现,比如在ScrollView中嵌套的ScrollView,在第二个ScrollView中实现是否拦截事件。
public boolean dispatchTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 关闭父元素的拦截功能,当前容器拦截
            requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            if (父容器需要此类点击事件) {
                // 激活父元素原本的拦截功能 ,父容器拦截
                requestDisallowInterceptTouchEvent(false);
                break;
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.dispatchTouchEvent(event);
}

判断滑动的方向

/**
     * @param startX 按下x  这个值不能在 onTouchEvent当局部变量,因为局部变量每次按下x,y都是0
     * @param startY 按下y
     * @param endX   结束x
     * @param endY   结束y
     * @return top = 1  bottom = 2 left = 3 right = 4
     */
    public int getSlideDirection(float startX, float startY, float endX, float endY) {
        final int top = 1;
        final int bottom = 2;
        final int left = 3;
        final int right = 4;

        float x = Math.abs(endX - startX);
        float y = Math.abs(endY - startY);
        double z = Math.sqrt(x * x + y * y);
        int jiaodu = Math.round((float) (Math.asin(y / z) / Math.PI * 180));//角度
        if (endY < startY && jiaodu > 45) {//上
            Log.d(TAG, "角度:" + jiaodu + ", 動作:上");
            return top;
        } else if (endY > startY && jiaodu > 45) {//下
            Log.d(TAG, "角度:" + jiaodu + ", 動作:下");
            return bottom;
        } else if (endX < startX && jiaodu <= 45) {//左
            Log.d(TAG, "角度:" + jiaodu + ", 動作:左");
            return left;
        } else if (endX > startX && jiaodu <= 45) {//右
            Log.d(TAG, "角度:" + jiaodu + ", 動作:右");
            return right;
        }
        return -1;
    }

    float X = 0;
    float Y = 0;

    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: // 按下
                X = event.getX();
                Y = event.getY();
            case MotionEvent.ACTION_UP: // 抬起
                float upX = event.getX();
                float upY = event.getY();
                getSlideDirection(X, Y, upX, upY);
        }
        return super.onTouchEvent(event);
    }

相关文章

网友评论

      本文标题:android 事件分发

      本文链接:https://www.haomeiwen.com/subject/bdjabctx.html