美文网首页
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