编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 1.1App启动流程梳理
  • 1.2ActivityThread分析
  • 1.3Context设计思想
  • 2.1Activity基础介绍
  • 2.2Activity启动流程
  • 2.3Activity布局创建
  • 2.4Activity布局绘制
  • 2.5Service基础介绍
  • 2.6Service启动流程
  • 2.7Receiver广播基础
  • 2.8Receiver深入原理
  • 2.9ContentProvider分析
  • 2.10Fragment实践
  • 2.11Intent深入思考
  • 3.1Paint和Canvas
  • 3.2View的绘制基础
  • 3.3onMeasure流程设计
  • 3.4onLayout流程设计
  • 3.5onDraw流程设计
  • 3.6View工作原理
  • 3.7View刷新设计流程
  • 3.8自定义View控件
  • 3.9自定义ViewGroup控件
  • 4.1Handler基础使用
  • 4.2消息机制流程分析
  • 4.3Handler深度解析
  • 4.4Message深度理解
  • 4.5MessageQueue解析
  • 4.6Looper深度解析
  • 4.7理解Handler同步屏障
  • 4.8ThreadLocal分析
  • 4.9ThreadLocal场景
  • 5.1View事件设计思考
  • 5.2View滑动冲突处理
  • 5.3View事件源码分析
  • 5.4View事件总结案例
  • 6.1DecorView设计思想
  • 6.2视图的载体View
  • 6.3视图管理者Window
  • 6.4窗口管理服务WMS
  • 6.5布局解析者Inflater
  • 7.1AsyncTask深入介绍
  • 7.2HandlerThread设计
  • 7.3IntentService设计
  • 8.1IPC通信方式介绍
  • 8.2AIDL进程间通信
  • 8.7Binder通信机制设计
  • /zh/android/basic/9.1注解设计思想和原理.html
  • 9.2APT技术设计详解
  • 9.3APT多种案例实践

5.2View滑动冲突处理

目录介绍

  • 01.什么是滑动冲突
  • 02.外部拦截法
  • 03.内部拦截法
  • 04.滑动冲突实例
  • 05.外部拦截法解决滑动冲突
  • 06.内部拦截法解决滑动冲突

01.什么是滑动冲突

  • 当父容器与子 View 都可以滑动时,就会产生滑动冲突。
  • 解决 View 之间的滑动冲突的方法分为两种,分别是外部拦截法和内部拦截法

02.外部拦截法

  • 父容器根据需要在 onInterceptTouchEvent 方法中对触摸事件进行选择性拦截,思路可以看以下伪代码
    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: {
                intercepted = false;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (满足父容器的拦截要求) {
                    intercepted = true;
                } else {
                    intercepted = false;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }
        mLastXIntercept = x;
        mLastYIntercept = y;
        return intercepted;
    }
  • 思路如下所示
    • 根据实际的业务需求,判断是否需要处理 ACTION_MOVE 事件,如果父 View 需要处理则返回 true,否则返回 false 并交由子 View 去处理
    • ACTION_DOWN 事件需要返回 false,父容器不能进行拦截,否则根据 View 的事件分发机制,后续的 ACTION_MOVE 与 ACTION_UP 事件都将默认交由父容器进行处理
    • 原则上 ACTION_UP 事件也需要返回 false,如果返回 true,那么子 View 将接收不到 ACTION_UP 事件,子 View 的onClick 事件也无法触发

03.内部拦截法

  • 内部拦截法则是要求父容器不拦截任何事件,所有事件都传递给子View,子View根据需求判断是自己消费事件还是传回给父容器进行处理,思路可以看以下伪代码:
    • 子 View 修改其 dispatchTouchEvent 方法
    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);
    }
    • 父容器修改其 onInterceptTouchEvent 方法
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            return false;
        } else {
            return true;
        }
    }
  • 思路所示
    • 内部拦截法要求父容器不能拦截 ACTION_DOWN 事件,否则一旦父容器拦截 ACTION_DOWN 事件,那么后续的触摸事件都不会传递给子View
    • 滑动策略的逻辑放在子 View 的 dispatchTouchEvent 方法的 ACTION_MOVE 事件中,如果父容器需要处理事件则调用 parent.requestDisallowInterceptTouchEvent(false) 方法让父容器去拦截事件

04.滑动冲突实例

  • 场景解释:
    • 为了能使整个Activity界面能够上下滑动,使用了ScrollView,将Tablayout和ViewPager的联合包裹在LinearLayout中,作为一部分。
  • 代码如下所示
    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:background="@drawable/bg_autumn_tree_min"/>
    
            <include layout="@layout/include_reflex_view"/>
    
            <android.support.design.widget.TabLayout
                android:id="@+id/tab_layout"
                android:layout_width="match_parent"
                android:layout_height="50dp"/>
    
            <android.support.v4.view.ViewPager
                android:id="@+id/vp_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    
        </LinearLayout>
    
    </ScrollView>
  • 遇到问题:
    • 正常使用情况下,ViewPager中的Fragment没有显示出来,需要设置ViewPager的高度才可以,结果是ViewPager中的Fragment可以正常显示。
    • 由于滑动方法不一样,导致滑动冲突。

05.外部拦截法解决滑动冲突

  • 滑动方向不同之以ScrollView与ViewPager为例的外部解决法
    • 从 父View 着手,重写 onInterceptTouchEvent 方法,在 父View 需要拦截的时候拦截,不要的时候返回false,代码大概如下
    举例子:以ScrollView与ViewPager为例
    public class MyScrollView extends ScrollView {
    
        public MyScrollView(Context context) {
            super(context);
        }
    
        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(21)
        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        private float mDownPosX = 0;
        private float mDownPosY = 0;
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            final float x = ev.getX();
            final float y = ev.getY();
            final int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    mDownPosX = x;
                    mDownPosY = y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float deltaX = Math.abs(x - mDownPosX);
                    final float deltaY = Math.abs(y - mDownPosY);
                    // 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截
                    if (deltaX > deltaY) {
                        return false;
                    }
            }
            return super.onInterceptTouchEvent(ev);
        }
    }

06.内部拦截法解决滑动冲突

  • 从子View着手,父View 先不要拦截任何事件,所有的 事件传递给子View,如果子View需要此事件就消费掉,不需要此事件的话就交给 父View 处理。
  • 实现思路 如下,重写 子View 的dispatchTouchEvent方法,在Action_down动作中通过方法requestDisallowInterceptTouchEvent(true) 先请求 父View 不要拦截事件,这样保证子View能够接受到Action_move事件,再在Action_move动作中根据自己的逻辑是否要拦截事件,不要的话再交给 父View 处理
    public class MyViewPager extends ViewPager {
    
        private static final String TAG = "yc";
    
        int lastX = -1;
        int lastY = -1;
    
        public MyViewPager(Context context) {
            super(context);
        }
    
        public MyViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int x = (int) ev.getRawX();
            int y = (int) ev.getRawY();
            int dealtX = 0;
            int dealtY = 0;
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    dealtX = 0;
                    dealtY = 0;
                    // 保证子View能够接收到Action_move事件
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_MOVE:
                    dealtX += Math.abs(x - lastX);
                    dealtY += Math.abs(y - lastY);
                    Log.i(TAG, "dealtX:=" + dealtX);
                    Log.i(TAG, "dealtY:=" + dealtY);
                    // 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截
                    if (dealtX >= dealtY) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    } else {
                        getParent().requestDisallowInterceptTouchEvent(false);
                    }
                    lastX = x;
                    lastY = y;
                    break;
                case MotionEvent.ACTION_CANCEL:
                    break;
                case MotionEvent.ACTION_UP:
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    }

其他介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yczbj/activities
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
贡献者: yangchong211
上一篇
5.1View事件设计思考
下一篇
5.3View事件源码分析