探索安卓中有意义的动画!
ribot 致力于打造美好且充满意义的用户体验,在这一过程中,动画不可或缺。
在 Droidcon London 听完一场 激励人心的演讲之后, 笔者决定深入研究安卓动画。本文集中展示了其研究结果,希望使开发者和设计者们意识到,为 Android 应用添加漂亮的动画并不复杂。
动画!
如果你想尝试这些动画效果,本文所有实例都能在 Github 上的这款 Android 应用 中找到。
笔者非常喜欢动画效果,因为它不仅提高用户参与度,还能迅速夺人眼球。想想那些以动画设计著称的应用,它们使用起来是多么可心、流畅、自然。
Falcon Pro:即使细微的动画效果也可以对用户体验产生巨大影响。
现在,与那些你很喜欢但没有动画的应用做一番比较。
Medium: 尽管笔者很喜爱 medium APP,但它的确缺少恰当的动画。
小动作也能造就大不同
我们可以从多个方面利用动画,从而:
- 通过导航上下文传输用户;
- 强化元素的层级结构;
- 展示屏幕显示的组件变化。
本文旨在说明,在应用中实现有意义的动画十分简单可行——那么,即刻开始吧。
触觉反馈
在用户触摸屏幕时提供反馈,有助于视觉交流,形成互动。这些动画不应分散用户的注意力,但又使他们享受其中,获得清晰的视感,从而鼓励进一步操作。
安卓框架为此类反馈提供了波纹效果,通过设定视图背景,即可使用:
?android:attr/selectableItemBackground-在视图范围内展示波纹效果;
波纹在接触点开始,之后填充整个视图背景。
?android:attr/selectableItemBackgroundBorderless –将波纹效果延伸至视图之外。
圆形波纹效果在接触点开始,并沿半径延伸至视图之外。
View Property Animator
ViewPropertyAnimator 在 API 12 首次引入,允许我们只使用一个Animator实例,就可以简单高效地使多个视图属性(并行地)执行动画操作。
此处将绘制下文提到的所有动画属性。
- alpha() –设定动画的 alpha 值;
- scaleX() 与 scaleY() – 将视图缩放于 X 和/或 Y 坐标轴;
- translationZ()-在 Z 轴上平移视图;
- setDuration() –设置动画时长;
- setStartDelay() –设置动画延时;
- setInterpolator() – 设置动画插值;
- setListener() – 为动画的开始、结束、重复、取消设置侦听器。
注意: 如果已在视图中设置了侦听器,并打算在相同视图下,实现其他动画且不使用回调函数,则需要将侦听器设为 null。
用程序实现时,简单又整洁:
mButton.animate().alpha(1f).scaleX(1f).scaleY(1f).translationZ(10f).setInterpolator(new FastOutSlowInInterpolator()).setStartDelay(200).setListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) { }@Overridepublic void onAnimationEnd(Animator animation) { }@Overridepublic void onAnimationCancel(Animator animation) { }@Overridepublic void onAnimationRepeat(Animator animation) { }}).start();
注意: 其实我们不需要在动画生成器中调用 start( ) 方法,因为在停止声明的同时,动画就会自动启动。在这种情况下,只有在 UI toolkit 事件队列开始下一次更新时,动画才会再开始。
制作 FAB 的 alpha 动画值
FAB 的(X 和 Y 轴)坐标动画
FAB 的 Z 坐标动画
注意: 考虑到向后兼容性,你可以使用ViewCompat 类,来实现在安卓 API 4 以及以上版本的ViewPropertyAnimator 类。
Object Animator
和 ViewPropertyAnimator 类似,ObjectAnimator 允许我们在目标视图(代码和 XML 源文件中)的不同属性中执行动画。然而,它们还是有些差异的:
- 在每个实例中,ObjectAnimator 只允许对单一属性执行动画。例如,坐标 Y 随坐标 X 变化;
- 但是,它允许自定义属性的动画,例如视图的前景色。
使用自定义属性给视图做缩放动画,并改变其前景色,可以达成下图的效果:
使用自定义属性时,可以通过调用ObjectAnimator.ofInt(),创建一个ObjectAnimator实例,此处我们声明:
- view – 应用动画的视图;
- property – 设定动画的属性;
- start color – 动画视图的初始颜色;
- target color – 动画视图的目标颜色。
接下来,设置评估器(此处使用ArgbEvaluator 设置颜色动画),设置延迟并执行 start( )。
private void animateForegroundColor(@ColorInt final int targetColor) {ObjectAnimator animator = ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor);animator.setEvaluator(new ArgbEvaluator());animator.setStartDelay(DELAY_COLOR_CHANGE);animator.start();
}
接下来,使用相似的方法做视图缩放的动画,主要区别在于:
- 使用ObjectAnimator.ofFloat() 创建 ObjectAnimator 实例,因为在调整视图大小时,并没有改动整型值;
- 使用 View.SCALE_X 和 View.SCALE_Y 视图属性,而非自定义属性。
private void resizeView() {final float widthHeightRatio = (float) getHeight() / (float) getWidth();resizeViewProperty(View.SCALE_X, .5f, 200);resizeViewProperty(View.SCALE_Y, .5f / widthHeightRatio, 250);}private void resizeViewProperty(Property<View, Float> property,float targetScale, int durationOffset) {ObjectAnimator animator = ObjectAnimator.ofFloat(this, property, 1f, targetScale);animator.setInterpolator(new LinearOutSlowInInterpolator());animator.setStartDelay(DELAY_COLOR_CHANGE + durationOffset);animator.start();}
最后,将调整完大小的视图移开屏幕。在这种情况下,使用AdapterViewFlipper 容纳离屏视图,可以对 ViewFlipper 实例调用showNext()方法,后者会使用(定义好的动画处理该过程。接着,下一个视图也会使用定义好的入场动画,自动出现在屏幕上。
Interpolators
Interpolator 可用于定义动画的变化率,意味着动画的速度、加速度、行为都可以改变。可用的 interpolator 有数种,且相互之间的差别微乎其微,建议读者在设备上一探究竟。
- No Interpolator -该视图动画没有变化率;
- Fast-Out Linear-In
该视图以线型动作开始和结束动画。
- Fast-Out Slow-In
该视图开始动作很快,逐渐降速直至结束。
- Linear-Out Slow-In
该视图以线型动作开始,逐渐降速直至结束。
- Accelerate-Decelerate
该视图在动画开始时加速,并在接近结束时逐渐减速。
- Accelerate –视图逐渐加速直到动画结束;
- Decelerate –视图逐渐减速直到动画结束;
- Anticipate –在以标准方式开始动画之前,视图先进行轻微反转;
- Anticipate-Overshoot –与 Anticipate 类似,但动画过程中,回拉动作更为夸张;
- BounceInterpolator – 视图动画结束之前会有‘反弹’效果;
- LinearInterpolator – 视图以线型平滑的动画开始,直到结束;
- OvershootInterpolator – 视图动画先放大给定值,再缩回原值。
Circular Reveal
CircularReveal 使用剪切的圆形显示或隐藏一组 UI 元素。该动画除了带来视觉上的连续性,还十分赏心悦目,有助于提高用户参与度。
如上图所示,在视图的动画效果显示之前,使用 ViewPropertyAnimator 隐藏浮动操作图标。只需定义如下属性就可以配置 circular reveal:
- startView – CircularReveal 的开始视图(即压缩视图);
- centerX –点击视图的 X轴中心;
- centerY -点击视图的 Y轴中心;
- targetView –要显示的视图;
- finalRadius –剪切圆的半径,大小等于以 X 中心和 Y 中心为直角边的三角形的斜边的值。
int centerX = (startView.getLeft() + startView.getRight()) / 2;
int centerY = (startView.getTop() + startView.getBottom()) / 2;
float finalRadius = (float) Math.hypot((double) centerX, (double) centerY);
Animator mCircularReveal = ViewAnimationUtils.createCircularReveal(targetView, centerX, centerY, 0, finalRadius);
窗口转换
定制用于活动间导航的转换,可使用户对应用状态产生更为强烈的视觉联系。默认情况可定制如下转换:
- enter –决定活动视图如何进入场景;
- exit -决定活动视图如何退出场景;
- reenter –决定活动视图退出后如何再度进入;
- shared elements –决定活动间如何共享视图转换。
自 API 21起,还有如下几种新的转换方式:
爆炸
Explode 转换允许视图从屏幕各个方位退出,会使压缩视图产生爆炸效果。
在网格布局中爆炸效果尤其好。
这种效果易于实现——首先,需要在 res/transition 目录中创建如下转换:
<explode xmlns:android="http://schemas.android.com/apk/res/android"android:duration="300"/>
具体做法如下:
- 声明 explode 转换;
- 设置持续时间为300毫秒。
接下来,需要将此设置为活动的转换。既可以将其添加到活动主题:
<style name="AppTheme.Explode" parent="AppTheme.NoActionBar"><item name="android:windowExitTransition">@transition/slide_explode</item><item name="android:windowReenterTransition">@android:transition/slide_top</item>
</style>
也可以编程的方式解决:
Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
getWindow().setEnterTransition(explode);
滑动
滑动切换可以使活动从屏幕右侧或底部滑入/出。可能你以前有过类似的效果,但是这个新切换更加灵活。
滑动切换使我们依次滑动子视图
这种转换在切换活动时尤为常见,笔者对向右侧滑的流畅感觉情有独钟,当然这也很容易创建:
<slide xmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:interpolator/decelerate_cubic"android:slideEdge="end"/>
在这里:
- 声明了 slide 转换;
- 设置切换的slideEdge为end(右侧),从而实现从右侧开始滑动——若想要底部滑动将设置为 bottom。
渐变
渐变切换使活动转换出现淡入或淡出的效果。
在视图中使用渐变动画操作简单,且效果宜人。
创建此切换的操作比之前的切换更加简单:
<fade xmlns:android="http://schemas.android.com/apk/res/android"android:duration="300"/>
在这里:
- 声明了 fade 转换;
- 设置持续时间为300毫秒。
优化转换
实验的同时,笔者发现了一些可以改善上述转换效果的方法。
允许窗口页面转换——需要在主题中启用下列属性,主题都来源于一个资料主题:
<item name="android:windowContentTransitions">true</item>
启用/禁用转换重叠——上一转换过程结束,新的页面动画才会开始,这样就会形成时延。在不同的案例中,若启用如下属性,转换过程都会更加流畅自然:
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
排除特定视图转换—有时我们并不想让活动中的所有视图参与动画,而且大多数情况下,工具栏和状态栏是造成转换故障主因。所幸,可以排除特定的视图,使之无法转换:
<explode xmlns:android="http://schemas.android.com/apk/res/android"android:duration="200"><targets><target android:excludeId="@android:id/navigationBarBackground"/><target android:excludeId="@android:id/statusBarBackground"/></targets></explode>
工具栏和操作栏——当使用操作栏的活动向使用工具栏的活动转换时(反之亦然),转换过程总是磕磕绊绊。为此,应当确保转换中的两个活动都使用相同的组件。
转换持续时间——既不能让用户等太久,也不能让动画转换过快。这取决于转换持续时间,最好通过试验敲定恰当的时间。笔者发现,多数情况下200-500微秒最为合适。
共享元素转换
共享元素转换方便我们为页面间的共享视图制作动画,使动画更为人性化,并给用户带来更好的视觉感受。
这里,第一个页面中的视图缩小并平移至第二个页面的标题图片位置。
在布局中,必须使用 transitionName 属性将所有共享视图联系起来——这表明了视图间的转换关系。下图展示了之前动画中的共享视图:
这些都是共享视图,意味着它们会在每次页面转换过程中形成动画。
为了完成如上转换,我们首先要声明共享转换名称,可以通过使用 XML 布局中的 transitionName 属性来完成。
屏幕 1)
<RelativeLayout><LinearLayout><View android:id="@+id/view_shared_transition"android:transitionName="@string/transition_view"/><!-- Your other views --></LinearLayout>
</RelativeLayout>
屏幕2)
<LinearLayout><Viewandroid:id="@+id/view_shared_transition"android:transitionName="@string/transition_view"/><Viewandroid:id="@+id/view_separator"/><TextViewandroid:id="@+id/text_detail"/><TextViewandroid:id="@+id/text_close"/></LinearLayout>
之后,在页面1中创建Pair对象,使之包含转换视图与其 transitionName。然后将其传给页面选择实例(ActivityOptionsCompat),由此两个页面都得知了共享组件,就可以开始动画了。
Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView));ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(SharedTransitionsActivity.this, participants);ActivityCompat.startActivity(SharedTransitionsActivity.this, intent, transitionActivityOptions.toBundle());
转换的同时滑动这些视图,有助于完成转换。
以上就是两个视图间的转换,那么在第二个页面中从底部滑入的视图怎么办呢?
(它们就是左边的那些视图)
其实这个实现过程也很简单,如下:
Slide slide = new Slide(Gravity.BOTTOM);
slide.addTarget(R.id.view_separator);
slide.addTarget(R.id.text_detail);
slide.addTarget(R.id.text_close);
getWindow().setEnterTransition(slide);
如你所见,创建一个新的Slide 转换:将目标视图添加到转换中,并将滑动动作设为入场动画。
自定义转换
我们也可以使用之前介绍过的 API 动画创建自己的自定义转换。例如,将共享元素转换衍伸,成为转换视图变体——当我们需要显示对话框(或者类似的弹框视图)时,自定义转换就会非常有用。具体如下所示:
该动画可以在组件状态间引导用户的注意力。
先来简单了解一下上图发生了什么:
- 首先创建一个SharedTransition,传入压缩视图与转换名称以引用共享组件。
- 然后创建ArcMotion 实例,使两个视图转换时形成曲线动画效果。
- 接下来扩展 ChangeBounds 以创建自定义转换,改变(morph)两个形状(对于button 和 FAB ,有两个不同的类)。此处重写了类中的多个方法,以便为所需属性做动画。最后,使用 ViewPropertyAnimator 调整对话框的透明度,使用 ObjectAnimator 调整两个视图间的色彩,使用 AnimatorSet 实例将两种动画效果整合在一起。
动态矢量图片
在 API 21中(Lollipop),AnimatedVectorDrawable 可用于制定VectorDrawable 属性的动画,生成动态图片。
在图片上做几种不同的动画并不容易。
那么如何完成呢,请看下图:
该图由几个不同文件组成,首先创建两个独立的矢量文件,每个都包含如下属性:
- Height & Width –矢量图像的实际大小;
- Viewport Height & Width –声明描述矢量路径的虚拟画布的大小;
- Group name –声明路径所属的组名;
- Pivot X & Y –声明群组规模和旋转所使用的中心点;
- Path Fill Color –描述矢量路径的填充色;
- Path Data –声明用于绘制矢量的矢量路径数据。
注意: 所有被引用的属性都存储在 general strings file 中,这样可以保持程序整洁美观。
<vector xmlns:android="http://schemas.android.com/apk/res/android"android:height="56dp" android:width="56dp"android:viewportHeight="24.0"android:viewportWidth="24.0"><groupandroid:name="@string/groupAddRemove"android:pivotX="12"android:pivotY="12"><path android:fillColor="@color/stroke_color"android:pathData="@string/path_add"/></group>
</vector>
该矢量由 ic_add.xml 文件(如下所示)生成
<vector xmlns:android="http://schemas.android.com/apk/res/android"android:height="56dp"android:width="56dp"android:viewportHeight="24.0"android:viewportWidth="24.0"><groupandroid:name="@string/groupAddRemove"android:pivotX="12"android:pivotY="12"><path android:fillColor="@color/stroke_color" android:pathData="@string/path_remove"/></group>
</vector>
该矢量由ic_remove.xml 文件(如下所示)生成
接下来声明 Animated Vector Drawable 文件,其中包含 Vector Drawable 和每个图片状态动画(Add 或 Remove)的声明。检查从 Add 到 Remove 的矢量动画,声明一个目标文件(target)以完成:
- 状态转换的动画;
图片旋转的动画。
<animated-vector android:drawable="@drawable/ic_add"><targetandroid:name="@string/add"android:animation="@animator/add_to_remove" /><targetandroid:name="@string/groupAddRemove"android:animation="@animator/rotate_add_to_remove" /></animated-vector>
然后创建目标文件中引用的每个文件。
改变图片状态
在add_to_remove.xml 文件中,使用ObjectAnimator 改变图形形状,其中会用到如下属性:
- propertyName –执行动画的属性;
- valueFrom –矢量路径的初始值;
- valueTo –矢量路径的目标值;
- duration –动画持续时间;
- interpolator –动画插值器;
- valueType –动画值类型。
<objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"android:propertyName="pathData"android:valueFrom="@string/path_add"android:valueTo="@string/path_remove"android:duration="@integer/duration"android:interpolator="@android:interpolator/fast_out_slow_in"android:valueType="pathType" />
形状旋转
可使用相似的方法旋转图像,只是会用到旋转属性和旋转值:
<objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"android:propertyName="rotation"android:valueFrom="-180"android:valueTo="0"android:duration="@integer/duration"android:interpolator="@android:interpolator/fast_out_slow_in" />
执行相反的动画(从 Remove 到 Add)所需的操作方法相同,只不过将动画值反置。
完成后的动态矢量图,效果很不错吧?
使用OneAPM分析UI卡顿
使用 OneAPM 可以快速定位分析UI性能,Mobile Insight的卡顿可以直观地展示这些信息。
可以分析绘制APP卡顿趋势图,精确定位每1秒内的绘图刷新信号中断的次数,从多维度分析卡顿现象,如APP版本、操作系统版本的分布情况等。
卡顿详情列表展示:访问时间,发生卡顿时的流畅度,耗时,发生卡顿时的设备信息,APP版本,操作系统及版本,CPU信息
通过分析该页面信息可以清楚了解到卡顿来源,以便针对性快速优化。
动画卡顿原因
动画卡顿的原因大概有这样三种,这些因素将直接影响动画的性能,导致卡顿。即:
- 手势滑动速度
- 帧率
- 触摸事件响应的速度
手势滑动、帧率是跟各种手机设备有直接的关系,各种各样的硬件设备会表现出不一样的性能,如果从这个方面入手考虑优化,就十分需要 OneAPM Mobile Insight 这样的从多维度来分析性能的一款工具。
结语
虽然只是浅谈,文本旨在围绕创建有意义的动画提供有益的视角,使读者受益。今后,笔者会继续努力,以求进一步改善应用的外观与用户体验。
原文地址:https://medium.com/ribot-labs/exploring-meaningful-motion-on-android-1cd95a4bc61d#.vqazussmj
OneAPM Mobile Insight以真实用户体验为度量标准进行 Crash 分析,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想技术文章,请访问 OneAPM 官方技术博客。
转载于:https://www.cnblogs.com/oneapm/p/5066782.html
探索安卓中有意义的动画!相关推荐
- 安卓案例:View动画——弹球碰壁
安卓案例:View动画--弹球碰壁 一.运行效果
- 安卓Activity转场动画
安卓Activity转场动画:两个Activity进行跳转的时候,转场动画 1,ActivityOptions 适合API21及安卓5.0以上版本 2,兼容类:ActivityOptionsCompa ...
- 安卓手机adb自定义动画速度
安卓手机adb自定义动画速度 首先你需要fastboot工具以及一个键盘 连接上手机打开adb调试 第一步:进入shell模式 adb shell 设置动画速度(1.25自己改,范围[0-10]) s ...
- 安卓开发20:动画之Animation 详细使用-主要通过java代码实现动画效果
AlphaAnimation 透明效果实现: activity_main.xml中仅仅是一个简单的图片,下面的例子都会使用这个xml: <RelativeLayout xmlns:android ...
- 安卓设置Activity切换动画无效的问题
做安卓App时,常会设置Activity之间切换的动画.常用的方法有两种,一种是用Activity的overridePendingTransition方法:另一种是用style文件通过xml进行设置. ...
- android6.0关机动画,安卓系统修改开机动画,关机动画
开机动画 两种开机动画 关于安卓动画的研究主要在frameworks/base/cmds/bootanimation/BootAnimation.cpp中. 安卓系统默认的开机动画(谷歌)的动画是显示 ...
- java运用ascii实现动画效果_安卓开发20:动画之Animation 详细使用-主要通过java代码实现动画效果...
AlphaAnimation 透明效果实现: activity_main.xml中仅仅是一个简单的图片,下面的例子都会使用这个xml: xmlns:tools="http://schemas ...
- 安卓案例:View动画 - 弹球碰壁
文章目录 一.View视图动画实现方法 二.案例演示:View动画 - 弹球碰壁 (一)运行效果 (二)涉及知识点 (三)实现步骤 1.创建安卓应用[BouncingBall] 2.将图片素材拷贝到d ...
- 如何在安卓上显示复杂动画
在安卓上显示一个复杂的动画原来可以这样简单 使用GitHub社开源的框架lottie可以轻松的加载使用Adobe After Effects(后面简称ae)软件生成的.json格式的动画文件,ae生成 ...
最新文章
- 如何在Eclipse中开启代码自动提示功能
- WinForm界面开发之“HTML内容编辑控件”
- Markdown 图片助手-MarkdownPicPicker
- linux下查看、删除指定文件名长度的文件
- 「头条推荐技术解析一」运营和业务模式详解
- 4由通道检测_十堰城区108处桥隧通道接受全面“体检”将持续至8月底
- 安卓微信8.0.11正式版发布:体积突破200MB
- http 长连接 短连接
- 实现JavaScript继承
- .Net Core3 新特性整理
- 【数据结构与算法】【算法思想】【算法应用】【排序查找搜索】并行
- 大数据之-Hadoop3.x_MapReduce_TextInputFormat---大数据之hadoop3.x工作笔记0107
- 自学c语言后的感受,一路走来,浅谈c语言的学习感想
- 24速算c语言实训报告ppt,C语言课程设计(速算24).doc
- Quartz的misfire特性
- 应对对日软件开发的危机
- matlab imrotated,图像旋转由Matlab无需使用imrotate
- Windows7驱动调试小Tips
- QWebEngineView右键菜单翻译
- uni.chooseImage 选择上传多张图片
热门文章
- 对象序列化与反序列化(二进制 byte[])
- vue 动态的修改样式
- CentOS安装nextcloud-17.0.0
- django2.0 自己入门记录一些基础url 模板等
- L255 Learning to say no brings a thrill of freedom
- dev GridControl Column 背景色
- jqGrid 操作一些总结(二)
- 江苏省职称计算机考试internet,江苏省计算机职称考试题库及答案
- kernel32.dll已加载但找不到入口点_关键词分析法和工具,关键词分析包括哪几个方面(已帮助1376人)...
- android 检测网络ftp,Android端与Android端利用WIFI进行FTP通信