ribot 致力于打造美好且充满意义的用户体验,在这一过程当中,动画不可或缺。html
在 Droidcon London 听完一场 激励人心的演讲以后, 笔者决定深刻研究安卓动画。本文集中展现了其研究结果,但愿使开发者和设计者们意识到,为 Android 应用添加漂亮的动画并不复杂。java
动画!android
若是你想尝试这些动画效果,本文全部实例都能在 Github 上的这款 Android 应用 中找到。git
笔者很是喜欢动画效果,由于它不只提升用户参与度,还能迅速夺人眼球。想一想那些以动画设计著称的应用,它们使用起来是多么可心、流畅、天然。github
Falcon Pro:即便细微的动画效果也能够对用户体验产生巨大影响。编程
如今,与那些你很喜欢但没有动画的应用作一番比较。性能优化
Medium: 尽管笔者很喜好 medium APP,但它的确缺乏恰当的动画。网络
咱们能够从多个方面利用动画,从而:app
本文旨在说明,在应用中实现有意义的动画十分简单可行——那么,即刻开始吧。框架
在用户触摸屏幕时提供反馈,有助于视觉交流,造成互动。这些动画不该分散用户的注意力,但又使他们享受其中,得到清晰的视感,从而鼓励进一步操做。
安卓框架为此类反馈提供了波纹效果,经过设定视图背景,便可使用:
?android:attr/selectableItemBackground-在视图范围内展现波纹效果;
波纹在接触点开始,以后填充整个视图背景。
?android:attr/selectableItemBackgroundBorderless –将波纹效果延伸至视图以外。
圆形波纹效果在接触点开始,并沿半径延伸至视图以外。
ViewPropertyAnimator 在 API 12 首次引入,容许咱们只使用一个Animator实例,就能够简单高效地使多个视图属性(并行地)执行动画操做。
此处将绘制下文提到的全部动画属性。
注意: 若是已在视图中设置了侦听器,并打算在相同视图下,实现其余动画且不使用回调函数,则须要将侦听器设为 null。
用程序实现时,简单又整洁:
mButton.animate() .alpha(1f) .scaleX(1f) .scaleY(1f) .translationZ(10f) .setInterpolator(new FastOutSlowInInterpolator()) .setStartDelay(200) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }) .start();
注意: 其实咱们不须要在动画生成器中调用 start( ) 方法,由于在中止声明的同时,动画就会自动启动。在这种状况下,只有在 UI toolkit 事件队列开始下一次更新时,动画才会再开始。
制做 FAB 的 alpha 动画值
FAB 的(X 和 Y 轴)坐标动画
FAB 的 Z 坐标动画
注意: 考虑到向后兼容性,你可使用ViewCompat 类,来实如今安卓 API 4 以及以上版本的ViewPropertyAnimator 类。
和 ViewPropertyAnimator 相似,ObjectAnimator 容许咱们在目标视图(代码和 XML 源文件中)的不一样属性中执行动画。然而,它们仍是有些差别的:
使用自定义属性给视图作缩放动画,并改变其前景色,能够达成下图的效果:
使用自定义属性时,能够经过调用ObjectAnimator.ofInt(),建立一个ObjectAnimator实例,此处咱们声明:
接下来,设置评估器(此处使用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(); }
接下来,使用类似的方法作视图缩放的动画,主要区别在于:
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()方法,后者会使用(定义好的动画处理该过程。接着,下一个视图也会使用定义好的入场动画,自动出如今屏幕上。
Interpolator 可用于定义动画的变化率,意味着动画的速度、加速度、行为均可以改变。可用的 interpolator 有数种,且相互之间的差异微乎其微,建议读者在设备上一探究竟。
该视图以线型动做开始和结束动画。
该视图开始动做很快,逐渐降速直至结束。
该视图以线型动做开始,逐渐降速直至结束。
该视图在动画开始时加速,并在接近结束时逐渐减速。
CircularReveal 使用剪切的圆形显示或隐藏一组 UI 元素。该动画除了带来视觉上的连续性,还十分赏心悦目,有助于提升用户参与度。
如上图所示,在视图的动画效果显示以前,使用 ViewPropertyAnimator 隐藏浮动操做图标。只需定义以下属性就能够配置 circular reveal:
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);
定制用于活动间导航的转换,可以使用户对应用状态产生更为强烈的视觉联系。默认状况可定制以下转换:
自 API 21起,还有以下几种新的转换方式:
Explode 转换容许视图从屏幕各个方位退出,会使压缩视图产生爆炸效果。
在网格布局中爆炸效果尤为好。
这种效果易于实现——首先,须要在 res/transition 目录中建立以下转换:
<explode xmlns:android="http://schemas.android.com/apk/res/android" android:duration="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"/>
在这里:
渐变切换使活动转换出现淡入或淡出的效果。
在视图中使用渐变更画操做简单,且效果宜人。
建立此切换的操做比以前的切换更加简单:
<fade xmlns:android="http://schemas.android.com/apk/res/android" android:duration="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> <View android:id="@+id/view_shared_transition" android:transitionName="@string/transition_view"/> <View android:id="@+id/view_separator"/> <TextView android:id="@+id/text_detail"/> <TextView android: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 动画建立本身的自定义转换。例如,将共享元素转换衍伸,成为转换视图变体——当咱们须要显示对话框(或者相似的弹框视图)时,自定义转换就会很是有用。具体以下所示:
该动画能够在组件状态间引导用户的注意力。
先来简单了解一下上图发生了什么:
在 API 21中(Lollipop),AnimatedVectorDrawable 可用于制定VectorDrawable 属性的动画,生成动态图片。
在图片上作几种不一样的动画并不容易。
那么如何完成呢,请看下图:
该图由几个不一样文件组成,首先建立两个独立的矢量文件,每一个都包含以下属性:
注意: 全部被引用的属性都存储在 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"> <group android: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"> <group android: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"> <target android:name="@string/add" android:animation="@animator/add_to_remove" /> <target android:name="@string/groupAddRemove" android:animation="@animator/rotate_add_to_remove" /> </animated-vector>
而后建立目标文件中引用的每一个文件。
在add_to_remove.xml 文件中,使用ObjectAnimator 改变图形形状,其中会用到以下属性:
<objectAnimator xmlns: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" />
可以使用类似的方法旋转图像,只是会用到旋转属性和旋转值:
<objectAnimator xmlns: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性能,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 官方技术博客。