Android View滑动处理大法

news2024/11/12 11:18:33

原文链接 Android View滑动处理大法

对于触控式操作来说,滑动是一个特别重要的手势操作,如何做到让应用程序的页面滑动起来如丝般顺滑,让用户感觉到手起刀落的流畅感,是开发人猿需要重点解决的问题,这对提升用户体验是最为重要的事情。本文就将探讨一下,Android中View的滑动相关知识,以及如何做到丝般顺滑。

如何让View滑动起来

View的滑动是GUI支持的一项基本特性,就像触摸事件一件,这是废话,平台如果不支持,你还搞个毛线。

View滑动的基本原理

我们先来看一下Android中实现View的滑动的基本原理。其实屏幕并没有动啊,一个View的可绘制区域,对于屏幕来说,对于view tree来说都是没有变化 的。父布局给某一个View的绘制区域是在layout之后就确定好了的,当View的真实高度或者宽度超过了这块可绘制区域,那么就需要滑动才可以把整个View做到用户可见。View内部通过两个关键成员变量mScrollX和mScrollY来记录滑动之后的坐标,View本身有mLeft和mTop来标识自己相对于父布局的坐标位置,那么当有滑动的时候,在此View当中具体要绘制的区域就变成了以mLeft+mScrollX和mTop+mScrollY为起点的区域了。由此View便滚动起来了。

如何实现View的滑动

对于开发人猿来说,实现View的滑动,需要关注三个重要的方法,也即是View#scrollBy,View#scrollTo以及View#onScrollChanged,这是实现滑动的三个最为核心的方法。

scrollBy提供的参数是需要滑动的距离,而scrollTo则是需要传入要滑动到的目标坐标值,这两个方法都是要修改mScrollX和mScrollY的值,本质上是一样的。而onScrollChanged则是一个回调,用以通知更新了的滑动位置。

Scroll手势

要想让View滑动起来,离不开事件手势的支持。最简单也是最直接的手势就是onScroll手势,这个在GestureDetecor中可以识别出此手势,或者自己去直接处理touch event也可以得出此手势。这个并不复杂,就是直接通过touch 事件来计算滑动多少距离就好了,按照View预设计的可以滑动的方向,比如横向就计算不同时间点MotionEvent的坐标值,得到一个水平距离deltaX,然后调用scrollBy即可。垂直方向依此类推。

Scroll手势简单是因为它是直接来源于事件,且速度较慢,并不需要额外处理,所以整体逻辑处理流程并不复杂。

在GestureDetector中的识别就是在ACTION_MOVE时,查看滑动过的距离,这个距离(由sqrt(dx x dx, dy x dy)如果大于touch slop,就会触发onScroll手势回调。

Fling手势

Fling也即是快速滑动,就是手指在屏幕上使劲的『挠』一下,手势的要点是手指在屏幕快速滑过一小段短距离,就像把一个小球弹出去的感觉一样。对于Fling手势来说,最重要的是速度,水平方向的速度和垂直方向的速度,可以理解为高中物理常讲到的平抛运动一样。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zolPYiCN-1688998312494)(https://ts1.cn.mm.bing.net/th/id/R-C.16bf196415d4a9adb260a465059753cf?rik=iJZkKq8qecE9ew&riu=http%3a%2f%2fwww.androidpolice.com%2fwp-content%2fuploads%2f2017%2f05%2fnexus2cee_fling-animation.gif&ehk=FJQxdKhU05GazX23tULh7WVJ3Spv1upP6zHbMY8s9YA%3d&risl=&pid=ImgRaw&r=0)]

GestureDetector识别Fling的逻辑是,在ACTION_UP时,检查此次事件的速度,如果水平方向速度或者垂直方向速度超过了阈值,便会触发Fling手势回调。

注意:留意Scroll与Fling的区别,Scroll是慢的,不关心时间与速度,只关心滑动的距离,是在ACTION_MOVE时,手指并未有离开屏幕时就触发了,只要是ACTION_MOVE还在继续,就会继续触发onScroll,并且ACTION_UP时终止整个Scroll,而Fling只关心速度,不关心距离,是在ACTION_UP时,手指离开了屏幕了(此次事件流处理结了)才会触发。

VelocityTracker

Fling事件速度是决定性的,仔细看GestureDetector的处理过程会发现它使用了一个叫做VelocityTracker的对象,来帮忙处理一些关于速度的具体逻辑,那么有必要深入了解一下这个对象。

VelocityTracker使用起来并不复杂,获取它的一个对象后,只需要不断的把MotionEvent塞给它就可以了,然后在需要的时候让其计算两个方向上的速度,然后就没有然后了:

    velocityTracker = VelocityTracker.obtain();
    
    onTouchEvent(MotionEvent ev) {
        velocityTracker.addMovement(ev);
        
        if (want to know velocities) {
           velocityTracker.computeCurrentVelocity(100);
           vx = velocityTracker.getXVelocity();
           vy = veolocityTracker.getYVelocity();
           be happy with vx and vy.
        }
     }

这个类的实现,值得仔细看一下,它主要的实现都是用JNI去实现,可能是因为计算方式较复杂,所以computeCurrentVelocity方法也说明了,让你真用的时候再调,这个不用去管细节实现。重点看一下这个类,里面有一个对象池,用以缓存对象,并且创建对象的方式并不是直接new,而是用其obtain方法。这里用的是叫享元(Flyweight Pattern)的设计模式,也就是说VelocityTracker对象其实是共享的。

顺滑如丝

前面提到了,让View滑动,只需要调用scrollBy或者scrollTo即可,但这个吧,是直接修改了mScrollX,mScrollY,然后invalidate,View下次draw时就直接在把目标区域内容绘制出来了,换句话说这两个方法滑动是瞬间跳格式的。

一般来说,这也没有问题,就像onScroll手势,ACTION_MOVE时,不断的scrollBy刚刚滑过的距离,都还okay,没有什么问题。

但是对于Fling事件就不行了,Fling事件,也即快速滑动,要求短时间内进行大距离滑动,或者像有跳转的需求时,也是短时间内要滑动大距离。如果直接scrollBy或者scrollTo一步到位了,会显得 相当的突兀,体验相当不好,卡顿感特别强。如果能像做动画那样,在一定时间内,让其平滑的滑动,就会如丝般顺滑,体验好很多。Scroller就是专门用来解决此问题的。

Scroller

Scroller是对滑动的封装,并不是View的子类,其实它跟View一点关系也没有,也不能操作View,实际上它与属性动画类似,它仅是一个滚动位置的计算器,告诉它起始位置和要滚动的距离,然后它就会告诉你位置随时间变化的值。其实这是一个中学物理题,也即给定初始位置,给定要滚动的距离,以一定的方式来计算每个时间点的位置。具体的计算方式由mInterpolater成员来控制,默认是ViscousFluid,是按自然指数为减速度来计算的,具体的可以查看Scroller的源码。如果不喜欢默认的计算方式,可以自己实现个Interpolator,然后在构造时传进去。

Scroller的作用在于实现平稳滑动,不让View的滚动出现跳跃,比如滑动一下ListView,开始滑动时的位置是x0,y0(ActionDown的位置),要向下滑动比如500个像素,不平稳的意思是,从x0,一下跳到x0+500的位置。要平稳,就要不断的一点点的改变x的值然后invalidate,这也就是Scroller的典型使用场景:

Scroller scroller = new Scroller(getContext());
scroller.startScroll(x0, y0, 500, 0);

然后在computeScroll时:

if (scroller.computeScrollOffset()) {
   int currX = scroller.getCurrX();
   int currY = scroller.getCurrY();
   invalidate(); // with currX and currY
}

computeScrollOffset在滚动没结束时返回true,也就是说你需要继续刷新view。返回false时表明滚动结束了,当然也就没有必要再刷新view(当然如果你乐意也可以继续刷,但是位置啥的都不变了,所以刷了也白刷)。

滑动冲突处理

关于View的滑动,最难搞的问题便是手势冲突处理,特别是当页面的结构变得复杂了以后。一般来讲,滑动手势,是让某一个View沿着某一个方向『平移』一段距离,如果某一个页面中只有一个View是可以滑动的,或者页面中不同的View的可滑动方向是垂直正交的,那么就不会有冲突的问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmf47f23-1688998312494)(https://ts1.cn.mm.bing.net/th/id/R-C.0b4a444cd44ddeebce726777cdddf2f9?rik=Wrup7%2bYTmIZibA&riu=http%3a%2f%2funitid.nl%2fandroidpatterns%2fwp-content%2fuploads%2fscrollthumb.png&ehk=hHYe%2byE4JOmZrc8S4vLHliUmVEJ%2f6uI3oeHeg8s0dMM%3d&risl=&pid=ImgRaw&r=0)]

所谓滑动冲突,是指父View和子View都接受滑动手势,并且方向又是一样的,这时就产生了滑动冲突,常见就是ScrollView中套着ListView(这个通常是垂直Y方向上面有滑动冲突),或者ViewPager中套着ScrollView(这个是水平X方向上有滑动冲突)。

要想解决好滑动冲突问题,需要先确实好整体的设计方案,有了大的原则后,就容易用技术方案找到解法。最理想的方案,也是目前用的最多的方案就是在子View的边界设定一个margin区域,当ACTION_DOWN在margin区域以外,认定滑动手势归父View处理,否则交由子View处理。像一些全局手势也是要用如此的方案,当点击距离屏幕一定范围内(margin区域)认定此事件归当前页面处理,否则就认定为全局手势,就好比从屏幕左边向右滑动,很多应该将此识别为BACK到上一页,但如果离左边较远时滑动,就会是页面内部的滑动事件(假如它有可滑动的组件的话,事件手势会被其滑消耗掉)。

参考资料

  • Detect common gestures
  • Flyweight pattern
  • Design Patterns - Flyweight Pattern
  • Animate a scroll gesture
  • Android Scroller simple example

原创不易,打赏点赞在看收藏分享 总要有一个吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/739168.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python:使用 np.lib.stride_tricks.sliding_window_view 将立方体切割成N个三维小块

函数说明:np.lib.stride_tricks.sliding_window_view(arr, window_shape) 参数说明: arr:要创建滑动窗口视图的数组。 window_shape:滑动窗口的形状,表示切割出的小块的大小。 作用:用于创建滑动窗口视图。…

软件设计模式与体系结构-软件体系-基于事件的软件体系结构

目录 三、基于事件的软件体系结构代码显式调用隐式调用事件系统软件体系结构的概念事件系统的连接机制 例子:图形用户界面事件系统调度策略1.带有分离的派遣模块的事件管理器 观察者模式类图观察者模式应用实例 课程作业 三、基于事件的软件体系结构 计算机中&…

Prototype Completion for Few-Shot Learning

小样本学习的目的是用很少的例子来识别新类。基于预训练的方法有效地解决了通过预训练一个特征提取器,然后通过最近的基于质心的元学习对其进行微调 (pretraining fine-tuning)。然而,结果表明微调步骤使边际改进。在本文中,1)我们找出原因,…

Kafka入门,漏消费和重复消费, 消费者事务,数据积压(二十四)

漏消费和重复消费 重复消费:已经消费了数据,但是offset没提交。 漏消费:先提交offset后消费,有可能会造成数据得漏消费 消费者事务 如果向完成consumer端得进准一次性消费,那么需要Kafka消费端将消费过程和提交offs…

Gradio库中的State模块:保存用户会话状态的神秘组件

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

Python Flask构建微信小程序订餐系统 (六)

🔥 账号管理 🔥 展示账户列表 默认情况下的账户列表布局 查询用户信息 查询 所有用户信息 按照 倒序 的方式查询出来 User.query.order_by( User.uid.desc() ).all() ......@route_account.route("/index") def index():#模版文件夹取名叫 "account/login…

【netty】Netty模型

工作原理 1)Netty抽象出两组线程池 BossGroup 专门负责接收客户端的连接, WorkerGroup 专门负责网络的读写; 2)BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup 3)NioEventLoopGroup 相当于一个事件循环组, 这个组中含有多…

RabbitMQ系列(17)--延迟队列的简介与实现

1、延迟队列的概念 延迟队列内部是有序的,重要的特性体现在它的延迟属性上,延迟队列中的元素希望在指定时间到了之后或之前取出处理,简单的说延迟队列就是用来存放需要在指定时间被处理的元素的队列。 2、延迟队列的应用场景 (1)订单指定时…

Python实现本地电脑启动HTTP服务

在Python中,可以使用Python内置的http.server模块来启动一个简单的HTTP服务器。以下是一个简单的Python代码示例,实现本地电脑启动HTTP服务: import http.server import socketserverport 8081# 在当前目录下启动http服务器 Handler http.…

JavaWeb 速通JavaScript

目录 一、JavaScript快速入门 1.基本介绍 : 2.JavaScript特点 : 3.JavaScript使用方式 : 1 方式一 : 写在 2 方式二 : 以外部文件形式引入 PS : 注意事项 4.JavaScript查错方式 : 二、JavaScript数据类型 1.变量 : 2.数据类型 : 3.特殊值 : 三、JavaScript运算符 1.算…

解决forest低版本请求不安全的网站出现SSL认证不通过问题

文章目录 前言解决问题的步骤1、当然是百度2、官网3、看源码4、GPT5、直接去gitee上看源代码的问题 解决一解决二 前言 先说结论:无法解决 那既然无法解决,为啥还要写这样一篇文章呢,是因为这个问题我弄了一天多,我觉得有必要记…

SpringBoot开启子线程执行任务

目录 一、EnableAsync 二、Async 三、测试 一、EnableAsync 二、Async Service public class IotLocationServiceImpl {Asyncpublic void testA() {try {// 模拟阻塞Thread.sleep(5000);System.out.println("子线程执行完毕");} catch (InterruptedException e) {…

WPS Office AI实战:智能表格化身智能助理

前面我们已经拿 WPS AI 对Word文字、PPT幻灯片、PDF 做了开箱体验,还没有看过的小伙伴,请翻看以前的文章,本文开始对【智能表格】进行AI开箱测验。 表格在日常的数据处理中占绝对地位,但表格处理并不是每一个人都擅长,…

《向量数据库指南》——Milvus 中的向量索引概览和平面索引

目录 Milvus 中的向量索引 索引概览 平面索引 在之前的教程中,我们简单介绍了单词 Embedding 示例,了解了 Embedding 的强大,以及如何在向量数据库中进行向量存储和索引。此外,我们也简单介绍了最近邻搜索算法,这个问题涉及根据所选距离度量找到距离查询向量最接近的向…

spring boot+MySQL智慧食堂”设计与实现(包论文)

“智慧食堂”系统运用springboot框架开发,MIS的总体思想,以及MYSQL等技术的支持下共同完成了该系统的开发,实现了“智慧食堂”系统管理的信息化,使用户体验到更优秀的“智慧食堂”管理系统,管理员管理操作将更加方便&a…

青岛大学_王卓老师【数据结构与算法】Week05_01_栈和队列的定义和特点1_学习笔记

本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享, 另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权,请留言作删文处理。 课程视频链接: 数据结构与算法基础…

常用排序,快速排序,归并排序算法讲解

文章目录 快速排序归并排序 排序有很多种算法,常听的十大排序有:冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序、希尔排序、计数排序、基数排序、桶排序。 这里只介绍两个常用的算法。 排序: 快速排序归并排序 你可能想知道…

python安装cartopy

1.安装cartopy 创建新环境:https://www.machinelearningplus.com/deployment/conda-create-environment-and-everything-you-need-to-know-to-manage-conda-virtual-environment/ conda create --name mlenv python3.7.5 激活环境后,安装cartopy包&#…

IDEA+springboot+mybatis+shiro+bootstrap+Mysql网上书店管理系统源码

IDEAspringbootmybatisshirobootstrapMysql网上书店管理系统 一、系统介绍1.环境配置 二、系统展示1. 首页2.图书详情3.登录4. 注册5. 购物车6. 个人信息7.我的订单8.填写订单9.用户管理10.添加用户11.店铺列表12.添加店铺13.角色列表14.添加角色15.权限管理16.店铺信息17.我的…

十、SkyWalking链路追踪

解决什么问题? 串联整个调用链路,快速定位问题。缕清服务之间的依赖关系。对各个微服务接口的进行性能分析。跟踪整个业务流程的调用处理顺序。 一、skywalking介绍 1、skywalking是什么? skywalking是一个国产开源框架,2015年…