本文共 14165 字,大约阅读时间需要 47 分钟。
属性动画系统是一个强大的框架,可以让你几乎任何动画。您可以定义动画来随时间改变任何对象属性,而不管它是否绘制到屏幕上。属性动画会在指定的时间长度内更改属性的(对象中的字段)值。要为某些东西设置动画,可以指定要设置动画效果的对象属性,例如对象在屏幕上的位置,要为其设置动画效果的时间长度,以及要在两者之间进行动画处理的值。
属性动画系统允许您定义动画的以下特征:
要查看属性动画的完整示例,请参阅 示例中的 ChangeColor类。
首先,让我们通过一个简单的例子来看看动画是如何工作的。图1描绘了一个假设对象,该对象使用其x属性进行动画处理,该属性表示其在屏幕上的水平位置。动画的持续时间设置为40毫秒,行进距离为40像素。每10毫秒(默认帧刷新率),对象水平移动10个像素。在40ms结束时,动画停止,并且对象在水平位置40结束。这是具有线性插值的动画的示例,这意味着对象以恒定的速度移动。
图1.线性动画的例子您还可以指定动画以进行非线性插值。图2展示了一个假设对象,该对象在动画开始时加速,并在动画结束时减速。该对象仍然在40毫秒内移动40个像素,但是非线性。开始时,这个动画加速到中途点,然后从中途点减速直到动画结束。如图2所示,动画开始和结束时的行进距离比中间的小。
图2.非线性动画的例子让我们详细看看属性动画系统的重要组件如何计算上面所示的动画。图3描述了主要类如何相互协作。图3.如何计算动画该ValueAnimator对象会跟踪动画的时间,如动画运行的时间长度以及动画的属性的当前值。
定义动画插值的ValueAnimator封装a TimeInterpolator和TypeEvaluator定义如何计算正在动画的属性的值的a。例如,在图2中,所TimeInterpolator使用的将是 AccelerateDecelerateInterpolator和TypeEvaluator将会IntEvaluator。
要开始动画,请创建一个动画ValueAnimator并为其指定要设置动画的属性的开始和结束值以及动画的持续时间。当你调用start()动画开始。在整个动画过程中 ,根据动画持续时间和已经过多少时间,ValueAnimator计算0和1之间的经过小数。经过的分数表示动画完成的时间百分比,0表示0%,1表示100%。例如,在图1中,t = 10 ms处的流逝分数将为.25,因为总持续时间为t = 40 ms。
当ValueAnimator完成计算经过的分数时,它调用TimeInterpolator当前设置的分数来计算 内插分数。内插分数将流逝分数映射到考虑了设置的时间内插的新分数。例如,在图2中,由于动画缓慢加速,因此在t = 10 ms时,插值分数约为0.15,小于流逝分数.25。在图1中,内插分数总是与流逝分数相同。
计算插值分数时,根据内插分数,起始值和动画结束值ValueAnimator调用适当TypeEvaluator的值,以计算您正在动画的属性的值。例如,在图2中,插值分数在t = 10 ms时为.15,因此当时属性的值为.15×(40 - 0)或6。
视图动画系统提供了仅对View 对象进行动画处理的功能,因此如果要为非View对象设置动画效果,则必须执行自己的代码才能实现动画效果。视图动画系统也受到限制,因为它仅暴露View对象的几个方面以进行动画制作,例如视图的缩放和旋转,而不是背景色。
视图动画系统的另一个缺点是只能在视图被绘制的地方进行修改,而不是实际的视图本身。例如,如果您为按钮在屏幕上移动设置动画,该按钮绘制正确,但您可以单击该按钮的实际位置不会更改,因此您必须实现自己的逻辑来处理此操作。
通过属性动画系统,这些约束被完全删除,并且可以为任何对象(视图和非视图)的任何属性设置动画,并且对象本身实际上被修改。属性动画系统在执行动画时也更加健壮。在较高的层次上,您可以将动画设计人员分配给您想要制作动画的属性,例如颜色,位置或大小,并且可以定义动画的各个方面,例如插值和多个动画师的同步。
然而,视图动画系统需要更少的时间进行设置,并且需要的代码更少。如果视图动画完成了您需要执行的所有操作,或者您的现有代码已按照您的要求运行,则无需使用属性动画系统。如果出现用例,那么在不同情况下使用这两种动画系统也是有意义的。
你可以在其中找到大部分属性动画系统的API android.animation。由于视图动画系统已经定义了许多内插器android.view.animation,因此您也可以在属性动画系统中使用这些内插器。下表介绍了属性动画系统的主要组件。
该Animator课程提供了创建动画的基本结构。您通常不直接使用此类,因为它只提供最小的功能,必须扩展才能完全支持动画值。以下子类扩展Animator:
表2.Evaluators时间插值器定义动画中的特定值如何作为时间的函数进行计算。例如,您可以指定动画在整个动画中线性发生,这意味着动画会平均移动整个时间,也可以指定动画以使用非线性时间,例如,开始时加速并在结束时减速动画。表3描述了包含在其中的插入器android.view.animation。如果所提供的插补器都不符合您的需求,请实现TimeInterpolator接口并创建您自己的插件。有关如何编写自定义插补器的更多信息,请参见使用插补器。表3.插值器的ValueAnimator类,可以通过指定一组的动画某种类型为动画的持续时间的值int,float或颜色值进行动画通过。您获得ValueAnimator通过调用其工厂方法之一:ofInt(),ofFloat()或ofObject()。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);animation.setDuration(1000);animation.start();
在此代码中,ValueAnimator当start()方法运行时,开始计算动画的值,介于0和100之间,持续时间为1000毫秒。
您还可以通过执行以下操作来指定一个自定义类型来设置动画效果:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);animation.setDuration(1000);animation.start();
在此代码中,当方法运行时,ValueAnimator开始计算动画的值,之间startPropertyValue以及endPropertyValue使用所提供的逻辑MyTypeEvaluator持续1000毫秒start()。
您可以通过将对象添加AnimatorUpdateListener 到动画中来使用动画的值 ValueAnimator,如下面的代码所示:
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator updatedAnimation) { // You can use the animated value in a property that uses the // same type as the animation. In this case, you can use the // float value in the translationX property. float animatedValue = (float)updatedAnimation.getAnimatedValue(); textView.setTranslationX(animatedValue); }});
在该onAnimationUpdate() 方法中,您可以访问更新后的动画值并将其用于其中一个视图的属性中。有关侦听器的更多信息,请参阅关于动画侦听器的部分 。
这ObjectAnimator是ValueAnimator(上一节讨论的)的一个子类,它将时序引擎和值计算ValueAnimator与对目标对象的命名属性进行动画处理的能力相结合。这使得任何对象的动画更容易,因为你不再需要实现ValueAnimator.AnimatorUpdateListener,因为动画属性会自动更新。
实例化ObjectAnimator类似于a ValueAnimator,但您还要指定对象和该对象属性的名称(作为字符串)以及值之间进行动画处理:
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);animation.setDuration(1000);animation.start();
要ObjectAnimator正确获取更新属性,您必须执行以下操作:
如果您values...在其中一个ObjectAnimator工厂方法中只为参数指定了一个值,则假定它是动画的结束值。因此,您正在动画的对象属性必须具有用于获取动画起始值的getter函数。getter函数的格式必须为get<PropertyName>()。例如,如果属性名称是 foo,您需要有一个getFoo()方法。
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);animation.setDuration(1000);animation.start();
在许多情况下,您想播放取决于另一个动画何时开始或结束的动画。Android系统允许您将动画组合到一起AnimatorSet,以便您可以指定是同时,按顺序开始动画还是在指定的延迟之后开始动画。您也可以AnimatorSet在彼此中嵌套对象。
以下代码片段Animator 按以下方式播放以下对象:
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();
您可以在动画持续时间期间与下面介绍的听众一起收听重要事件。
Animator.AnimatorListener
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() - 调用动画的每一帧。收听此事件以使用ValueAnimator动画期间生成的计算值。要使用该值,请查询ValueAnimator传入事件的对象以通过getAnimatedValue()方法获取当前动画值。如果您使用,则需要实现此侦听器ValueAnimator。
如果您不想实现 接口的所有方法,则可以扩展AnimatorListenerAdapter该类而不是实现Animator.AnimatorListener该Animator.AnimatorListener接口。本AnimatorListenerAdapter类提供的,你可以选择覆盖的方法的空实现。
例如,下面的代码片段AnimatorListenerAdapter 为onAnimationEnd() 回调创建一个:
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);fadeAnim.setDuration(250);fadeAnim.addListener(new AnimatorListenerAdapter() {public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget());}
属性动画系统提供了对ViewGroup对象进行动画变化的功能,并提供了一种简单的方法来为View对象本身制作动画。
您可以使用LayoutTransition该类在ViewGroup内动画布局更改 。ViewGroup中的视图可以在将ViewGroup添加到ViewGroup中或从ViewGroup中删除它们时,或者setVisibility()使用,,或 调用View的方法时 VISIBLE,经历出现和消失的动画 。当您添加或删除视图时,ViewGroup中的其余视图也可以动画到其新位置。您可以通过调用 并传入具有以下常量之一的对象来在对象中定义以下动画:INVISIBLEGONELayoutTransitionsetAnimator()AnimatorLayoutTransition
您可以为这四种类型的事件定义自己的自定义动画,以自定义布局转换的外观,或者仅告诉动画系统使用默认动画。
API演示中的 LayoutAnimations示例向您展示了如何为布局转换定义动画,然后在您想要制作动画的View对象上设置动画。
该 LayoutAnimationsByDefault及其相应layout_animations_by_default.xml 布局资源文件告诉你如何启用默认的布局转换为XML ViewGroups。唯一需要做的就是将该android:animateLayoutchanges 属性设置true为ViewGroup。例如:
将此属性设置为true会自动生成从ViewGroup添加或删除的视图以及ViewGroup中剩余的视图。
本StateListAnimator类允许您定义动画运行时的景色变化的状态。该对象表现为对象的包装, Animator每当指定的视图状态(例如“按下”或“聚焦”)发生更改时调用该动画。
在StateListAnimator可与一个根XML资源被定义 <selector>元素和子<item>每个指定由所限定的不同视图状态元素StateListAnimator类。每个都 <item>包含属性动画集的定义。
例如,以下文件会创建一个状态列表动画器,用于在按下时更改视图的x和y比例:
RES / XML / animate_scale.xml
要将状态列表动画添加到视图,请按如下所示添加属性: android:stateListAnimator
现在animate_scale.xml当这个按钮的状态改变时,使用定义的动画。
或者,也可以将状态列表动画器分配给代码中的视图,使用该AnimatorInflater.loadStateListAnimator()方法,并使用该 方法将动画器分配给视图View.setStateListAnimator()。
或者,也可以不使用视图的动画属性,而是使用状态更改之间播放可绘制动画AnimatedStateListDrawable。Android 5.0中的一些系统小部件默认使用这些动画。以下示例显示如何将AnimatedStateListDrawable一个XML资源定义为:
... - ...
如果您想为Android系统未知的类型创建动画,可以通过实现该TypeEvaluator界面来创建自己的评估器。由Android系统中已知的类型是int,float,或颜色,其由支持IntEvaluator,FloatEvaluator和ArgbEvaluator类型评估。
只有一种方法可以在TypeEvaluator 界面中实现,即evaluate()方法。这允许您正在使用的动画设计人员在动画当前点为您的动画属性返回适当的值。该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); }}
注意:当ValueAnimator(或ObjectAnimator)运行时,它会计算动画的当前流逝部分(介于0和1之间的值),然后根据您正在使用的插值器计算插值版本。内插分数是您TypeEvaluator通过fraction参数接收到的分数,因此您在计算动画值时不必考虑内插器。
内插器定义动画中特定值如何作为时间的函数进行计算。例如,您可以指定动画在整个动画中线性发生,这意味着动画会平均移动整个时间,或者您可以指定动画以使用非线性时间,例如在开始或结束时使用加速或减速动画。
动画系统中的插值器会接收来自动画师的一小部分,表示动画已用时间。插值器修改此分数以符合它旨在提供的动画类型。Android系统提供了一组常用的插值器android.view.animation package。如果这些都不符合您的需求,您可以实现TimeInterpolator界面并创建自己的界面。
作为一个例子,默认插补器AccelerateDecelerateInterpolator和LinearInterpolator计算插值分数如何在下面进行比较。在LinearInterpolator对过去部分没有任何影响。在AccelerateDecelerateInterpolator加速进入动画和减速出来。以下方法定义了这些内插器的逻辑:
AccelerateDecelerateInterpolator
public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;}
LinearInterpolator
public float getInterpolation(float input) { return input;}
下表代表这些插补器对持续1000毫秒的动画计算的近似值:
如表格所示,LinearInterpolator以相同的速度改变数值,.2每200ms通过一次。该AccelerateDecelerateInterpolator值LinearInterpolator在200ms和600ms之间变化更快,在600ms和1000ms之间变化更慢。一个Keyframe对象由一个时间/值对组成,可让您在动画的特定时间定义特定状态。每个关键帧还可以具有自己的插补器,以控制前一关键帧时间与该关键帧时间之间的间隔中的动画行为。
实例化一个Keyframe对象,你必须使用的工厂方法之一,ofInt(),ofFloat(),或ofObject()以获取相应的类型Keyframe。然后您调用ofKeyframe()工厂方法来获取PropertyValuesHolder对象。一旦你有了这个对象,你就可以通过传入PropertyValuesHolder对象和对象来获得动画。以下代码片段演示了如何执行此操作:
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(5000ms);
属性动画系统允许View对象的简化动画,并且与视图动画系统相比具有一些优点。视图动画系统通过改变它们被绘制的方式来转换视图对象。这是在每个视图的容器中处理的,因为视图本身没有可操作的属性。这导致视图被动画,但在视图对象本身中没有改变。这导致行为,例如对象仍然存在于其原始位置,即使它是在屏幕上的不同位置绘制的。在Android 3.0中,添加了新的属性和相应的getter和setter方法来消除这个缺点。
属性动画系统可以通过更改视图对象中的实际属性来在屏幕上动画视图。另外,视图也会自动调用该invalidate() 方法刷新屏幕,只要其属性发生更改。View帮助属性动画的类中的新属性是:
要为View对象的属性(例如其颜色或旋转值)设置动画效果,您只需创建属性动画器并指定要设置动画效果的View属性即可。例如:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
有关创建动画的更多信息,请参阅关于使用ValueAnimator和ObjectAnimator进行动画制作的章节 。
在ViewPropertyAnimator提供了一个简单的方法来制作动画的几个属性View并联,使用单个底层Animator 对象。它的行为非常类似于an ObjectAnimator,因为它会修改视图属性的实际值,但在同时为多个属性制作动画时效率会更高。此外,使用该代码ViewPropertyAnimator更简洁,更易于阅读。以下代码片段显示了在使用多个ObjectAnimator对象,单个对象 ObjectAnimator以及ViewPropertyAnimator何时同时为视图设置动画x和y属性方面的差异 。
多个ObjectAnimator对象ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);AnimatorSet animSetXY = new AnimatorSet();animSetXY.playTogether(animX, animY);animSetXY.start();
一个ObjectAnimator
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
ViewPropertyAnimator
myView.animate().x(50f).y(100f);
有关更多详细信息ViewPropertyAnimator,请参阅相应的Android开发者 博客文章。
属性动画系统允许您用XML声明属性动画,而不是以编程方式进行。通过用XML定义动画,您可以轻松地在多个活动中重复使用动画,并更轻松地编辑动画序列。
为了将使用新属性动画API的动画文件与使用旧视图动画框架的动画文件区分开来,从Android 3.1开始,应该将用于属性动画的XML文件保存在res/animator/目录中。
以下属性动画类使用以下XML标记支持XML声明:
要查找可以在XML声明中使用的属性,请参阅动画资源。以下示例按顺序播放两组对象动画,第一个嵌套组同时播放两个对象动画:
为了运行此动画,您必须将代码中的XML资源充满到AnimatorSet对象中,然后在开始动画设置之前为所有动画设置目标对象。为方便起见,调用setTarget()为所有孩子设置了一个目标对象AnimatorSet。以下代码显示了如何执行此操作:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);set.setTarget(myObject);set.start();
您还可以ValueAnimator用XML 声明一个,如下例所示:
要ValueAnimator在代码中使用先前的代码,您必须膨胀对象,添加 AnimatorUpdateListener,获取更新后的动画值,并将其用于其中一个视图的属性中,如以下代码所示:
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator);xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator updatedAnimation) { float animatedValue = (float)updatedAnimation.getAnimatedValue(); textView.setTranslationX(animatedValue); }});xmlAnimator.start();
有关定义属性动画的XML语法的信息,请参阅动画资源。
更新UI的动画师会为动画运行的每个帧导致额外的渲染工作。因此,使用资源密集型动画可能会对应用程序的性能产生负面影响。
将渲染UI的动画添加到渲染管道的动画阶段。您可以通过启用Profile GPU渲染和监控动画阶段来了解您的动画是否会影响应用的性能。有关更多信息,请参阅配置文件GPU渲染演练。
Lastest Update:2018.04.24
QQ:94297366
微信打赏:
公众号推荐:
转载于:https://blog.51cto.com/4789781/2120969