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

android动画基础知识最全总结

2017-01-20 14:48 701 查看
动画设计几个原则:

1、动画一定要有用

不要为了动画而动画

2、动画是为了让用户体验更平滑

3、动画是为了让用户使用的时候更自然

自然的动画指的是动画要尽量与人在现实生活中的感知一致,越一致,人的思维开销就越小,也就越舒服

4、某些精心设计的动画还能表达一些含义,有效降低用户的学习和使用软件的成本

不要为了动画而动画,使用动画是为了与人现实生活中的认知相一致,动画要平滑、自然、有用

Android系统提供了很多丰富的API去实现UI的2D与3D动画,最主要的划分可以分为如下几类:

View Animation: 视图动画只能被用来设置View的动画。
Drawable Animation: 这种动画(也叫Frame动画、帧动画)其实可以划分到视图动画的类别,专门用来一个一个的显示Drawable的resources,就像放幻灯片一样。
Property Animation: 属性动画只对Android 3.0(API 11)以上版本系统才有效,这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。这种动画是可扩展的,可以自定义任何类型和属性的动画。

一、View Animation

视图动画(View Animation)又称补间动画(Tween Animation),即给出两个关键帧通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。视图动画只能作用于View对象,是对View的变换,默认支持的类型有:

透明度变化(AlphaAnimation)
缩放(ScaleAnimation)
位移(TranslateAnimation)
旋转(RotateAnimation)

可以使用AnimationSet让多个动画集合在一起运行,使用插值器(Interpolator)设置动画的速度。

上面的几种动画以及AnimationSet都是Animation的子类,因此Animation中有的属性以及xml的配置属性,他们都有,对于使用xml配置时需要放到res下面的anim文件夹下。



在Android3.0(api 11)之前,是不能用属性动画的,只能用补间动画,而补间动画所做的动画效果只是将View的显示转为图片,然后再针对这个图片做透明度、平移、旋转、缩放等效果。这带来的问题是,View所在的区域并没有发生变化,变化的只是个“幻影”而已。也就是说,在Android
3.0之前,要想将View区域发生变化,就得改变top、left、right、bottom。如果想让View的动画是实际的位置发生变化,并且要兼容3.0之前的软件,为了解决这个问题,从3.0开始,加了几个新的参数:x,y,translationX,translationY。

x = left + translationX;

y = top + translationY;

这样,如果想要移动View,只需改变translationX和translationY就可以了,top和left不会发生变化。也可以使用属性动画去改变translationX和translationY。

1.1 Animation属性详解

xml属性java方法解释
android:detachWallpapersetDetachWallpaper(boolean)是否在壁纸上运行
android:durationsetDuration(long)动画持续时间,毫秒为单位
android:fillAftersetFillAfter(boolean)控件动画结束时是否保持动画最后的状态
android:fillBeforesetFillBefore(boolean)控件动画结束时是否还原到开始动画前的状态
android:fillEnabledsetFillEnabled(boolean)与android:fillBefore效果相同
android:interpolatorsetInterpolator(Interpolator)设定插值器(指定的动画效果,譬如回弹等)
android:repeatCountsetRepeatCount(int)重复次数
android:repeatModesetRepeatMode(int)重复类型有两个值,reverse表示倒序回放,restart表示从头播放
android:startOffsetsetStartOffset(long)调用start函数之后等待开始运行的时间,单位为毫秒
android:zAdjustmentsetZAdjustment(int)表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal
1.2 AlphaAnimation 透明度动画

改变视图的透明度,可以实现淡入淡出等动画。这个动画比较简单只需要设置开始透明度和结束透明度即可。

xml属性java方法解释
android:fromAlphaAlphaAnimation(float fromAlpha, …)动画开始的透明度(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlphaAlphaAnimation(…, float toAlpha)动画结束的透明度,同上
1.3 ScaleAnimation 缩放

缩放动画,支持设置开始x缩放(宽度缩放倍数),开始y缩放, 结束x缩放,结束y缩放,以及缩放基点x坐标,缩放基点y坐标。

x缩放和y缩放都是相对于原始的宽度和高度的,1.0表示不缩放。

坐标基点,同时有参数可以设置坐标基点类型,分别是:

Animation.ABSOLUTE(默认值) 相对于控件的0点的坐标值

Animation.RELATIVE_TO_SELF 相对于自己宽或者高的百分比(1.0表示100%)

Animation.RELATIVE_TO_PARENT 相对于父控件的宽或者高的百分比.

默认基点是视图的0点,默认坐标基点类型是ABSOLUTE。

有如下几种构造函数:

ScaleAnimation(Context
context,
AttributeSet attrs)

ScaleAnimation(float
fromX,
float toX,
float fromY,
float toY)

new
ScaleAnimation(1.0f,
1.5f,
1.0f,
1.5f);

ScaleAnimation(float
fromX,
float toX,
float fromY,
float toY,
float pivotX,
float pivotY)

new
ScaleAnimation(1.0f,
1.5f,
1.0f,
1.5f,
10,
10);

ScaleAnimation(float
fromX,
float toX,
float fromY,
float toY,
int pivotXType,
float pivotXValue,
int pivotYType,
float pivotYValue)

new
ScaleAnimation(1.0f,
1.5f,
1.0f,
1.5f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f);
//以中心点为基点

XML配置

<scale

android:fromXScale="float"

android:toXScale="float"

android:fromYScale="float"

android:toYScale="float"

android:pivotX="float"

android:pivotY="float"
/>

1.4 TranslateAnimation 位移

平移支持x轴平移起点和y轴平移起点,以及设置结束点。同时每个点都可以设置type,type和上面缩放动画的基点类型一样,默认类型是ABSOLUTE.

有以下几个构造函数:

TranslateAnimation(Context
context,
AttributeSet attrs)

TranslateAnimation(float
fromXDelta,
float toXDelta,
float fromYDelta,
float toYDelta)

TranslateAnimation(int
fromXType,
float fromXValue,
int toXType,
float toXValue,
int fromYType,
float fromYValue,
int toYType,
float toYValue)

XML配置:

<translate

android:fromXDelta="float"

android:toXDelta="float"

android:fromYDelta="float"

android:toYDelta="float"
/>

1.5 RoatationAnimation 旋转

旋转支持设置旋转开始角度,和旋转结束角度,以及旋转基点,和旋转基点类型。类型同上面一样,默认旋转基点是(0,0),默认类型同上面一样。

有以下几个构造函数:

RotateAnimation(Context
context,
AttributeSet attrs)

RotateAnimation(float
fromDegrees,
float toDegrees)

RotateAnimation(float
fromDegrees,
float toDegrees,
float pivotX,
float pivotY)

RotateAnimation(float
fromDegrees,
float toDegrees,
int pivotXType,
float pivotXValue,
int pivotYType,
float pivotYValue)

XML配置:

<rotate

android:fromDegrees="float"

android:toDegrees="float"

android:pivotX="float"

android:pivotY="float"
/>

旋转中心点的坐标有三种表示方式,一是纯数字,使用绝对位置(比如"50",表示以当前View左上角坐标加50px作为X坐标);二是百分数,相对于控件本身定位(比如"50%",表示以当前View的左上角加上当前View宽度的50%作为X坐标);三是百分数p,相对于父控件定位(比如"50%p",表示以当前View的左上角加上父控件宽度的50%做为X坐标)。

1.6 AnimationSet详解

动画集合就是可以让多个动画一起运行,或者依次运行。AnimationSet继承自Animation,是上面四种的组合容器管理类,没有自己特有的属性,他的属性继承自Animation,所以特别注意,当对set标签使用Animation的属性时会对该标签下的所有子控件都产生影响。通过addAnimation(Animation a)向集合中添加动画,使用子动画的setStartOffset(long offset)设置延时,从而实现子动画之间的间隔。可以设置是否共享时间插值器。

视图动画的使用

<?xml version="1.0"
encoding="utf-8"?>

<set
xmlns:android="http://schemas.android.com/apk/res/android"

android:interpolator="@[package:]anim/interpolator_resource"

android:shareInterpolator=["true"
| "false"]
>

<alpha

android:fromAlpha="float"

android:toAlpha="float"
/>

<scale

android:fromXScale="float"

android:toXScale="float"

android:fromYScale="float"

android:toYScale="float"

android:pivotX="float"

android:pivotY="float"
/>

<translate

android:fromXDelta="float"

android:toXDelta="float"

android:fromYDelta="float"

android:toYDelta="float"
/>

<rotate

android:fromDegrees="float"

android:toDegrees="float"

android:pivotX="float"

android:pivotY="float"
/>

<set>

...

</set>

</set>

Animation animation
=
AnimationUtils.loadAnimation(this,
R.anim.hyperspace_jump);

view.startAnimation(animation);

//或者这样

view.setAnimation(animation);

animation.start();

Animation还有如下一些比较实用的方法介绍:

Animation类的方法解释
reset()重置Animation的初始化
cancel()取消Animation动画
start()开始Animation动画
setAnimationListener(AnimationListener listener)给当前Animation设置动画监听
hasStarted()判断当前Animation是否开始
hasEnded()判断当前Animation是否结束
动画原理解析

动画就是根据间隔时间,不停的去刷新界面,把时间分片,在那个时间片,通过传入插值器的值到Animation.applyTransformation(),来计算当前的值(比如旋转角度值,透明度等).

在Animation类中applyTransformation()方法是空的,具体实现由它的子类来完成。每个动画实现类都重载了Animation类的applyTransformation()方法,该方法把一些属性组装成一个Transformation类,该方法会被getTransformation()方法调用。

在动画的执行过程中,会反复的调用applyTransformation()方法。每次调用参数interpolatedTime值都会变化,该参数从0.0递增为1.0,当该参数为1.0时表示动画结束。Transformation类封装了矩阵Matrix和透明度Alpha值,通过参数Transformation来设置Matrix和Alpha,实现各种效果。

因此,可以继承Animation,重写applyTransformation()来实现其他的动画。

在启动动画时,一般是调用View类的startAnimation(anim)方法。

先从View类开始看起。

/**

* Start the specified animation now.

*

* @param
animation
the animation to start now

*/

public void startAnimation(Animation animation) {

animation.setStartTime(Animation.START_ON_FIRST_FRAME);

setAnimation(animation);

invalidateParentCaches();

invalidate(true);

}

/**

* Sets the next animation to play for this view.

* If you want the animation to play immediately, use

* {@link
#startAnimation(android.view.animation.Animation)} instead.

* This method provides allows fine-grained

* control over the start time and invalidation, but you

* must make sure that 1) the animation has a start time set, and

* 2) the view's parent (which controls animations on its children)

* will be invalidated when the animation is supposed to

* start.

*

* @param
animation
The next animation, or null.

*/

public void setAnimation(Animation animation) {

mCurrentAnimation = animation;

if (animation !=
null) {

// If the screen is off assume the animation start time is now instead of

// the next frame we draw. Keeping the START_ON_FIRST_FRAME start time

// would cause the animation to start when the screen turns back on

if
(mAttachInfo
!= null &&
mAttachInfo.mDisplayState
== Display.STATE_OFF

&& animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {

animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());

}

animation.reset();

}

}

在startAnimation()方法内部,先调用setAnimation()方法给成员变量mCurrentAnimation赋值,然后它调用invalidate()来重绘自己。

接下来进入draw()方法。

boolean draw(Canvas canvas,
ViewGroup parent, long drawingTime) {
//
other code ......
final Animation
a = getAnimation();
if (a
!= null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
}
// other code ......
}

public Animation
getAnimation() {
return mCurrentAnimation;
}

private boolean applyLegacyAnimation(ViewGroup
parent, long drawingTime,
Animation a, boolean scalingRequired)
{
//
other code ......
boolean more
= a.getTransformation(drawingTime, t, 1f);
//
other code ......
}

在draw()方法内部调用了View类的applyLegacyAnimation(),而applyLegacyAnimation()内部又调用了Animation类的getTransformation(long,Transformation,float)方法。

再次回到Animation类中,会发现最终在getTransformation()方法中调用了applyTransformation()方法,实现各种动画效果。

public boolean getTransformation(long currentTime,
Transformation outTransformation,
float scale)
{
mScaleFactor = scale;
return getTransformation(currentTime,
outTransformation);
}

public boolean getTransformation(long currentTime,
Transformation outTransformation) {
//
other code ......
applyTransformation(interpolatedTime, outTransformation);
//
other code ......
}

二、 Drawable Animation

Drawable动画其实就是Frame动画(帧动画),实现像播放幻灯片一样的效果,这种动画的实质其实是Drawable,所以这种动画的XML定义方式文件一般放在res/drawable/目录下。

可以使用xml或者java方式实现帧动画。但是依旧推荐使用xml,具体如下:

<animation-list> 必须是根节点,包含一个或者多个<item>元素,属性有:android:oneshot true代表只执行一次,false循环执行。

<item> 类似一帧的动画资源。 animation-list的子项,包含属性如下:

android:drawable 一个frame的Drawable资源。

android:duration 一个frame显示多长时间。

关于帧动画相对来说比较简单,这里给出一个常规使用框架,如下:

<!--
注意:rocket.xml文件位于res/drawable/目录下
-->

<?xml version="1.0"
encoding="utf-8"?>

<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"

android:oneshot=["true"
|
"false"]
>

<item

android:drawable="@[package:]drawable/drawable_resource_name"

android:duration="integer"
/>

</animation-list>

使用java代码创建示例代码:

AnimationDrawable animation = new AnimationDrawable();
for (int i
= 0; i < 5;
i++) {
int id
= getResources().getIdentifier("card_icon_wifi_" + i, "drawable",
getPackageName());
Drawable drawable = getResources().getDrawable(id);
// 调用addFrame()方法依次添加drawable对象
animation.addFrame(drawable, 500);
}
// 添加到View中
imageView.setBackgroundDrawable(anim);
// 启动动画
animation.start();

在java代码中启动动画

// 获取AnimationDrawable对象
AnimationDrawable animation = (AnimationDrawable) imageView.getBackground();
// 启动动画
animation.start();

特别注意,AnimationDrawable的start()方法不能在Activity的onCreate方法中调用,因为AnimationDrawable还未完全附着到window上,所以最好的调用时机是onWindowFocusChanged()方法中。

帧动画的分析

帧动画是通过AnimationDrawable类来实现。在AnimationDrawable类中,定义了一个AnimationState类型的成员变量mAnimationState,用来存储一系列的drawable对象。

public class AnimationDrawable extends DrawableContainer implementsRunnable,
Animatable {
//
...
private AnimationState
mAnimationState;
//
...
}

AnimationState继承自DrawableContainerState类。AnimationState类自己定义了成员变量mDurations和mOneShot,分别存储每一帧的时长和动画是否需要循环。而在其父类中有一个Drawable类型数组mDrawables,用于存储每一帧drawable对象。

private final static class AnimationState extends DrawableContainerState
{
//
...
private int[]
mDurations;
private boolean mOneShot
= false;
// ...

public void addFrame(Drawable
dr, int dur) {
//
...
int pos
= super.addChild(dr);
mDurations[pos] = dur;
// ...
}
// ...
}

public abstract static class DrawableContainerState extends ConstantState
{
// ...
Drawable[] mDrawables;
// ...

public final int addChild(Drawable
dr) {
//
...
mDrawables[pos] = dr;
//
...
}
}

当调用addFrame()方法时,动画的每一帧被依次添加到成员变量mAnimationState中。

public void addFrame(@NonNull Drawable
frame, int duration) {
mAnimationState.addFrame(frame, duration);
if (!mRunning)
{
setFrame(0, true, false);
}
}

最后,调用start()方法启动动画。方法内部调用了setFrame()方法,在setFrame()中调用selectDrawable(),传入当前帧的索引,并在方法最后调用invalidateSelf()重写绘制。

public void start()
{
mAnimating = true;

if (!isRunning())
{
// Start from 0th frame.
setFrame(0, false,
mAnimationState.getChildCount() > 1
|| !mAnimationState.mOneShot);
}
}

private void setFrame(int frame, boolean unschedule, boolean animate)
{
if (frame
>= mAnimationState.getChildCount()) {
return;
}
mAnimating = animate;
mCurFrame = frame;
selectDrawable(frame);
if (unschedule
|| animate) {
unscheduleSelf(this);
}
if (animate)
{
//
Unscheduling may have clobbered these values; restore them
mCurFrame = frame;
mRunning = true;
scheduleSelf(this,
SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
}
}

三、Property Animation(属性动画)使用详解

在使用属性动画之前先看几个常用的View属性成员:

translationX,translationY:控制View的位置,值是相对于View容器左上角坐标的偏移。

rotationX,rotationY:控制相对于轴心旋转。

x,y:控制View在容器中的位置,即左上角坐标加上translationX和translationY的值。

alpha:控制View对象的alpha透明度值。

3.1属性动画概述

Android 3.0以后引入了属性动画,可以轻而易举的实现许多View动画做不到的事,属性动画实现原理就是修改控件的属性值实现的动画。



java类名xml关键字描述信息
ValueAnimator放置在res/animator/目录下在一个特定的时间里执行一个动画
TimeAnimator不支持时序监听回调工具
ObjectAnimator放置在res/animator/目录下一个对象的一个属性动画
AnimatorSet放置在res/animator/目录下动画集合
The property animation system is a robust framework that allows you to animate almost anything. You can define an animation to change any object property over time, regardless of whether it draws to the screen or not. A property
animation changes a property's (a field in an object) value over a specified length of time. To animate something, you specify the object property that you want to animate, such as an object's position on the screen, how long you want to animate it for, and
what values you want to animate between.

Android属性动画提供了以下属性:

Duration:动画的持续时间;
TimeInterpolation:定义动画变化速率的接口,所有插值器都必须实现此接口,如线性、非线性插值器;
TypeEvaluator:用于定义属性值计算方式的接口,有int、float、color类型,根据属性的起始、结束值和插值一起计算出当前时间的属性值;
Animation sets:动画集合,即可以同时对一个对象应用多个动画,这些动画可以同时播放也可以对不同动画设置不同的延迟;
Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响;
Repeat Country and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以让此动画一直重复,或播放完时向反向播放;

3.2 属性动画计算原理



Figure 1. Example of a linear animation



Figure 2. Example of a non-linear animation



Figure 3. How animations are calculated

其中的ValueAnimator是动画的执行类,跟踪了当前动画的执行时间和当前时间下的属性值;ValueAnimator封装了动画的TimeInterpolator时间插值器和一个TypeEvaluator类型估值,用于设置动画属性的值,就像上面图2非线性动画里,TimeInterpolator使用了AccelerateDecelerateInterpolator、TypeEvaluator使用了IntEvaluator。为了执行一个动画,需要创建一个ValueAnimator,并且指定目标对象属性的开始、结束值和持续时间。在调用start后,整个动画过程中,
ValueAnimator会根据已经完成的动画时间计算得到一个0到1之间的分数,代表该动画的已完成动画百分比。0表示0%,1表示100%,譬如上面图一线性匀速动画中总时间
t = 40 ms,t = 10 ms的时候是 0.25。

当ValueAnimator计算完已完成动画百分比后,它会调用当前设置的TimeInterpolator,去计算得到一个interpolated(插值)分数,在计算过程中,已完成动画百分比会被加入到新的插值计算中。如上图2非线性动画中,因为动画的运动是缓慢加速的,它的插值分数大约是 0.15,小于t = 10ms时的已完成动画分数0.25。而在上图1中,这个插值分数一直和已完成动画分数是相同的。

当插值分数计算完成后,ValueAnimator会根据插值分数调用合适的 TypeEvaluator去计算运动中的属性值。

看如下源码如何实现计算插值分数的:

/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extendsBaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context,AttributeSet attrs) {
}
//这是关注重点,可以发现如下计算公式计算后(input即为时间因子)插值大约为0.15。
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
returnNativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
}
}

其实AccelerateDecelerateInterpolator的基类接口是TimeInterpolator,如下只有getInterpolation方法:

/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}

接着ValueAnimator会根据插值分数调用合适的TypeEvaluator(IntEvaluator)去计算运动中的属性值,如下,因为startValue = 0,所以属性值:0+0.15*(40-0)= 6。

/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, IntegerendValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}

3.3 XML方式属性动画

在xml中可直接用的属性动画节点有ValueAnimator、ObjectAnimator、AnimatorSet。

<set
android:ordering=["together" | "sequentially"]>
<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=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]/>
<animator
android:duration="int"
android:valueFrom="float
| int | color"
android:valueTo="float
| int | color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:valueType=["intType" | "floatType"]/>
<set>
...
</set>
</set>

<set>

A container that holds other animation elements (<objectAnimator>, <valueAnimator>, or other <set> elements). Represents an AnimatorSet. You can specify nested <set> tags to further group animations together. Each <set> can define
its own ordering attribute.

<objectAnimator>

Animates a specific property of an object over a specific amount of time. Represents an ObjectAnimator.

attributes:

xml属性解释
android:propertyNameString. Required. The object's property to animate, referenced by its name. For example you can specify "alpha" or "backgroundColor" for a View object. The objectAnimator element does not expose a target attribute, however, so you cannot set the object to animate
in the XML declaration. You have to inflate your animation XML resource by calling loadAnimator() and call setTarget() to set the target object that contains this property.
android:valueToRequired float、int或者color类型,必须要设置的节点属性,表明动画结束的点;如果是颜色的话,由6位十六进制的数字表示。
android:valueFrom相对应valueTo,动画的起始点,如果没有指定,系统会通过属性的get方法获取,颜色也是6位十六进制的数字表示。
android:duration动画的时长,int类型,以毫秒为单位,默认为300毫秒。
android:startOffset动画延迟的时间,从调用start方法后开始计算,int型,毫秒为单位。
android:repeatCount一个动画的重复次数,int型,”-1“表示无限循环,”1“表示动画在第一次执行完成后重复执行一次,也就是两次,默认为0,不重复执行。
android:repeatMode重复模式:int型,当一个动画执行完的时候应该如何处理。该值必须是正数或者是-1,“reverse”会使得按照动画向相反的方向执行,可实现类似钟摆效果。“repeat”会使得动画每次都从头开始循环。
android:valueType关键参数,如果该value是一个颜色,那么就不需要指定,因为动画框架会自动的处理颜色值。有intType和floatType(默认)两种:分别说明动画值为int和float型。
<animator>

Performs an animation over a specified amount of time. Represents a ValueAnimator.

XML属性动画使用方法:

AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();

3.4 Java方式属性动画

1、ObjectAnimator:继承自ValueAnimator,允许指定要进行动画的对象以及该对象的一个属性。该类会根据计算得到的新值自动更新属性。大多数的情况使用ObjectAnimator就足够了,因为它使得目标对象动画值的处理过程变得足够简单,不用像ValueAnimator那样得写动画更新的逻辑,但是ObjectAnimator有一定的限制,比如它需要目标对象的属性提供指定的处理方法(譬如提供getXXX,setXXX方法),这时候就需要根据特定的需求在ObjectAnimator和ValueAnimator中看哪种实现更方便了。

ObjectAnimator类提供了ofInt、ofFloat、ofObject这个三个常用的方法,这些方法都是设置动画作用的元素、属性、开始、结束等任意属性值。当属性值(上面方法的参数)只设置一个时就把通过getXXX反射获取的值作为起点,设置的值作为终点;如果设置两个(参数),那么一个是开始、另一个是结束。

特别注意:ObjectAnimator的动画原理是不停的调用setXXX方法更新属性值,所有使用ObjectAnimator更新属性时的前提是Object必须声明有getXXX和setXXX方法。

通常使用ObjectAnimator设置View已知的属性来生成动画,而一般View已知属性变化时都会主动触发重绘图操作,所以动画会自动实现;但是也有特殊情况,譬如作用Object不是View,或者作用的属性没有触发重绘,或者在重绘时需要做其它的操作,那都可以通过如下方法手动设置:

ObjectAnimator mObjectAnimator= ObjectAnimator.ofInt(view,"customerDefineAnyThingName", 0, 1).setDuration(2000);
mObjectAnimator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
//int
value = animation.getAnimatedValue(); 可以获取当前属性值
//view.postInvalidate();
可以主动刷新
//view.setXXX(value);
//view.setXXX(value);
//......可以批量修改属性
}
});

2、PropertyValuesHolder:多属性动画同时工作管理类。有时需要同时修改多个属性,那就可以用到此类,具体如下:

PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f,1f);
PropertyValuesHolder a2 =PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);
......
ObjectAnimator.ofPropertyValuesHolder(view, a1, a2,......).setDuration(1000).start();

3、ValueAnimator:属性动画中的时间驱动,管理着动画时间的开始、结束属性值,相应时间属性值计算方法等。包含所有计算动画值的核心函数以及每一个动画时间节点上的信息、一个动画是否重复、是否监听更新事件等,并且还可以设置自定义的计算类型。

特别注意:ValueAnimator只是动画计算管理驱动,设置了作用目标,但没有设置属性,需要通过updateListener里设置属性才会生效。

ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight); //定义动画
animator.setTarget(view); //设置作用目标
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation){
float value = (float) animation.getAnimatedValue();
view.setXXX(value); //必须通过这里设置属性值才有效
view.mXXX = value; //不需要setXXX属性方法
}
});

正是由于ValueAnimator不直接操作属性值,所以要操作对象的属性可以不需要setXXX与getXXX方法,完全可以通过当前动画的计算去修改任何属性。

4、AnimationSet:动画集合,提供把多个动画组合成一个组合的机制,并可设置动画的时序关系,如同时播放、顺序播放或延迟播放。具体使用方法比较简单,如下:

ObjectAnimator a1 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0f);
ObjectAnimator a2 = ObjectAnimator.ofFloat(view, "translationY", 0f,viewWidth);
......
AnimatorSet animSet = new AnimatorSet();
animSet.setDuration(5000);
animSet.setInterpolator(new LinearInterpolator());
//animSet.playTogether(a1, a2, ...); //两个动画同时执行
animSet.play(a1).after(a2); //先后执行
......//其他组合方式
animSet.start();

5、Evaluators相关类解释: Evaluators就是属性动画系统如何去计算一个属性值。它们通过Animator提供的动画的起始和结束值去计算一个动画的属性值。

IntEvaluator:整数属性值。

FloatEvaluator:浮点数属性值。

ArgbEvaluator:十六进制color属性值。

TypeEvaluator:用户自定义属性值接口,譬如对象属性值类型不是int、float、color类型,必须实现这个接口去定义自己的数据类型。如下示例。

ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(5000);
valueAnimator.setObjectValues(new float[2]); //设置属性值类型
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<float[]>()
{
@Override
public float[] evaluate(float fraction, float[] startValue,
float[] endValue)
{
//实现自定义规则计算的float[]类型的属性值
float[] temp = new float[2];
temp[0] = fraction * 2;
temp[1] = (float)Math.random() * 10 * fraction;
return temp;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float[] xyPos = (float[]) animation.getAnimatedValue();
view.setHeight(xyPos[0]); //通过属性值设置View属性动画
view.setWidth(xyPos[1]); //通过属性值设置View属性动画
}
});

6、Interpolators相关类

java类xml id值描述
AccelerateDecelerateInterpolator@android:anim/accelerate_decelerate_interpolator动画始末速率较慢,中间加速
AccelerateInterpolator@android:anim/accelerate_interpolator动画开始速率较慢,之后慢慢加速
AnticipateInterpolator@android:anim/anticipate_interpolator开始的时候从后向前甩
AnticipateOvershootInterpolator@android:anim/anticipate_overshoot_interpolator类似上面AnticipateInterpolator
BounceInterpolator@android:anim/bounce_interpolator动画结束时弹起
CycleInterpolator@android:anim/cycle_interpolator循环播放速率改变为正弦曲线
DecelerateInterpolator@android:anim/decelerate_interpolator动画开始快然后慢
LinearInterpolator@android:anim/linear_interpolator动画匀速改变
OvershootInterpolator@android:anim/overshoot_interpolator向前弹出一定值之后回到原来位置
PathInterpolator新增,定义路径坐标后按照路径坐标来跑。
7、ViewPropertyAnimator动画

在Android API 12时,View中添加了animate方法,具体如下:

public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
......
/**
* This method returns a ViewPropertyAnimator object, which can be used
to animate
* specific properties on this View.
*
* @return ViewPropertyAnimator The ViewPropertyAnimator associated with
this View.
*/
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}
......
}

iewPropertyAnimator提供了一种非常方便的方法为View的部分属性设置动画(是部分属性),它可以直接使用一个Animator对象设置多个属性的动画;在多属性设置动画时,比上面的ObjectAnimator更加高效,因为他会管理多个属性的invalidate方法统一调用触发,而不像上面分别调用,所以还会有一些性能优化。如下就是一个例子:

myView.animate().x(0f).y(100f).start();

四、Transition动画(Layout动画和Activity动画)

Transition概述

Lollipop 中Activity和 Fragment的过渡动画是基于 Android一个叫作 Transition 的新特性实现的。 初次引入这个特性是在 KitKat API 19(Android 4.4.2)中,Transition 框架提供了一个方便的 API 来构建应用中不同 UI 状态切换时的动画。 这个框架始终围绕两个关键概念:场景和过渡。

场景(Scene) 描述应用中 UI 的状态,在一个动画中一般会定义两个场景,开始和结束,有点类似于之前的关键帧

过渡(Transtion) 确定两个场景转换之间的过渡动画,描述一个视图树变化效果。

当场景转换,Transition 的主要职责是:

捕获每一个 View 的起始和结束状态

根据这些数据来创建从一个场景到另一个场景间的过渡动画。

那为什么要引入Transition动画呢?由于在Android引入了Metrial Desigon之后,动画的场面越来越大,比如以前我们制作一个动画可能涉及到的View就一个,或者就那么几个,如果我们一个动画中涉及到了当前Activity视图树中的各个View,那么情况就复杂了。比如我们要一次针对视图树中的10个View进行动画,这些View的效果都不同,可能有的是平移,有的是旋转,有的是淡入淡出,那么不管是使用之前哪种方式的动画,我们都需要为每个View定义一个开始状态和结束状态【关键帧,比如放缩,我们得设置fromXScale和toXScale
】,随着View个数的增加,这个情况会越来越复杂。

Transition框架中的着三个核心类:

场景 Scene:场景记录了一个视图树中所有View的属性值

Scene.getSceneForLayout(rootView, R.layout.scene2, this);

Scene mScene = new Scene(mSceneRoot, (ViewGroup) mSceneRoot.findViewById(R.id.container));

Scene mScene = new Scene(mSceneRoot, findViewById(R,id.scene));

不管哪种方式,我们都需要为场景指定一个rootview,这个rootview将为作为视图树场景的根View。

rootview在动画开始时,会将rootview中的所有子View都remove掉,然后在rootview中加载我们的end场景。所以,对于end场景,如通过代码new Scene(mSceneRoot, view)创建的场景其实对于view是有要求的:要么view是mSceneRoot直接子view,这样在一开始就会被rootview remove掉,或者view是没有parentview的,不然在addview的时候会报错。

过渡 Transition:

Transition针对start场景和end场景视图树中View的一些属性,比如width,height,position,visibility等的变化,定义了过渡的效果。系统内置了changebounds、fade、slide等效果。

Transition不仅仅可以直接作用到整个视图树,如果我们不想把动画应用到视图树中的所有的View上,通过Transition.addTarget&removeTarget。将上面例子中的Transition改成:

Scene scene2 = Scene.getSceneForLayout(rootView,R.layout.scene2,BasicTransitionsActivity.this);

ChangeBounds changeBounds = new ChangeBounds();

changeBounds.addTarget(R.id.image1);

TransitionManager.go(scene2,changeBounds);

可以给视图树增加一个动画集合,使用TransitionSet即可

延迟动画

通过上面的例子,可以看到使用Transition动画,在定义一个end场景的时候需要通过一个layout来描述视图树的状态,这样多少有些麻烦,特别是只想改变当前已经在展示的视图树,基于此,Transition框架提供了一个更加简单的机制,称为延迟动画。

它的使用非常简单:

ChangeBounds changeBounds = new ChangeBounds();

changeBounds.setDuration(1000);

//开启延迟动画,在这里会记录当前视图树的状态

TransitionManager.beginDelayedTransition(rootView,changeBounds);

//我们直接修改视图树中的View的属性

ViewGroup.LayoutParams layoutParams = circulView1.getLayoutParams();

layoutParams.height = 400;

layoutParams.width = 400;

circulView1.setLayoutParams(layoutParams);

ViewGroup.LayoutParams layoutParams2 = circulView2.getLayoutParams();

layoutParams2.height = 100;

layoutParams2.width = 100;

circulView2.setLayoutParams(layoutParams2);

在执行TransitionManager.beginDelayedTransition后,系统会保存一个当前视图树状态的场景,然后我们直接修改视图树,在下一次绘制时,系统会自动对比之前保存的视图树,然后执行一步动画。

可以自己定义过渡动画的效果,此时,我们就需要继承自Transition,然后自己去实现动画

4.1 LayoutAnimator容器布局动画

Property动画系统还提供了对ViewGroup中View添加时的动画功能,可以用LayoutTransition对ViewGroup中的View进行动画设置显示。LayoutTransition的动画效果都是设置给ViewGroup,然后当被设置动画的ViewGroup中添加删除View时体现出来。该类用于当前布局容器中有View添加、删除、隐藏、显示等时候定义布局容器自身(其他的子View)的动画和View的动画,也就是说当在一个LinerLayout中隐藏一个View的时候,可以自定义整个由于LinerLayout隐藏View而改变的动画,同时还可以自定义被隐藏的View自己消失时候的动画等。

This class enables automatic animations on layout changes in ViewGroup objects. To enabletransitions for a layout container, create a LayoutTransition object and set it on any ViewGroup by calling {@link ViewGroup#setLayoutTransition(LayoutTransition)}. This
will cause default animations to run whenever items are added to or removed from that container. To specify custom animations, use the {@link LayoutTransition#setAnimator(int, Animator) setAnimator()} method.

LayoutTransition类中主要有五种容器转换动画类型,具体如下:

LayoutTransition.APPEARING:当View出现或者添加的时候View出现的动画。
LayoutTransition.CHANGE_APPEARING:当添加View导致布局容器改变的时候整个布局容器的动画。
LayoutTransition.DISAPPEARING:当View消失或者隐藏的时候View消失的动画。
LayoutTransition.CHANGE_DISAPPEARING:当删除或者隐藏View导致布局容器改变的时候整个布局容器的动画。
LayoutTransition.CHANGE:当不是由于View出现或消失造成对其他View位置造成改变的时候整个布局容器的动画。





XML方式使用系统提供的默认LayoutTransition动画:

可以通过如下方式使用系统提供的默认ViewGroup的LayoutTransition动画:

android:animateLayoutChanges=”true”

在ViewGroup添加如上xml属性默认是没有任何动画效果的,因为前面说了,该动画针对于ViewGroup内部东东发生改变时才有效,所以当设置如上属性然后调用ViewGroup的addView、removeView方法时就能看见系统默认的动画效果了。

还有一种就是通过如下方式设置:

android:layoutAnimation=”@anim/customer_anim”

java方式使用系统提供的默认LayoutTransition动画:

在使用LayoutTransition时,可以自定义这几种事件类型的动画,也可以使用默认的动画,总之最终都是通过setLayoutTransition(LayoutTransition lt)方法把这些动画以一个LayoutTransition对象设置给一个ViewGroup。

譬如实现如上Xml方式的默认系统LayoutTransition动画如下:

mTransitioner = new LayoutTransition();
mViewGroup.setLayoutTransition(mTransitioner);

自定义这几类事件的动画,分别实现他们,那么可以像下面这么处理:

mTransitioner = new LayoutTransition();
......
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "scaleX", 0, 1);
......//设置更多动画
mTransition.setAnimator(LayoutTransition.APPEARING, anim);
......//设置更多类型的动画 mViewGroup.setLayoutTransition(mTransitioner);

4.2 Activity切换动画

1. 使用的方法为 overridePendingTransition,传入两个参数。第一个参数是第二个 activity 进入的动画,第二个参数是第一个 activity 退出的动画(5.0之前),方法必须在startActivity()或者 finish()方法的后面。

2. Activity Transition(5.0之后):

提供了几种Transition类型:

进入(enter):一个进入的过渡(动画)决定activity中的所有的视图怎么进入屏幕。

再次进入(reenter):用于决定如果当前Activity已经打开过,并且再次打开该Activity时的动画

退出(exit):一个退出的过渡(动画)决定一个activity中的所有视图怎么退出屏幕。

返回(Return):返回

共享元素(shared elements):一个共享元素过渡(动画)决定两个activities之间的过渡,怎么共享(它们)的视图。

Android内置的几种动画效果

explode(分解) –进或出地移动视图,从屏幕中间

slide(滑动) -进或出地移动视图,从屏幕边缘

fade(淡出) –通过改变屏幕上视图的不透明度达到添加或者移除视图(的效果)

在以上动画基础上还可以添加还支持共享元素过渡:(以上效果的共享元素效果基于分解动画基础上进行)

它的作用就是共享两个acitivity共同的元素,在Android 5.0下支持如下效果:

changeBounds - 改变目标视图的布局边界

changeClipBounds - 裁剪目标视图边界

changeTransform - 改变目标视图的缩放比例和旋转角度

changeImageTransform - 改变目标图片的大小和缩放比例

使用步骤:

1.设置动画(两种方式):

1.1xml设置

当定义继承了material主题样式时,使用android:windowContentTransitions属性启用窗口的内容转换(效果)。还可以指定进入、退出、和共享元素的转换:

<style name="myTheme" parent="android:Theme.Material">
<!--
允许使用transitions -->
<item name="android:windowContentTransitions">true</item>

<!--
指定进入和退出transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>

<!--
指定shared element transitions -->
<item name="android:windowSharedElementEnterTransition">
@transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">
@transition/change_image_transform</item>
</style>

定义transition动画:

<transitionSetxmlns:android="http://schemas.android.com/apk/res/android">
<explode/>
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
</transitionSet>

1.2代码设置

// 允许使用transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

// 设置一个exit transition
getWindow().setExitTransition(new Explode());//new
Slide() new Fade()

Window.setEnterTransition():设置进入动画
Window.setExitTransition():设置退出效果
Window.setSharedElementEnterTransition():设置共享元素的进入动画
Window.setSharedElementExitTransition():设置共享元素的退出动画

2.Activity跳转方法:

进入退出动画跳转:

startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

共享元素跳转:

为了使有一个共享元素的两个activities间使用过渡动画:

1.在主题中启用窗口内容过渡

2.在主题样式中指定共享元素的过渡

3.定义过渡动画XML资源

4.使用android:transitionName属性给两个布局中的共享元素指定一个相同的名字(名字一定不要写错)

5.使用ActivityOptions.makeSceneTransitionAnimation() 方法

intent = new Intent(Transitions.this, Transitions4.class);
startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this,view,"shareName").toBundle());

如果有多个共享元素:

ActivityOptions options =ActivityOptions.makeSceneTransitionAnimation(this,
Pair.create(view1, "shareName1"),
Pair.create(view2, "shareName2"));

在另外一个页面中对共享元素使用

ViewCompat.setTransitionName(image, "shareName");

4.3 Fragment切换动画

原理和上面大致一样

使用注意问题

1.OOM问题

帧动画使用图片过大容易OOM。

2.内存泄漏

当有些属性动画是无限运行的,比如转圈~,这类动画要在Activity的onPause()中及时暂停!

3.使用View动画后无法隐藏

setVisibility(View.GONE)失效,使用clearAnimation()消除View动画。

4.点击问题

View动画新位置无法触发点击事件,属性动画旧位置无法触发点击事件。(版本也有些区别,需要注意)

参考:

http://blog.csdn.net/yanbober/article/details/46481171

http://www.jianshu.com/p/1104b47cbfc9

http://blog.isming.me/2015/02/01/android-view-animation/

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0113/2310.html

http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html

http://blog.csdn.net/ruancoder

http://blog.csdn.net/yegongheng/article/details/38455191

http://www.jianshu.com/u/a79bd8f1db7c
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息