该文章是一个系列文章, 是本人在 Android 开发的漫漫长途上的一点感想和记录, 我会尽量按照先易后难的顺序进行编写该系列. 该系列引用了Android 开发艺术探索以及深入理解 Android 卷 ,,中的相关知识, 另外也借鉴了其他的优质博客, 在此向各位大神表示感谢, 膜拜!!!
前言
上一篇文章呢, 我们说了关于 View 动画的那些事, 这里也在总结一下, 使用 View 动画时需要注意以下 4 点:(更多详情请参看我的上一篇博客.)
View 动画的坐标系: View 动画说到底是 View 的一系列运动, 既然是运动, 那么参照物 (坐标系) 是很重要的.
View 动画的使用场景: View 动画的主体是 View, 更准确的说是 View 的副本(影子),View 动画更改的只是显示, 其 x,y 坐标仍然没有改变, 响应事件的位置没有改变, 也就是说 view 本身并没有改变. 也因此, 不要使用 View 动画做交互性操作, 例如点击.
自定义 View 动画的步骤: 如果我们不满足于系统已经定义好的 Animation,=, 可以自定义自己的 Animation, 重写 initialize 和 applyTransformation 这两个方法即可.
Matrix: 接着上一点说, 如果想要掌握高级的酷炫的动画效果, 那么理解 Android View 动画的矩阵变换的实质是必经之路.
那么本章呢是来介绍 Android 动画的另外一个大类属性动画
属性动画简介
属性动画是 API11 新加入的特性, 和 View 动画不同, 它可以对任何对象做动画, 甚至还可以没有对象, 动画默认时间间隔 300ms, 默认帧率 10ms / 帧. 其可以达到的效果是: 在一个时间间隔内完成对对象从一个属性值到另一个属性值得改变. 常用属性动画类 ValueAnimator,ObjectAnimator 和 AnimationSet, 其中 ObjectAnimator 继承于 ValueAnimator.
注: gihub 上 JakeWharton 大神对 API11 之前做了属性动画的兼容, 它的原理其实也很简单, 主要就是判断当前 sdk 版本, 如果大于 API11, 那么就调用官方的 API, 否则自己实现动画效果. 另外, 在 API 使用方面, 它与官方的属性动画基本一致. 另外, 在 API 使用方面, 它与官方的属性动画基本一致. 比如 ObjectAnimator,ValueAnimator 等等. 感兴趣的同学可以访问该项目的 github 网址 https://github.com/JakeWharton/NineOldAndroids
属性动画的使用
Java 代码实现
例如改变一个对象 (obj) 的 translationY 属性, 可以写为
ValueAnimator.ofFloat(obj,"translationY",100);
XML 实现(属性动画的 XML 描述语法的固定格式)
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering=["sequentially"|"together"]>
<objectAnimator
android:propertyName="string"
android:duration="int"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["restart"|"reverse"]
android:valueType=["colorType"|"intType"]>
</objectAnimator>
<animator
android:duration="int"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["restart"|"reverse"]
android:valueType=["colorType"|"intType"]>
</animator>
...
</set>
很多属性都是见名知义的, 下面呢简单介绍一下各属性名称的含义.
属性动画的核心类有 3 个类, AnimatorSet,ObjectAnimator 以及 ValueAnimator
XML 文件中的 <set > 标签对应 AnimatorSet, <set > 标签的 ordering 属性有 2 个候选值 "sequentially"|"together", 分别表示 <set > 标签内的动画是按照前后顺序播放和同时播放.
<animatior > 对应 ValueAnimator,
android:duration: 表示动画的时长
android:valueFrom: 表示属性的起始值
android:valueTo: 表示属性的结束值
android:startOffset: 表示动画的延迟时间, 动画开始后, 需要延迟多少毫秒后才会真正播放该动画
android:repeatCount: 表示动画的重复次数, 默认值是 0, 为 - 1 时, 表示无限循环.
android:repeatMode: 表示动画的重复播放模式, restart 表示动画每次都是重新开始播放, reverse 表示动画第 1 次播放完毕后, 第 2 次会逆向播放, 第 3 次又从头开始播放, 以此类推
<objectAnimator > 对应 ObjectAnimator,
android:propertyName: 表示属性动画作用对象的属性名称
android:valueType: 表示 android:propertyName 的值的类型, 分为 intType, 和 floatType, 分别代表整型数值和浮点型数值, 若 android:propertyName 指定的属性表示的是颜色, 那么无需指定 android:valueType, 系统会自动适配
其他属性的含义与上面的 < animatior > 一致.
属性动画的进阶
我们先来看一个需求: 要求对一个 Button 做动画, 要求让其宽度从原始宽度增加到 500px. 这也太简单了,
- Button mButton = (Button) findViewById(R.id.button);
- ObjectAnimator.ofInt(mButton,"width",500).setDuration(1000).start();
程序运行, 但是却没有效果, 这是为什么呢, 仔细想想没效果也是应该的, 因为你随便传递了一个属性名称过去, 轻则动画没有效果, 重则直接 Crash. 那么这个号称可以对任意属性做动画的属性动画使用的时候有哪些需要注意的地方呢
属性动画要求动画的作用对象提供该属性的 get 和 set 方法,
属性的改变必须通过某种方法反映出来, 比如会带来 UI 的修改之类的
以上的条件缺一不可
这时又有一个问题如果想要对一个对象的属性做动画, 但是属性又没有对应的 get 和 set 方法怎么办呢??
概括来讲有如下 3 种解决办法:
给你的对象加上 get 和 set 方法, 如果你有权限的话. 而这个方法对于 Android SDK 内部实现的类就不可行, 这个方法是最简单的, 但是往往是不可行的.
用一个类包装原始对象, 间接为其提供 get 和 set 方法
采用 ValueAnimator, 监听动画过程, 自己实现属性的改变
下面仍以上面 Button 的宽度动画作为需求给出方法 2,3 的解决代码
方法 2:
- mButton = (Button) findViewById(R.id.button);
- ObjectAnimator.ofInt(new ViewWrapper(mButton),"width",500).setDuration(1000).start();
- private class ViewWrapper{
- private View mTarget;
- public ViewWrapper(View mTarget) {
- this.mTarget = mTarget;
- }
- public int getWidth() {
- return mTarget.getLayoutParams().width;
- }
- public void setWidth(int width) {
- mTarget.getLayoutParams().width = width;
- mTarget.requestLayout();
- }
- }
方法 3:
使用方法 3 之前, 我们先来看属性动画的监听器 AnimatorUpdateListener 和 AnimatorListener
属性动画的监听器 AnimatorUpdateListener 和 AnimatorListener
- AnimatorUpdateListener
- public static interface AnimatorListener {
- void onAnimationStart(Animator animation);
- void onAnimationEnd(Animator animation);
- void onAnimationCancel(Animator animation);
- void onAnimationRepeat(Animator animation);
- }
如上代码所示 AnimatorListener 监听了动画的开始, 结束, 取消和重复播放, 同时系统提供了 AnimatorListenerAdapter 适配器方便我们使用, 我们可以继承这个类并有选择的实现方法.
- AnimatorListener
- public static interface AnimatorUpdateListener {
- /**
- * <p>Notifies the occurrence of another frame of the animation.</p>
- *
- * @param animation The animation which was repeated.
- */
- void onAnimationUpdate(ValueAnimator animation);
- }
如上图所示, AnimatorUpdateListener 监听了动画的整个过程, 动画每播放一帧, onAnimationUpdate 就被调用一次,
下面就来看一下如何使用上面的属性动画的监听器来实现属性动画
- mButton = (Button) findViewById(R.id.button);
- performAnimate(mButton, mButton.getWidth(), 500);
- private void performAnimate(final View target, final int start, final int end) {
- ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- // 持有一个 IntEvaluator 对象, 方便下面估值的时候使用
- private IntEvaluator mEvaluator = new IntEvaluator();
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- // 获得当前动画的进度值, 整型, 1-100 之间
- int currentValue = (Integer) animator.getAnimatedValue();
- Log.d(TAG, "current value:" + currentValue);
- // 获得当前进度占整个动画过程的比例, 浮点型, 0-1 之间
- float fraction = animator.getAnimatedFraction();
- // 直接调用整型估值器通过比例计算出宽度, 然后再设给 Button
- target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
- target.requestLayout();
- }
- });
- valueAnimator.setDuration(5000).start();
- }
使用动画的注意事项
OOM 问题, 此类问题主要出现在帧动画中, 帧动画中如果使用大量图片, 则可能会造成 OOM 问题, 避免的方法同理, 最好不要在帧动画中使用大量大尺寸的图片
View 动画的问题, View 动画的作用主题实际上是 View 的影子, 所以 View 动画不适合做有交互性点击的动画, 另外 View 动画也不能真正改变 View 的状态, 所以有的时候会出现动画结束后, View.setVisibily(VIEW.GONE)失效, 或者其他异常情况, 使用 View.clearAnimation()清除动画即可
内存泄露, 在属性动画中有一类无限循环的动画, 这类动画要在 Activity 销毁之前及时停止, 否则会造成 Activity 无法回收导致内存泄漏.
本篇总结
本章呢接着上一篇说了 Android 动画的另外一个大类属性动画, 至此 Android 动画相关的文章完结, 因笔者水平有限, 所以有不当之处还请指出
来源: https://www.cnblogs.com/wangle12138/p/9467084.html