Android:将自定义视图设为互动式

news2024/9/21 19:05:08

一、简介

点击查看将自定义视图设为互动式官网文档

绘制界面只是创建自定义视图的一个部分。您还需要让视图以非常类似于您模仿的真实操作的方式响应用户输入。

让应用中的对象的行为方式与真实对象相似。例如,不要让应用中的图片消失后重新出现在其他位置,因为现实世界中的对象不会这样做。而是应该将图片从一个位置移动到另一个位置。

用户可以感受到界面中的细微行为或感觉,并对模仿现实世界的细微差别做出最佳反应。例如,当用户快滑界面对象时,在开始时为用户提供一种惯性感,以延迟移动。在动作结束时,让他们感受到使物体超出快滑范围的动量。

本页演示了如何使用 Android 框架的功能将这些真实行为添加到您的自定义视图中。

如需了解其他相关信息,请参阅输入事件概览和属性动画概览。

二、处理输入手势

像许多其他界面框架一样,Android 支持输入事件模型。用户操作会转换为触发回调的事件,您可以替换回调以自定义应用对用户的响应方式。Android 系统中最常见的输入事件是“轻触”,会触发 onTouchEvent(android.view.MotionEvent)。您可以重写此方法来处理事件,如下所示:

override fun onTouchEvent(event: MotionEvent): Boolean {
    return super.onTouchEvent(event)
}

触摸事件本身并不是特别有用。现代触控界面根据手势定义互动,例如点按、拉、推、快滑和缩放。为了将原始轻触事件转换为手势,Android 提供了 GestureDetector。

通过传入实现 GestureDetector.OnGestureListener 的类的实例来构建 GestureDetector。如果您只想处理几个手势,可以扩展 GestureDetector.SimpleOnGestureListener,而不是实现 GestureDetector.OnGestureListener 接口。例如,以下代码会创建一个扩展 GestureDetector.SimpleOnGestureListener 并替换 onDown(MotionEvent) 的类。

private val myListener =  object : GestureDetector.SimpleOnGestureListener() {
    override fun onDown(e: MotionEvent): Boolean {
        return true
    }
}

private val detector: GestureDetector = GestureDetector(context, myListener)

无论您是否使用 GestureDetector.SimpleOnGestureListener,请始终实现返回 true 的 onDown() 方法。这是必要的,因为所有手势都以 onDown() 消息开头。如果您从 onDown() 返回 false(就像 GestureDetector.SimpleOnGestureListener 一样),系统会假设您想要忽略其余手势,并且不会调用 GestureDetector.OnGestureListener 的其他方法。仅当您想要忽略整个手势时,才从 onDown() 返回 false。

实现 GestureDetector.OnGestureListener 并创建 GestureDetector 的实例后,您可以使用 GestureDetector 解读在 onTouchEvent() 中收到的触摸事件。

override fun onTouchEvent(event: MotionEvent): Boolean {
    return detector.onTouchEvent(event).let { result ->
        if (!result) {
            if (event.action == MotionEvent.ACTION_UP) {
                stopScrolling()
                true
            } else false
        } else true
    }
}

当您向 onTouchEvent() 传递无法被识别为手势一部分的触摸事件时,它会返回 false。然后,您可以运行自己的自定义手势检测代码。

三、创建物理上合理的动作

手势是控制触摸屏设备的一种强大方式,但它们可能违背常理且难以记住,除非它们产生物理上合理的结果。

例如,假设您想要实现一个水平快速滑动手势,用于设置在视图中绘制的项目围绕其垂直轴旋转。如果界面的响应方式是沿快滑方向快速移动,然后放慢速度,就好像用户推动飞轮并使其旋转一样,这种手势很合理。

有关如何为滚动手势添加动画效果的文档详细介绍了如何实现您自己的滚动行为。但模拟飞轮的感觉并非易事。要使飞轮模型正常工作,需要运用大量的物理知识和数学运算。幸运的是,Android 提供了辅助类来模拟此行为和其他行为。Scroller 类是处理飞轮式快速滑动手势的基础。

如需开始快滑,请调用 fling() 并传入初始速度以及快滑的最小和最大 x 值和最大 y 值。对于速度值,您可以使用由 GestureDetector 计算的值。

在这里插入图片描述

fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
    scroller.fling(
            currentX,
            currentY,
            (velocityX / SCALE).toInt(),
            (velocityY / SCALE).toInt(),
            minX,
            minY,
            maxX,
            maxY
    )
    postInvalidate()
    return true
}

注意: 虽然由 GestureDetector 计算的速度在物理上是准确的,但许多开发者认为使用此值会导致快滑动画速度过快。常见的做法是将 x 速度和 y 速度除以四到八的倍数。

调用 fling() 将设置快滑手势的物理模型。之后,通过定期调用 Scroller.computeScrollOffset() 来更新 Scroller。computeScrollOffset() 通过读取当前时间并使用物理模型计算当时的 x 和 y 位置,从而更新 Scroller 对象的内部状态。调用 getCurrX() 和 getCurrY() 可检索这些值。

大多数视图会将 Scroller 对象的 x 和 y 位置直接传递给 scrollTo()。此示例略有不同:它使用当前的滚动 x 位置来设置视图的旋转角度。

scroller.apply {
    if (!isFinished) {
        computeScrollOffset()
        setItemRotation(currX)
    }
}

Scroller 类会为您计算滚动位置,但不会自动将这些位置应用到视图。应经常应用新坐标,以保证滚动动画的流畅性。您可以采用下列两种方法:

  • 调用 fling() 后调用 postInvalidate(),以强制重新绘制。此方法要求您在 onDraw() 中计算滚动偏移,并在每次滚动偏移发生变化时调用 postInvalidate()。
  • 设置 ValueAnimator 以在快滑期间添加动画效果,并通过调用 addUpdateListener() 添加监听器以处理动画更新。 通过此方法,您可以为 View 的属性添加动画效果。

四、让转场更顺畅

用户希望现代界面能够在状态之间流畅过渡:界面元素应淡入和淡出,而不是出现和消失;动作开始和结束平稳,而不是突然开始和停止。Android 属性动画框架可以更轻松地实现平滑过渡。

如需使用动画系统,每当属性更改会影响视图外观时,请勿直接更改该属性,请改用 ValueAnimator 进行更改。在以下示例中,修改视图中的选定子组件会使整个渲染视图旋转,使选择指针居中。ValueAnimator 会用几百毫秒的时间更改旋转,而不是立即设置新的旋转值。

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply {
    setIntValues(targetAngle)
    duration = AUTOCENTER_ANIM_DURATION
    start()
}

如果您要更改的值是基本 View 属性之一,则可以更轻松地执行动画,因为视图具有针对多个属性同时播放动画进行了优化的内置 ViewPropertyAnimator,如以下示例所示:

animate()
    .rotation(targetAngle)
    .duration = ANIM_DURATION
    .start()

五、优化自定义视图

如果您有一个精心设计的视图可以响应手势和状态之间的转换,请确保该视图快速运行。为避免播放过程中界面响应缓慢或卡顿,请确保动画始终以每秒 60 帧的速度运行。

5.1 加快观看速度

为了提高视图的运行速度,可从频繁调用的例程中剔除不必要的代码。从 onDraw() 开始,这将为您带来最大的回报。特别是应消除 onDraw() 中的分配,因为分配可能会导致垃圾回收,从而造成卡顿。请在初始化期间或动画之间分配对象。切勿在动画运行期间进行分配。

除了精简 onDraw() 之外,还应确保尽可能降低调用它的频率。对 onDraw() 的大多数调用都是调用 invalidate() 的结果,因此可以避免不必要的 invalidate() 调用。

另一种成本非常高昂的操作是遍历布局。当视图调用 requestLayout() 时,Android 界面系统会遍历整个视图层次结构,以确定每个视图所需的大小。如果发现冲突的测量值,可能会多次遍历层次结构。界面设计人员有时会创建由嵌套的 ViewGroup 对象组成的深层次结构。这些深层视图层次结构会导致性能问题,因此应尽可能浅层视图。

如果您的界面比较复杂,不妨考虑编写自定义 ViewGroup 来执行其布局。与内置视图不同,自定义视图可以对其子项的尺寸和形状做出特定于应用的假设,从而避免遍历其子项以计算测量值。

例如,如果您有一个自定义 ViwGroup,它不通过调整自身大小来适应其所有子视图,就可以避免测量所有子视图所产生的开销。如果您使用适合各种用例的内置布局,则无法进行此优化。

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

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

相关文章

1.厦门面试

1.Vue的生命周期阶段 vue生命周期分为四个阶段 第一阶段(创建阶段):beforeCreate,created 第二阶段(挂载阶段):beforeMount(render),mounted 第三阶段&#…

基于Transformer模型的谣言检测系统的实现

新书速览|PyTorch深度学习与企业级项目实战-CSDN博客 谣言检测系统项目背景 1938年10月30日的晚上,哥伦比亚广播公司照例安排了广播剧,当晚的节目是根据HG威尔斯《世界之战》改编的“火星人进攻地球”。为提升吸引力,制作团队选择以类纪实风…

C# 智慧大棚nmodbus4

窗体 :图表(chart): 下载第三方: nmodbus4:可以实现串口直连,需要创建串口对象设置串口参数配置Serialport 如果需要把串口数据表通过tcp进行网口传递 需要创建tcpclient对象 ModbusSerialMaster master; /…

秋招突击——7/17——复习{二分查找——搜索插入位置、搜索二维矩阵,}——新作{链表——反转链表和回文链表,子串——和为K的子数组}

文章目录 引言新作二分模板二分查找——搜索插入位置复习实现 搜索二维矩阵复习实现 新作反转链表个人实现参考实现 回文链表个人实现参考实现 和为K的子数组个人实现参考实现 总结 引言 今天算法得是速通的,严格把控好时间,后面要准备去面试提前批了&a…

Camera Raw:首选项

Camera Raw 首选项 Preferences提供了丰富的配置选项,通过合理设置,可以显著提升图像处理的效率和效果。根据个人需求调整这些选项,有助于创建理想的工作环境和输出质量。 ◆ ◆ ◆ 打开 Camera Raw 首选项 方法一:在 Adobe Bri…

nginx 编译安装与配置

一、安装 官网下载合适的版本,建议选择稳定版本。 官网地址:https://nginx.org wget https://nginx.org/download/nginx-1.26.1.tar.gz -C /opt/ 解压后,进入源码目录 cd /opt/nginx-1.26.1tar -zxvf nginx-1.20.1.tar.gz cd nginx-1.26.1源…

react基础样式控制

行内样式 <div style{{width:500px, height:300px,background:#ccc,margin:200px auto}}>文本</div> class类名 注意&#xff1a;在react中使用class类名必须使用className 在外部src下新建index.css文件写入你的样式 .fontcolor{color:red } 在用到的页面引入…

睿考网:2024注册会计师考试考试在即,如何备考?

2024年注册会计师考试即将开始&#xff0c;准考证打印时间安排在8月5日至20日&#xff0c;每天上午8点至晚上8点&#xff0c;考生要确保在规定时间内完成准考证的打印。 注册会计师考试包含六个科目&#xff0c;每个科目都有其独特的特点和难度。考生需要根据各科目的特性采用…

vue3使用弹幕插件

1.安装插件 $ npm install vue3-danmaku --save 2.用法 <template><vue-danmaku v-model:danmus"danmus" loop style"height:100px; width:300px;"></vue-danmaku> </template><script setup> import vueDanmaku from vu…

Docker构建LNMP环境并运行Wordpress平台

1.准备Nginx 上传文件 Dockerfile FROM centos:7 as firstADD nginx-1.24.0.tar.gz /opt/ COPY CentOS-Base.repo /etc/yum.repos.d/RUN yum -y install pcre-devel zlib-devel openssl-devel gcc gcc-c make && \useradd -M -s /sbin/nologin nginx && \cd /o…

数据分析01——系统认识数据分析

1.数据分析的全貌 1.1观测 1.1.1 观察 &#xff08;1&#xff09;采集数据 a.采集数据&#xff1a;解析系统日志 当你在看视频的时候———就会产生日志———解析日志———得到数据 b.采集数据&#xff1a;埋点获取新数据&#xff08;自定义记录新的信息&#xff09; 日志…

redis之resp界面连接

解压资源 连接成功

目标检测入门:4.目标检测中的一阶段模型和两阶段模型

在前面几章里&#xff0c;都只做了目标检测中的目标定位任务&#xff0c;并未做目标分类任务。目标检测作为计算机视觉领域的核心人物之一&#xff0c;旨在从图像中识别出所有感兴趣的目标&#xff0c;并确定它们的类别和位置。现在目标检测以一阶段模型和两阶段模型为代表的。…

【spring boot】初学者项目快速练手

一小时带你从0到1实现一个SpringBoot项目开发_哔哩哔哩_bilibili 一、基础知识 1.注解 二、简介 三、项目结构 四、代码结构 1.生成Spring Boot项目的主程序 &#xff08;1&#xff09;在官网下载 Spring Initializr 快速生成一个初始的项目代码&#xff0c;会生成一个de…

Java二十三种设计模式-抽象工厂模式(3/23)

抽象工厂模式&#xff1a;复杂系统的灵活构建者 引言 在软件开发中&#xff0c;抽象工厂模式是一种提供接口以创建相关或依赖对象族的创建型设计模式。这种模式允许客户端使用一个共同的接口来创建不同的产品族&#xff0c;而无需指定具体类。 基础知识&#xff0c;java设计模…

SSE(Server Sent Event)实战(3)- Spring Web Flux 实现

上篇博客 SSE&#xff08;Server Sent Event&#xff09;实战&#xff08;2&#xff09;- Spring MVC 实现&#xff0c;我们用 Spring MVC 实现了简单的消息推送&#xff0c;并且留下了两个问题&#xff0c;这篇博客&#xff0c;我们用 Spring Web Flux 实现&#xff0c;并且看…

MySQL条件查询(DQL)

在此之前先给大家看一下我的表里面的数据&#xff0c;以方便接下来的讲解 还需要大家先熟悉这里面的条件 1.语法 SELECT 字段列表 FROM 表名 WHERE 条件列表 例如 1.查询年龄等于20的员工 select * from emp where age 20; 2.查询年龄小于等于20的员工信息 select * fr…

PPOCRLabel安装及使用

一、环境准备 1、 使用anaconda创建一个Python3.7.x的环境 # 在命令行输入以下命令&#xff0c;创建名为paddle_env的环境 conda create --name paddle_env python3.7 --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/输入以下命令可以查看我们所创建的所…

【作业】 贪心算法1

Tips:三题尚未完成。 #include <iostream> #include <algorithm> using namespace std; int a[110]; int main(){int n,r,sum0;cin>>n>>r;for(int i0;i<n;i){cin>>a[i];}sort(a0,an);for(int i0;i<n;i){if(i>r){a[i]a[i-r]a[i];}suma[…

借助Aspose.Note笔记工具,在Java 中更改 OneNote 中的样式

Microsoft OneNote是一款功能强大的笔记工具。学习如何使用 Java 更改样式&#xff0c;将您的 OneNote 体验提升到一个新的水平&#xff01;在本指南中&#xff0c;我们将向您展示如何使用 Java 更改 OneNote 文档中的样式以增强可读性。本指南将为您提供分步说明&#xff0c;以…