关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。
一、导读
我们继续总结学习基础知识,温故知新。
二、概览
属性动画系统是一个强健的框架,用于为几乎任何内容添加动画效果。您可以定义一个随时间更改任何对象属性的动画,无论其是否绘制到屏幕上。
属性动画会在指定时长内更改属性(对象中的字段)的值。要添加动画效果,请指定要添加动画效果的对象属性,
例如对象在屏幕上的位置、动画效果持续多长时间以及要在哪些值之间添加动画效果。
借助属性动画系统,您可以定义动画的以下特性:
- 时长:您可以指定动画的时长。默认时长为 300 毫秒。
- 时间插值:您可以指定如何根据动画的当前已播放时长来计算属性的值。
- 重复计数和行为:您可以指定是否在某个时长结束后重复播放动画以及重复播放动画多少次。您还可以指定是否要反向播放动画。如果将其设置为反向播放,则会先播放动画,然后反向播放动画,直到达到重复次数。
- Animator 集:您可以将动画分成多个逻辑集,它们可以一起播放、按顺序播放或者在指定的延迟时间后播放。
- 帧刷新延迟:您可以指定动画帧的刷新频率。默认设置为每 10 毫秒刷新一次,但应用刷新帧的速度最终取决于整个系统的繁忙程度以及系统为底层计时器提供服务的速度。
三、常用API
3.1 ValueAnimator
属性动画的主计时引擎,它也可计算要添加动画效果的属性的值。它具有计算动画值所需的所有核心功能,同时包含每个动画的计时详情、有关动画是否重复播放的信息、
用于接收更新事件的监听器以及设置待评估自定义类型的功能。为属性添加动画效果分为两个步骤:计算添加动画效果之后的值,以及对要添加动画效果的对象和属性设置
这些值。
ValueAnimator 不会重复,因此,您必须监听由 ValueAnimator 计算的值的更新情况,并使用您自己的逻辑修改要添加动画效果的对象。
3.2 ObjectAnimator
这是 ValueAnimator 的子类,用于设置目标对象和对象属性以添加动画效果。
此类会在计算出动画的新值后相应地更新属性。在大多数情况下,您不妨使用 ObjectAnimator,因为它可以极大地简化对目标对象的值添加动画效果这一过程。不过,有时您需要直接使用 ValueAnimator,
因为 ObjectAnimator 存在其他一些限制,例如要求目标对象具有特定的访问器方法。
3.3 AnimatorSet
此类提供一种将动画分组在一起的机制,以使它们彼此相对运行。您可以将动画设置为一起播放、按顺序播放或者在指定的延迟时间后播放。
插值器
时间插值器指定了如何根据时间计算动画中的特定值。例如,您可以指定动画在整个动画中以线性方式播放,即动画在整个播放期间匀速移动;也可以指定动画使用非线性时间,
例如动画在开始后加速并在结束前减速。 下面表格 介绍了 android.view.animation 中包含的插值器。
如果下表提供的插值器都不能满足您的需求,请实现 TimeInterpolator 接口并创建您自己的插值器。
类/接口 | 说明 |
---|---|
AccelerateDecelerateInterpolator | 该插值器的变化率在开始和结束时缓慢但在中间会加快。 |
AccelerateInterpolator | 该插值器的变化率在开始时较为缓慢,然后会加快。 |
AnticipateInterpolator | 该插值器先反向变化,然后再急速正向变化。 |
AnticipateOvershootInterpolator | 该插值器先反向变化,再急速正向变化,然后超过定位值,最后返回到最终值。 |
BounceInterpolator | 该插值器的变化会跳过结尾处。 |
CycleInterpolator | 该插值器的动画会在指定数量的周期内重复。 |
DecelerateInterpolator | 该插值器的变化率开始很快,然后减速。 |
LinearInterpolator | 该插值器的变化率恒定不变。 |
OvershootInterpolator | 该插值器会急速正向变化,再超出最终值,然后返回。 |
TimeInterpolator | 该接口用于实现您自己的插值器。 |
四、属性动画的使用
可以在xml中定义,也可直接在代码中使用
4.1 xml中定义属性动画
- 新建目录及文件 res/animator/filename.xml
语法如下:
指定此集合中动画的播放顺序, sequentially依序播放此集合中的动画。 together(默认)同时播放此集合中的动画。 如果值为颜色,则不要指定此属性。动画框架会自动处理颜色值。 intType指定动画值为整数。 floatType(默认)指定动画值为浮点数。 <set> ... set>set>
其实也可以不用 set包裹,定义单一的动画
- 资源引用:
在 Java 或 Kotlin 代码中:R.animator.filename
在 XML 中:@[package:]animator/filename
我们举例:
定义xml动画,然后在代码中调用开始动画。
<set android:ordering="sequentially"> <set> <objectAnimator android:propertyName="x" android:duration="500" android:valueTo="400" android:valueType="intType"/> <objectAnimator android:propertyName="y" android:duration="500" android:valueTo="300" android:valueType="intType"/> set> <objectAnimator android:propertyName="alpha" android:duration="500" android:valueTo="1f"/>set>
开始动画
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.animator.property_animator);set.setTarget(myObject); // 必须操作set.start();
4.2 代码中定义属性动画
4.2.1 使用 ValueAnimator 添加动画效果
借助 ValueAnimator 类,您可以为动画播放期间某些类型的值添加动画效果,只需指定一组要添加动画效果的 int、float 或颜色值即可。
您可以通过调用 ValueAnimator 的任一工厂方法来获取它:ofInt()、ofFloat() 或 ofObject()。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f); animation.setDuration(1000); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationResume(Animator animation) { } @Override public void onAnimationPause(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } }); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // do something } }); animator.setRepeatCount(2); animator.setRepeatMode(ValueAnimator.RESTART); animator.setInterpolator(interpolator); animation.start();
在上述代码中,当 start() 方法运行时,ValueAnimator 会开始计算 1000ms 时长内 0 和 100 之间的动画的值。
4.2.2 使用 ObjectAnimator 添加动画效果
ObjectAnimator 是上一部分中讨论的 ValueAnimator 的子类,它融合了 ValueAnimator 的计时引擎和值计算以及为目标对象的命名属性添加动画效果这一功能。
这可以极大地简化为任何对象添加动画效果的过程,因为动画属性会自动更新,因此您也无需再实现 ValueAnimator.AnimatorUpdateListener 了。
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(view, "alpha", 0.3f, 1f); objectAnimator1.setDuration(1000); objectAnimator1.setRepeatCount(ValueAnimator.INFINITE); objectAnimator1.setRepeatMode(ValueAnimator.REVERSE); objectAnimator1.start();
上面的代码是修改view的透明度,看到构造方法里面的那个 “alpha” 了吧,我们在构造时,传入的对象,如上面的view,
必须包含有setAlpha()方法,否则设置是失败的,我们看view的源码,发现果然有
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) 还有很多其他的public void setRotation(float rotation) public void setRotationX(float rotation) public void setRotationY(float rotation) public void setBackgroundColor(@ColorInt int color)public void setScaleX(float scaleX)public void setScaleY(float scaleX)public void setTranslationX(float translationX)public void setTranslationY(float translationX)等等
所以可以有
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 0f, xValue, 0f); ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "translationY", 0f, yValue, 0f); ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.95f, 1f, 0.95f); ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.95f, 1f, 0.95f); ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(view, "rotation", 0.0F, 360f); 等等
这里面的属性可以随意指定,只要我们要做动画的类中包含有该属性的set方法,例如:
public class Test { public final void setProcess(final float process) { } ObjectAnimator mProcessAnimator = ObjectAnimator.ofFloat(this, "process", 0, 0)}
注意事项
要使 ObjectAnimator 正确更新属性,您必须执行以下操作:
- 要添加动画效果的对象属性必须具有 set() 形式的 setter 函数(采用驼峰式大小写形式)。由于 ObjectAnimator 会在动画过程中自动更新属性,它必须能够使用此 setter 方法访问该属性。例如,如果属性名称为 foo,则需要使用 setFoo() 方法。如果此 setter 方法不存在,您有三个选择:
- 如果您有权限,可将 setter 方法添加到类中。
- 使用您有权更改的封装容器类,让该封装容器使用有效的 setter 方法接收值并将其转发给原始对象。
- 改用 ValueAnimator。
- 如果您在 ObjectAnimator 的一个工厂方法中仅为 values… 参数指定了一个值,则系统会假定它是动画的结束值。
因此,要添加动画效果的对象属性必须具有用于获取动画起始值的 getter 函数。getter 函数必须采用 get() 形式。例如,如果属性名称为 foo,则需要使用 getFoo() 方法。 - 要添加动画效果的属性的 getter(如果需要)和 setter 方法的操作对象必须与您为 ObjectAnimator 指定的起始值和结束值的类型相同。
例如,如果构建以下 ObjectAnimator,则必须具有 targetObject.setPropName(float) 和 targetObject.getPropName(float):
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- 根据您要添加动画效果的属性或对象,您可能需要对视图调用 invalidate() 方法,以强制屏幕使用添加动画效果之后的值重新绘制自身。
您可以在 onAnimationUpdate() 回调中执行此操作。例如,如果为可绘制对象的颜色属性添加动画效果,则仅当该对象重新绘制自身时,屏幕才会刷新。
视图的所有属性 setter(如 setAlpha() 和 setTranslationX())都会使视图失效,因此,在使用新值调用这些方法时,您无需使视图失效。如需详细了解监听器,请参阅动画监听器部分。
4.2.3 使用 AnimatorSet 编排多个动画
使用AnimatorSet ,可以指定是同时播放动画、按顺序播放还是在指定的延迟时间后播放。还可以相互嵌套 AnimatorSet 对象。
举例:
以下代码段通过以下方式播放相应的 Animator 对象:
播放 bounceAnim。
2、同时播放 squashAnim1、squashAnim2、stretchAnim1 和 stretchAnim2。
3、播放 bounceBackAnim。
4、播放 fadeAnim。
AnimatorSet bouncer = new AnimatorSet(); bouncer.play(bounceAnim).before(squashAnim1); bouncer.play(squashAnim1).with(squashAnim2); bouncer.play(squashAnim1).with(stretchAnim1); bouncer.play(squashAnim1).with(stretchAnim2); bouncer.play(bounceBackAnim).after(stretchAnim2); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(bouncer).before(fadeAnim); animatorSet.start();
下面同时播放3个动画
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(view, "alpha", 0.3f, 1f); objectAnimator1.setDuration(1000); objectAnimator1.setRepeatCount(ValueAnimator.INFINITE); objectAnimator1.setRepeatMode(ValueAnimator.REVERSE); ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(view, "rotation", 0.0F, 360f); objectAnimator2.setDuration(5000); objectAnimator2.setRepeatCount(ValueAnimator.INFINITE); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setInterpolator(new LinearInterpolator()); animatorSet.playTogether(objectAnimator1, objectAnimator2); animatorSet.start();
4.2.4 动画监听器
您可以使用下述监听器来监听动画播放期间的重要事件。
-
Animator.AnimatorListener
- onAnimationStart() - 在动画开始播放时调用。
- onAnimationEnd() - 在动画结束播放时调用。
- onAnimationRepeat() - 在动画重复播放时调用。
- onAnimationCancel() - 在动画取消播放时调用。取消的动画也会调用 onAnimationEnd(),无论它们以何种方式结束。
-
ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate() - 对动画的每一帧调用。监听此事件即可使用 ValueAnimator 在动画播放期间生成的计算值。要使用该值,请查询传递到事件中的 ValueAnimator 对象,
以使用 getAnimatedValue() 方法获取当前添加动画效果之后的值。如果使用了 ValueAnimator,则必须实现此监听器。
- onAnimationUpdate() - 对动画的每一帧调用。监听此事件即可使用 ValueAnimator 在动画播放期间生成的计算值。要使用该值,请查询传递到事件中的 ValueAnimator 对象,
4.2.5 为 ViewGroup 对象的布局更改添加动画效果
将 ViewGroup 的 android:animateLayoutchanges 属性设置为 true,将此属性设置为 true 可自动为添加到 ViewGroup 或从中删除的视图以及该 ViewGroup 中剩余的视图添加动画效果
eg:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
4.2.6 使用 StateListAnimator 为视图状态更改添加动画效果
使用根 元素和子 元素在 XML 资源中定义 StateListAnimator,每个元素都指定一个由 StateListAnimator 类定义的不同视图状态。每个 都包含一个属性动画集的定义。
例如,以下文件创建了一个状态列表 Animator,可在按下后更改视图的 x 和 y 比例:
res/xml/animate_scale.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> set> item> <item android:state_pressed="false"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> set> item> selector>
要将状态列表 Animator 附加到视图,请添加 android:stateListAnimator 属性,如下所示:
<Button android:stateListAnimator="@xml/animate_scale" ... />
现在,当此按钮的状态发生变化时,会使用 animate_scale.xml 中定义的动画。
或者,如果要转为在代码中将状态列表 Animator 分配给视图,则可使用 AnimatorInflater.loadStateListAnimator() 方法,
然后使用 View.setStateListAnimator() 方法将 Animator 分配给相应视图。
您还可以使用 AnimatedStateListDrawable 在状态更改间播放可绘制动画,而不是为视图的属性添加动画效果。
Android 5.0 中的一些系统微件默认会使用这些动画。以下示例展示了如何将 AnimatedStateListDrawable 定义为 XML 资源:
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/pressed" android:drawable="@drawable/drawableP" android:state_pressed="true"/> <item android:id="@+id/focused" android:drawable="@drawable/drawableF" android:state_focused="true"/> <item android:id="@id/default" android:drawable="@drawable/drawableD"/> <transition android:fromId="@+id/default" android:toId="@+id/pressed"> <animation-list> <item android:duration="15" android:drawable="@drawable/dt1"/> <item android:duration="15" android:drawable="@drawable/dt2"/> ... animation-list> transition> ... animated-selector>
4.2.7 使用 TypeEvaluator
如果要为 Android 系统无法识别的类型添加动画效果,则可以通过实现 TypeEvaluator 接口来创建您自己的评估程序。
Android 系统可以识别的类型为 int、float 或颜色,分别由 IntEvaluator、FloatEvaluator 和 ArgbEvaluator 类型评估程序提供支持。
TypeEvaluator 接口中只有一种要实现的方法,那就是 evaluate() 方法。这样,您使用的 Animator 就会在动画的当前点为添加动画效果之后的属性返回适当的值。FloatEvaluator 类演示了如何做到这一点:
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }
4.2.8 指定关键帧
Keyframe 对象由时间值对组成,用于在动画的特定时间定义特定的状态。每个关键帧还可以用自己的插值器控制动画在上一关键帧时间和此关键帧时间之间的时间间隔内的行为。
要实例化 Keyframe 对象,您必须使用它的任一工厂方法(ofInt()、ofFloat() 或 ofObject())来获取类型合适的 。然后,通过调用 ofKeyframe() 工厂方法来获取 PropertyValuesHolder 对象。
获取对象后,您可以通过传入 PropertyValuesHolder 对象以及要添加动画效果的对象来获取 Animator。以下代码段演示了如何做到这一点:
Keyframe kf0 = Keyframe.ofFloat(0f, 0f); Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); Keyframe kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation); rotationAnim.setDuration(5000);
五、 推荐阅读
来源地址:https://blog.csdn.net/fumeidonga/article/details/135049594