zoukankan      html  css  js  c++  java
  • Android处理滑动冲突

    1、技术概述

    在Android开发中,不可避免地会遇到View嵌套的情况,而其中就会出现滑动冲突的现象,所以了解解决滑动冲突的方法是十分有必要的

    2、技术详述

    滑动冲突的场景

    常见的滑动冲突的场景主要有以下两种:

    • 父View滑动方向和子View滑动方向不一致
    • 父View滑动方向和子View滑动方向一致

    滑动冲突产生的原因

    Android的事件分发机制是从父View传递到子View的,首先事件会传递到父View的dispatchTouchEvent方法中,而该方法会调用onInterceptTouchEvent方法判断父View在此时是否需要拦截该事件,如果用户在某个方向的滑动距离超过某一个阈值,则父View的onInterceptTouchEvent方法会返回true,此时父View会直接接管该事件流程直到出现ACTION_UP事件也就是用户抬手时,事件会直接交由父View的onTouchEvent方法处理,造成了子View无法接收到事件;亦或是父View在ACTION_DOWN事件时放弃了该事件,父View会将该事件分发给所有子View处理,此时某个子View先行检测到滑动,此时子View的onInterceptTouchEvent将返回true,事件就会由子View的onTouchEvent方法处理,若该方法返回了true,那么该事件将不会被父View处理。此时便产生了滑动冲突。

    滑动冲突的解决方案

    滑动冲突的解决方法主要有两种,分为外部拦截法与内部拦截法

    外部拦截法

    外部拦截法的原理是重写父View的onInterceptTouchEvent方法,通过判断父View是否需要该事件来选择是否拦截该事件,从而解决了滑动冲突

    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: 
                //父View的ACTION_DOWN事件必须返回false,否则后续ACTION_MOVE以及ACTION_UP事件会直接交由父View处理不会经过子View
                intercepted = false;
                break;
            case MotionEvent.ACTION_MOVE: 
                if (父容器需要当前点击事件) {
                    intercepted = true;
                } else {
                    intercepted = false;
                }
                break;
            case MotionEvent.ACTION_UP: 
                //如果父View拦截了该事件,则子View会接收不到ACTION_UP事件导致点击事件失效
                intercepted = false;
                break;
            default:
                break;
        }
        mLastXIntercept = x;
        mLastYIntercept = y;
        return intercepted;
    }
    
    内部拦截法

    内部拦截法的原理是重写子View的dispatchTouchEvent方法,利用调用父View的requestDisallowInterceptTouchEvent(true)方法使父View不处理该事件让事件分发给子View处理,如果子View不需要处理该事件则调用父View的requestDisallowInterceptTouchEvent(false)使父View重新具备处理事件的能力

    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: 
                parent.requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE: 
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (父容器需要此类点击事件) {
                    parent.requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP: 
                break;
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }
    

    同时还需要重写父View的onInterceptTouchEvent方法,因为父View在处理ACTION_DOWN事件时是不受requestDisallowInterceptTouchEvent设置的标志位约束的,具体而言是在父View的dispatchTouchEvent方法中判断事件是ACTION_DOWN类型时会清除requestDisallowInterceptTouchEvent设置的标志位,如果父View处理了ACTION_DOWN事件,之后的ACTION_MOVE以及ACTION_UP事件就不会分发给子View,这时重写的·dispatchTouchEvent·方法里写的判断就都失效了。所以需要重写方法使父View不处理ACTION_DOWN事件从而让事件分发给子View

    public boolean onInterceptTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            return false;
        } else {
            return true;
        }
    }
    

    3、总结

    滑动冲突可以利用外部拦截法以及内部拦截法处理,主要利用了onInterceptTouchEventrequestDisallowInterceptTouchEvent等方法进行处理

    4、参考文献

    《Android开发艺术探索》 任玉刚著. ————电子工业出版社

  • 相关阅读:
    哈夫曼树与哈夫曼编码
    HTML & CSS整理
    MYSQL整理
    数据库报告存档
    一些网络报错信息记录
    复活~
    你必须知道的Docker数据卷(Volume)
    Sql Prompt 10下载安装破解图文教程
    通过Logstash由SQLServer向Elasticsearch同步数据
    Linux(CentOS)配置Docker随宿主机自启,容器自启( --restart=always)
  • 原文地址:https://www.cnblogs.com/Xily9/p/13184648.html
Copyright © 2011-2022 走看看