创建自定义控件3-可交互性

2024-03-31 08:48

本文主要是介绍创建自定义控件3-可交互性,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

绘制UI仅仅是创建自定义视图的一部分。除此之外,你还需要以一种非常接近现实世界行为的方式来响应用户的输入。所有的对象都应该表现得像现实世界的对象那样。例如,图片不应该突然消失后又出现在别的地方,因为现实中的物体是不会那样的。合乎情理的做法是它应该从一个位置移动到另一个位置。 用户要么体验到UI中精细的操作,要么仅仅感觉到只是界面,相比较而言前者模拟的现实世界会获得最好的用户体验。例如,当用户在操作UI的时候,他们一旦感到界面的滑动响应延迟,将会感到烦躁而疯狂的滑动屏幕。

这篇文章展示了如何结合Android框架的特点来增添现实世界的行为到你所定制的视图中。

处理输入手势-Handle Input Gestures


跟许多其它的UI框架一样,Android也支持输入事件模型。用户的操作会转变成触发回调函数的事件,你可以重写这个回调函数来定制你的应用如何响应用户。在Android系统中最常用的输入事件是触摸事件,它会触发onTouchEvent(android.view.MotionEvent),重写该方法可以处理该事件:

1
2
3
4
@Override   
public boolean onTouchEvent(MotionEvent event) {    
return super.onTouchEvent(event);  
}

触摸事件本身并不是特别的有用。现在的可触摸UI可以接受许多种手势动作的交互,如 轻拍、拉、推、摇动和缩放。Android系统提供了GestureDetector类,将基本的触摸事件转变成各种不同的手势动作。 通过实现GestureDetector.OnGestureListener接口来构造一个GestureDetector的一个实例。如果你仅仅只是想处理少部分的手势动作,你可以选择继承GestureDetector.SimpleOnGestureListener而不用去实现GestureDetector.OnGestureListener接口。例如,下面的代码就是创建了一个继承GestureDetector.SimpleOnGestureListener的类并重写了onDown(MotionEvent)方法。

1
2
3
4
5
6
7
class mListener extends GestureDetector.SimpleOnGestureListener {   
@Override   
public boolean onDown(MotionEvent e) {       return true;   
}
}
mDetector = new GestureDetector(PieChart.this.getContext(),new mListener());

不管你是否使用了GestureDetector.SimpleOnGestureListener类,你都必须要实现onDown()方法并返回true。这一步是必须的因为所有的触摸操作都从onDown() 方法中的事件开始。如果你在onDown()方法中返回false,就像GestureDetector.SimpleOnGestureListener所设计的那样,系统会认为你不需要其它的手势操作,那么GestureDetector.OnGestureListener接口的其余方法都不会被调用。唯一能够将onDown()方法返回false的情况就是你真的想忽略所有的手势操作。一旦你实现了GestureDetector.OnGestureListener接口并且创造了GestureDetector类的一个实例,你就可以用GestureDetector类来完成你在 onTouchEvent()方法中接收的触屏事件。

 123456789
10
11
@Override
public boolean onTouchEvent(MotionEvent event) {   
boolean result = mDetector.onTouchEvent(event);   
if (result) {       
if(event.getAction()==MotionEvent.ACTION_UP){           
stopScrolling();           
result = true;       
}   
}   return result;
}

当你想执行onTouchEvent()方法中的触摸事件而并不想把它当作手势操作的一部分,那就要返回false。你可以执行你自定义的检测手势操作的代码。

创建让人感觉合理的操作-Create Physically Plausible Motion

手势动作是控制触摸屏设备的一种很强有力的方式,但是它会违背人的直觉而让人难以记住,除非它让人得到合理的结果。滑动手指就是一个很好的例子,用户用一根手指快速的在屏幕上滑动然后抬起。只有在UI的响应速度跟得上用户快速的滑动屏幕的频率的时候,这个手势操作才是有意义的,就像用户在推动一个飞轮并想让它旋转起来。

然而,模拟一种飞轮的感觉也是不平凡的。要想得到一个正确的飞轮运转模型是需要大量的物理和数学验证的。庆幸的是,Android提供了帮助类来模拟它以及别的操作行为。Scroller 类是处理具有飞轮风格手势操作的基类。 开始实现滑动操作时,要调用fling()并传入初始的速率和在X轴和Y轴上滑动的最小值和最大值。根据这个速率的值,你就可以用该值并通过GestureDetector类来为你计算。

1
2
3
4
5
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {   
mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);   
postInvalidate();
}

提示: 尽管通过GestureDetector类计算出的速率在理论上是精确的,但是许多开发人员都感到用这个值会让滑动的动画太快。常用的办法就是将X和Y方向的速率除以4到8之间的某一个值。

调用fling()方法会建立滑动操作的物理模型。接下来,你需要适时的调用 Scroller.computeScrollOffset() 方法来更新Scroller类的数据。computeScrollOffset() 方法通过读取当前的时间并用物理模型计算当前的X坐标和Y坐标来更新Scroller对象的内部状态调用getCurrX() 和getCurrY()方法可以获取最新的X坐标和Y坐标。 大部分的视图通过直接调用scrollTo()方法改变Scroller对象的X坐标和Y坐标的值。但是图表是有一点不一样的,它用当前Y轴的坐标来设定图表的张开角度。

1
2
3
4
if (mScroller.isFinished()) {mScroller.computeScrollOffset();setPieRotation(mScroller.getCurrY());
}

Scroller类能为你计算出滚动轴的位置,但是它不能自动的为你的视图提供它们的坐标位置。你必须确保你获得并应用的坐标在正常情况下足以能让滚动动画看起来流畅。这里有两种方法可以实现:

    • 为了能重绘视图,在调用fling()之后要调用postInvalidate()。这种技术需要你每次在补偿滚动轴的变化时要在onDraw()方法中计算滚动轴的变化并调用postInvalidate()方法。
    • 建立一个ValueAnimator来动态模拟滑动动作持续的状态,然后调用addUpdateListener()来添加一个监听者进行动画的更新。

图表用的就是第二种方法。这种方法在创建的时候略微会显得更加复杂,但是它工作起来会跟动画系统配合得更密切而且不需要可能不必要的无效视图。它的缺憾就是在低于API11的版本上ValueAnimator是不可用的,所以这种方法是不能用在低于Android3.0系统的设备上的。

提示:ValueAnimator虽然在低于API11的版本不可用,但是你仍然可以在你的应用程序中使用这个类并标上API版本过低。在程序运行的时候你必须确保检查了当前的API版本,如果当前的API版本低于11,在视图动画系统中就要忽略这些调用。

 123456789
10
11
12
13
14
mScroller = new Scroller(getContext(), null, true);
mScrollAnimator = ValueAnimator.ofFloat(0,1);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {           
@Override           
public void onAnimationUpdate(ValueAnimator valueAnimator) {               
if(mScroller.isFinished()){                   
mScroller.computeScrollOffset();
setPieRotation(mScroller.getCurrY());               
} else {                   
mScrollAnimator.cancel();                   
onScrollFinished();               
}           
}       
});

让你的界面流畅地转换

用户期望现代的UI能流畅的在状态之间转换。UI元素的淡入淡出会取代直接的出现和消失。各种操作都会按照自然的方式开始和结束而不是突然的启动和停止。在Android3.0系统中引入的property animation framework框架,会让流畅的转换变得简单。 在使用动画系统时,每一个视图属性的改变都将会影响到视图的外观,所以不要直接去改变视图的属性。取而代之的是,你可以用ValueAnimator来实现你对视图属性的改变。在下面的例子中,修改当前在图表中选定的饼图饼片会导致整个图表旋转,以便让你选择的点集中在选定的切片上。ValueAnimator 会在一个短暂的时间内实现旋转的改变,而不是突然地改变旋转的状态。

1
2
3
4
mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this,"PieRotation",0); 
mAutoCenterAnimator.setIntValues(targetAngle);
mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
mAutoCenterAnimator.start();

如果你想改变基本的View 中的一个属性值,不如去改变视图动画,因为视图中有一个嵌入的ViewPropertyAnimator类,它对同步的多属性的动画做了优化。例如:

1
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();

这篇关于创建自定义控件3-可交互性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863925

相关文章

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

MySQL 临时表创建与使用详细说明

《MySQL临时表创建与使用详细说明》MySQL临时表是存储在内存或磁盘的临时数据表,会话结束时自动销毁,适合存储中间计算结果或临时数据集,其名称以#开头(如#TempTable),本文给大家介绍M... 目录mysql 临时表详细说明1.定义2.核心特性3.创建与使用4.典型应用场景5.生命周期管理6.注

MySQL的触发器全解析(创建、查看触发器)

《MySQL的触发器全解析(创建、查看触发器)》MySQL触发器是与表关联的存储程序,当INSERT/UPDATE/DELETE事件发生时自动执行,用于维护数据一致性、日志记录和校验,优点包括自动执行... 目录触发器的概念:创建触www.chinasem.cn发器:查看触发器:查看当前数据库的所有触发器的定

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (