您的位置:首页 > 移动开发 > Android开发

Android动画译文(上)

2016-07-18 19:00 393 查看
声明:以下内容翻译于Android官网 Animation and Graphics章节。如果有表述不合理的地方,欢迎指正。

1.概述

Android框架提供了两套动画系统

property animation 属性动画

view animation 视图动画

他们都有可取之处,不过总的来讲。属性动画会更加灵活拥有更多特性。除此之外,你还可以使用(Drawable animation)图片动画,加载图片资源然后一帧一帧的展示它们。

属性动画 Property Animation

属性动画可以操控一个对象的任何属性,包括那些并没有展示在屏幕上的属性,Android3.0(API 11)引入。属性动画可扩展,你也可以操控自定义类型的属性。

视图动画 View Animation

视图动画引入的更早,只能应用于视图(View)上,他的使用简单能满足很多不同应用的需求。

图片动画 Drawable Animation

图片动画就像播放幻灯片一样,一个接一个的展示图片资源。如果你想展示的动画可以很方便的用图片资源做到,你可以采用这种方式。

2.属性动画

属性动画非常强大,你可以通多它控制任何事物动起来。你可以定义一个动画随着时间的变化来使一个对象的任何属性改变,不管它是否在屏幕上显示。概括地说,属性动画是在某一个特定的时间内改变一个对象的某个属性值(对象的一个字段)。通过指定想要改变的属性来达到使某个事物动起来的目的,比如,改变某个对象在屏幕上的位置,这个动画持续的时间,它的位置从哪里变到哪里等等。

属性动画可以定义动画的以下特性:

Duration:指定动画时间。默认为300ms

Time interpolation:属性值对时间的变化函数

Repeat count and behavior:指定动画在完成后是否重复,重复多少次。也可以指定让这个动画反向执行。

Animator sets:可以把一系列动画放在一起同时执行或者在指定间隔之后有序执行。

Frame refresh delay:指定帧刷新间隔

属性动画工作原理 How Property Animation Works

首先,让我们通过一个简单的例子来了解动画。图1 是一个假想物体x属性变化的动画,也即其在屏幕上的水平位置的变化。动画时间是40ms,移动距离为40像素。默认帧刷新频率为10ms,每10ms,物体移动10像素。40ms时动画结束,这个物体停在水平距离40px的位置。这是一个线性插值器的动画的例子。也就是说物体匀速运动。



你也可以指定一些非线性插值器的动画。图2 阐述这样一个例子,物体先加速,后减速运动。依然在40ms内移动40像素,但是是非线性运动。一开始,动画加速到中点,其后减速直到动画结束。就像图2显示的,物体在开始和结束时移动的距离要比中间移动的少。



接下来我们再来仔细看看,上面这些动画中,属性动画内的重要组件是如何运算的。图3展示了主要的类之间如何工作的。



ValueAnimator对象可以追踪动画的执行时间和属性值的变化。其内封装了一个TimeInterpolator和一个TypeEvaluator,用来定义动画的插值和属性的计算值。例如,图2的插值器为AccelerateDecelerateInterpolator,类型评估器为IntEvaluator。

通过创建ValueAnimator对象然后指定执行动画的属性,并设置开始和结束值以及执行时间,之后调用start()方法,动画便开始执行。整个动画中,ValueAnimator会根据动画周期和已经执行的时间来计算一个0到1之间执行率(elapsed fraction),执行率代表动画完成的百分比,0代表完成0%,1代表完成100%。图1中当执行时间t=10ms时,因为总时间为40ms,所以执行率为0.25。

ValueAnimator计算完执行率后,就调用当前的TimeInterpolator来计算插值(interpolated fraction)。插值会依据当前的时间插值器有所不同。比如,图2中动画开始加速慢,插值约等于0.15,而图1,插值和执行率始终相同,插值为0.25。

插值计算完成后,ValueAnimator调用合适的TypeEvaluator,然后依据插值,属性的初始值和结束值来计算当前的属性值。例如,图2执行时间t=10ms时插值为0.15,所以对应的属性值(位移)应该为 0.15 X (40 - 0), or 6.

属性动画和视图动画的区别 How Property Animation Differs from View Animation

视图动画只能适用于View对象,如果你想应用动画在non-View对象,就必须自己实现代码。

视图动画的应用有局限性,View对象只有很少一些方面的动画,比如缩放比例、旋转角度,但是不能改变背景颜色之类。

视图动画另一个弊端是,它只能改变视图绘制的位置,而不是视图实际在的位置。比如,如果你添加动画去移动一个按钮到另外的位置,按钮正确绘制,但是按钮的响应位置并不会改变。

使用属性动画,就没有这些限制,你可以对任何对象(View and non-View)的任何属性添加动画,并且对象本身也的确被改变。属性动画的执行方式更加强大。你可以指定动画执行者你想要改变的属性,比如颜色、位置、尺寸并且你可以定义动画的各个方面,比如插值器,多动画的同步执行等。

不过视图动画设置容易,代码编写简单。所以,如果视图动画能够胜任你的需求,或者现有代码工作正常,便没有必要使用属性动画。值得一提的是,有时候也会出现即使用属性动画也使用视图动画的情况。

属性动画接口概览 API Overview

你可以在
android.animation
包下找到所有属性动画的API。
android.view.animation
还包下定义了许多插值器可供使用。下面的表格,展示了属性动画的主要组件。

Animator类是创建动画的基类。一般情况不会直接使用它,因为它只提供了一些基本的抽象方法。下面是它的一些子类。

ClassDescription
ValueAnimator属性动画的主要时间引擎,并计算执行动画的属性的值。它内部有计算动画参数需要的所有的核心方法以及每个动画的时间细节,还有一些其他信息如,动画是否重复,接收动画更新的监听者,设置自定义类型。对属性执行动画包括两方面:计算属性变化后的值并将其设置给动画执行对象。ValueAnimator并不执行第二方面,所以你必须监听属性值的更新,然后添加自己的逻辑对动画执行对象进行相应的改变。
ObjectAnimator是ValueAnimator的子类,你可以设置目标对象和目标属性来添加动画。该类在根据当前动画计算出一个值后就更新相应的属性值。多数情况,都是使用ObjectAnimator,因为它简化了添加动画的过程。不过有时候还是需要直接使用ValueAnimator,因为ObjectAnimator有一些局限,比如属性必须有getter和setter方法。
AnimatorSet提供一种多个动画分组执行的机制。你可以设置动画一起同时执行或者延时后执行。
Evaluators 告诉动画如何计算指定属性的值。它通过Animator类提供的时间数据以及动画的属性的开始结束值来计算。属性动画提供了一下几种evaluators:

Class/InterfaceDescription
IntEvaluatorint类型的默认evaluator
FloatEvaluatorfloat类型的默认evaluator
ArgbEvaluator十六进制颜色值的默认evaluator
TypeEvaluator通过该接口可以自定义Evaluator。如果你添加动画的属性值不是int/float/color,就必须自己实现TypeEvaluator来指定如何计算对象的属性值。
时间插值器定义指定值随时间变化的函数。例如,你可以指定动画线性变化,也可以指定动画非线性变化。下表展示了android.view.animation包下包含的插值器。如果系统提供的插值器不能满足需求,可以实现TimeInterpolator自定义插值器。

Class/InterfaceDescription
AccelerateDecelerateInterpolator变化率开始慢中间快
AccelerateInterpolator变化率由慢变快
AnticipateInterpolator先反向然后猛得向前
BounceInterpolator弹到最终值
CycleInterpolator重复指定圈数
DecelerateInterpolator开始快然后慢慢减速
LinearInterpolator匀速变
eafd
OvershootInterpolator猛的向前超越目标值后停在最终值
TimeInterpolator自定义插值器的接口

使用ValueAnimator执行动画 Animating with ValueAnimator

ValueAnimator类可以通过制定一系列int,float,color值来指定某类型的值在动画时间内变化。可以通过以下工厂方法来获得ViewAnimator的实例:
ofInt()
,
ofFloat()
,
ofObject()
。例如:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();


你也可以通过以下方式指定自定义类型添加动画:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();


上面的代码片段并没有实际效果,因为
ValueAnimator
并没有直接操作一个对象的属性。你最想要做的应该还是通过这些计算值来改变你想要添加动画的对象。可以通过在动画生命周期中对
ValueAnimator
添加监听然后处理这些监听来达到目的。比如帧更新。实现监听者时,可以通过
getAnimatedValue()
来获得每一帧更新时指定属性的值。

使用ObjectAnimator执行动画 Animating with ObjectAnimator

ObjectAnimator时ValueAnimator的子类,包含时间引擎和目标对象的属性值计算。这让对象的动画的更容易添加,因为你不再需要实现
Value.AnimatorUpdateListener
因为动画属性会自动更新。

实例化ObjectAnimator的方式和ValueAnimator类似,不过你需要指定变化的属性名(字符串形式) 。

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();


想要ObjectAnimator正确的更新属性值,你需要注意以下几点:

动画变化的属性必须有
set<propertyName>()
形式的setter方法,因为ObjectAnimator会在动画期间自动更新属性,因此如果属性名为
foo
它必须通过一个
setFoo()
的方法来设置属性。如果属性没有这个方法,你有以下三个选择:

在对应类里给属性添加setter方法

用包裹类接收对应属性值然后添加setter方法给原始对象设置属性值。

使用ValueAnimator替代

如果你只指定了ObjectAnimator工厂方法中的一个参数,这个参数只能是指定属性的结束值。因此,动画属性必须有
get<propertyName>()
形式的getter方法来获得属性的初始值。

属性的getter和setter方法对应的属性类型必须和ObjectAnimator中指定的类型相同。比如,如果你的用以下方式构造ObjectAnimator,那么目标对象一定要有
targetObject.setPropName(float)
targetObject.getPropName(float)


ObjectAnimator.ofFloat(targetObject, "propName", 1f)


依据动画执行的属性和对象的不同,你也许会调用
invalidate()
方法来强制刷新界面,使视图重绘来更新属性。这些应该在
onAnimationUpdate()
回调函数中执行。比如,执行动画改变一个图片的颜色属性,会刷新屏幕使视图重绘。View中其他类似的setter比如
setAlpha()
setTranslationX()
都会合理的刷新视图,所以你不需要在属性值改变时主动地去刷新界面。

使用AnimatorSet编排多个动画 Choreographing Multiple Animations with AnimatorSet

很多情况下,你需要依据一个动画的开始或者结束来播放另一个动画。Android系统提供了AnimatorSet来将多个动画绑定起来,这样就可以指定是同时有序的开始还是一定间隔后播放这些动画。AnimatorSet也可以互相嵌套。

下面这段代码通过以下步骤播放下面的Animator对象:

1. 播放bounceAnim

2. 同时播放squashAnim1, squashAnim2, stretchAnim1, and 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();


动画的监听 Animation Listeners

你可以在动画执行期间通过下面这些监听者监听一些重要的事件。

Animator.AnimatorListener


onAnimationStart()
-动画开始时调用

onAnimationEnd()
- 动画结束时调用

onAnimationRepeat()
-动画重复时调用

onAnimationCancel()
-动画取消时调用,同时也调用
onAnimationEnd()
,不管动画如何结束。

ValueAnimator.AnimatorUpdateListener


-
onAnimationUpdate()
-每一帧动画都会调用。监听这个事件来使用由ValueAnimator计算的属性值。通过
getAnimatedValue()
来获取这个值。

如果你不想实现
Animator.AnimatorListener
接口的所有方法,你也可以通过继承
AnimatorListenerAdapter
类来代替。它提供了对
Animator.AnimatorListener
接口的空的实现方法。你可以有选择的去重写。例如下面的例子,创建一个AnimatorListenerAdapter类并只重写
onAnimationEnd()
回调方法。

ValueAnimatorAnimator 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布局改变的动画 Animating Layout Changes to ViewGroups

属性动画能既能给ViewGroup对象添加动画也能很方便的给View本身添加动画。

当ViewGroup调用一个View的setVisibility()方法想要使ViewGroup内的View执行出现和消失的动画时。可以通过LayoutTransiton类在ViewGroup内部执行动画。当添加或者删除View时,ViewGroup内剩余的View也能计算他们的新的位置。通过调用setAnimator()然后传递一个带有以下常量之一的Animator对象可以在LayoutTransition对象内定义以下的动画:

APPEARING -容器中的条目出现时执行动画的标志

CHANGE_APPEARING -容器中新条目出现时其他条目执行动画的标志

DISAPPEARING -容器中条目消失时执行动画的标志

CHANGE_DISAPPEARING -容器中条目消失时其他条目执行动画的标志

你可以使用默认动画也可以为以上四种情况自定义动画来个性化布局的过渡。

通过在布局文件中在对应布局下将android:animateLayoutchanges 属性设置为true即可开启ViewGroup的默认动画,例如:

<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />


这样ViewGroup内的条目在添加和移除时就会使用默认动画。

使用TypeEvaluator

如果操作动画改变的属性类型是android系统没有的,那么我们可以通过实现TypeEvaluator接口来自定义评估器。int,float,color所对应的评估器分别为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);
}
}


代码中的fraction的值取决于当前选用的插值器的类型。插值器计算好插值便传递给当前的评估器。

使用插值器 Using Interpolators

插值器定义动画中特定值随时间变化的函数,比如前面的例子。插值器接收动画的执行率(elapsed fraction)作为参数。不同的动画效果对应不同的插值器。Android系统默认提供了一些插值器在
android.view.animation
包下。如果它们都不能满足需求,便只能自己实现TimeInterpolator来自定义插值器。

前面讲过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;
}


下面的表格列出在一个动画中1000ms内根据两种插值器计算出的近似值:

运行时间(ms)插值(线性插值器)插值(加速减速插值器)
000
2000.20.1
4000.40.345
6000.60.8
8000.80.9
100011
可以看出线性插值器计算结果的变化率相同,每200ms变化0.2。而加速减速插值器的变化先慢后快。

指定关键帧 Specifying Keyframes

关键帧由“时间/值”这样一对数据组成,指定动画中特定时间的某个特定状态。每个关键帧可以有单独的插值器来控制动画在前一帧和当前帧期间的行为。

实例化关键帧需要使用工厂类方法。使用
ofInt()``ofFloat()``ofObject()
确定关键帧类型。之后调用
ofKeyFrame()
工厂方法获得一个
PropertyValuesHolder
之后通过Animator的
.ofPropertyValuesHolder(target,mPropertyValuesHolder)
方法就能获得由关键帧定义的动画,示例如下:

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);


给视图添加动画 Animating Views

视图动画通过改变视图绘制方式来改变视图。不过这些改变是在一个“容器”中完成的,因为视图本身没有可操作的属性。这就导致了视图执行了动画,但是其本身的属性并没有改变。所以,视图其实依然存在于原始位置,虽然他看起来已经移动了。在Android3.0中,对View添加了新的属性以及对应的getter和setter方法,来消除这个缺陷。

属性动画便是通过改变视图的属性来达到真实改变视图的目的。并且,当属性改变时,视图会自动调用invalidate()方法来刷新屏幕。以下是为视图新添加的属性以便于应用属性动画。

translationX 和 translationY:通过控制xy轴坐标来控制视图在容器中的位置移动。

rotation, rotationX, 和rotationY: 控制视图绕着轴心的2D或者3D旋转

scaleX and scaleY: 控制视图的2D缩放

pivotX and pivotY: 控制轴心点。默认轴心点为视图中心

x 和 y:xy轴坐标

alpha:视图的透明度

对视图添加动画改变其某个属性,比如:颜色,旋转角度等,你只需要创建属性动画然后指定属性就可以了。例如:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);


使用ViewPropertyAnimator添加动画 Animating with ViewPropertyAnimator

ViewPropertyAnimator内置了一个Animator对象,提供了一种同时改变视图多个属性的简单地方法。用法跟ObjectAnimator类似,也能实际的改变视图的属性。不过同时改变多个属性时,它更高效,而且代码可读性更强。下面的代码片段目的是同时改变视图的x和y属性,分别通过多个ObjectAnimator或一个ObjectAnimator或一个ViewPropertyAnimator来完成。

多个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对象

targetView.animate().x(50f).y(100f);


//注 :View 中的animate方法返回ViewPropertyAnimator 对象
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}


通过XML声明动画 Declaring Animations in XML

属性动画也可以通过XML文件来声明。这样可以更方便的在多个activity中复用动画,编辑动画顺序也更容易。

不过为了区别传统的视图动画文件,从API 3.1开始,应该把属性动画得XML文件放在res/animator目录下。

属性动画类名对应的XML标签如下:

ValueAnimator -
<animator>


AnimatorSet -
<set>


ObjectAnimator -
<objectAnimator>


下面这个例子同时播放两套属性动画:

<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>


为了播放动画,必须将XML文件设置给一个AnimatorSet对象,然后指定动画的目标对象,然后开始动画。例如:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 动画 翻译