两种方法,教你解决 ViewPager 嵌套 ViewPager滑动冲突(一)

简介: 两种方法,教你解决 ViewPager 嵌套 ViewPager滑动冲突

这篇博客主要讲解一下几个问题


  • 粗略地介绍一下View的事件分发机制
  • 解决事件滑动冲突的思路及方法
  • ScrollView 里面嵌套ViewPager导致的滑动冲突
  • ViewPager里面嵌套ViewPager 导致的滑动冲突
  • 轮播图的几种实现方式


文章首发地址CSDN:http://blog.csdn.net/gdutxiaoxu/article/details/52939127


先看一下效果图


ScrollView里面嵌套ViewPager


bc4b6672185f0dee959c055992493e04_20210515123641411.gif


ViewPager里面嵌套ViewPager


8979b7eddde81c5ea18215c13a404cf3_20210515123628654.gif


View的 事件分发机制


这篇博客不打算详细讲解View的事件分发机制,因为网上已经出现了一系列的好 文章,我自己的水平也有限,目前肯定写得不咋的。


先啰嗦一下,View 的事件分发机制主要涉及到一下三个 方法


  • dispatchTouchEvent ,这个方法主要是用来分发事件的
  • onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的是ViewGroup才有这个方法,View没有onInterceptTouchEvent这个方法
  • onTouchEvent 这个方法主要是用来处理事件的
  • requestDisallowInterceptTouchEvent(true),这个方法能够影响父View是否拦截事件,true 表示父 View 不拦截事件,false 表示父 View 拦截事件


下面引用图解 Android 事件分发机制这一篇博客的内容

15997180f9b47c406f7adac6c44156ed_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2dkdXR4aWFveHU=,size_16,color_FFFFFF,t_70.png


  • 仔细看的话,图分为3层,从上往下依次是Activity、ViewGroup、View
  • 事件从左上角那个白色箭头开始,由Activity的dispatchTouchEvent做分发
  • 箭头的上面字代表方法返回值,(return true、return false、return super.xxxxx(),super 的意思是调用父类实现。
  • dispatchTouchEvent和 onTouchEvent的框里有个【true---->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。
  • 目前所有的图的事件是针对ACTION_DOWN的,对于ACTION_MOVE和ACTION_UP我们最后做分析。
  • 之前图中的Activity 的dispatchTouchEvent 有误(图已修复),只有return super.dispatchTouchEvent(ev) 才是往下走,返回true 或者 false 事件就被消费了(终止传递)。


总结


当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,


  • 如果dispatchTouchEvent返回true 消费事件,事件终结。


  • 如果dispatchTouchEvent返回 false ,则回传给父View的onTouchEvent事件处理;


onTouchEvent事件返回true,事件终结,返回false,交给父View的OnTouchEvent方法处理


  • 如果dispatchTouchEvent返回super的话,默认会调用自己的onInterceptTouchEvent方法


默认的情况下interceptTouchEvent回调用super方法,super方法默认返回false,所以会交给子View的onDispatchTouchEvent方法处理


如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,


如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。


关于更多详细分析,请查看原博客图解 Android 事件分发机制,真心推荐,写得很好。


解决事件滑动冲突的思路及方法


常见的三种情况


第一种情况,滑动方向不同


b59e857252fd844a362db7bbe7feb132_56f7666a8a44e63896d91d80580231ef.png


第二种情况,滑动方向相同


00142d91b05781a8e2343d94080fdf50_03dbb090cdd0f27a2464c332fdc4ea4f.png


第三种情况,上述两种情况的嵌套


c167df08e953e7e83cdb7496f0deac52_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2dkdXR4aWFveHU=,size_16,color_FFFFFF,t_70.png


解决思路


看了上面三种情况,我们知道他们的共同特点是父View 和子View都想争着响应我们的触摸事件,但遗憾的是我们的触摸事件 同一时刻只能被某一个View或者ViewGroup拦截消费,所以就产生了滑动冲突?那既然同一时刻只能由某一个View或者ViewGroup消费拦截,那我们就只需要 决定在某个时刻由这个 View 或者 ViewGroup 拦截事件,另外的 某个时刻由 另外一个 View 或者 ViewGroup 拦截事件,不就OK了吗?综上,正如 在 《Android开发艺术》 一书提出的,总共 有两种解决方案


以下解决思路来自于 《Android开发艺术》 书籍


下面的两种方法针对第一种情况(滑动方向不同),父View是上下滑动,子View是左右滑动的情况。


外部解决法


从父View着手,重写onInterceptTouchEvent方法,在父View需要拦截的时候拦截,不要的时候返回false,为代码大概 如下


@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);
}


内部解决法


从子View着手,父View先不要拦截任何事件,所有的事件传递给 子View,如果子View需要此事件就消费掉,不需要此事件的话就交给 父View处理。


实现思路 如下,重写子 View的dispatchTouchEvent方法,在Action_down 动作中通过方法 requestDisallowInterceptTouchEvent(true) 先请求 父 View不要拦截事件,这样保证子 View 能够接受到 Action_move 事件,再在 Action_move 动作中根据自己的逻辑是否要拦截事件,不需要拦截事件的话再交给 父 View 处理。


@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);
}

相关文章
|
9月前
|
机器学习/深度学习 设计模式 人工智能
TinyAI :全栈式轻量级 AI 框架
一个完全用Java实现的全栈式轻量级AI框架,TinyAI IS ALL YOU NEED。
TinyAI :全栈式轻量级 AI 框架
|
10月前
|
传感器 数据可视化 流计算
《3D游戏动作交互优化:剑舞穿模与落地延迟的解决策略》
本文聚焦3D武侠游戏角色动画与物理引擎协同优化,针对初始版本中剑舞穿模、跳跃落地0.5秒物理延迟、衣物动画脱节等问题,展开多维度解决方案探索。通过“动画-物理混合驱动”,拆分核心骨骼与附属部件驱动方式;开发“动态碰撞体生成系统”,适配角色动作形态变化;建立“动作-物理参数映射表”,差异化匹配武器攻击反馈;设计“动作-场景交互绑定”,同步角色与场景物体运动;研发专用调试工具提升优化效率。
459 2
|
10月前
|
存储 弹性计算 文件存储
阿里云渠道商:云文件存储 NAS 怎么使用?
阿里云文件存储NAS提供全托管、弹性扩展的共享存储服务,支持多协议无缝挂载,适用于Web集群、容器持久化与大数据场景。具备高可用、高并发、低成本等优势,助力企业高效上云。
|
算法 Java Go
运行时管理GO与Java的概要对比
【5月更文挑战第17天】本文介绍Go、Python和Java的运行时机制各异。Go是编译型语言,其runtime负责内存管理、GC和协程调度,强调性能和低延迟。Java的JVM兼顾跨平台和性能,使用字节码和JIT编译,其GC策略复杂且高效。三种语言在设计和优化上各有侧重,适用不同场景。
565 3
|
IDE 程序员 API
一文彻底拿捏DevEco Studio的使用小技巧
本文介绍了DevEco Studio的安装与使用,包括其特性、常用快捷键、设置中文环境和文件注释模板等内容。DevEco Studio是华为为HarmonyOS开发者提供的集成开发环境(IDE),最新版本5.0.0增加了多项功能,如工程外模块代码支持、构建字节码har包等,极大提升了开发效率。通过简单步骤即可完成安装,并能轻松查阅组件API文档。跟随程序员Feri一起快速掌握鸿蒙开发!
1969 78
|
JSON JavaScript 前端开发
js跨域实现
【10月更文挑战第31天】在实际开发中,需要根据具体的需求和项目情况选择合适的跨域解决方案。
344 1
|
存储 安全 Java
Android 面试题及答案整理,最新面试题
Android 面试题及答案整理,最新面试题
793 2
|
传感器 数据采集 监控
基于STM32的温室大棚环境检测及自动浇灌系统设计
基于STM32的温室大棚环境检测及自动浇灌系统设计
4190 0
|
安全 网络协议 Linux
在CentOS 7上安装和使用Webmin的方法
在CentOS 7上安装和使用Webmin的方法
706 0