动画的意义:
视觉效果
引导用户
一、简介
Android动画可分为View Animation(视图动画
)和Property Animation(属性动画
)。
View Animation包括Tween Animation(补间动画
)和Frame Animation(帧动画
)。
Property Animation包括ValueAnimator
和ObjectAnimator
。
在Android系统中,视图动画继承至Animation类,而属性动画继承至Animator类。
这里先简单介绍一下它们的xml使用方式,让读者有个大致的了解:
动画类型 | 存放路径 | 标签 | 获取方式 | 使用方式 |
---|---|---|---|---|
补间动画 | res/anim | scale、rotate、translate、alpha、set | AnimationUtils.loadAnimation() | view.startAnimation(anim) |
帧动画 | res/drawable | animation-list | 将drawable作为src或background,然后从View中获取Drawable,并强转成AnimationDrawable | AnimationDrawable.start() |
ValueAnimator | res/animator | animator | AnimatorInflater.loadAnimator() | start(),然后在AnimatorUpdateListener回调中更新View属性 |
ObjectAnimator | 同上 | objectAnimator | 同上 | 先setTarget(),再start() |
AnimatorSet | 同上 | set | 同上 | 同上 |
二、视图动画
1、Tween Animation
顾名思义,补间动画,给出起点、终点、时长、变换方式,系统自动计算出这个过程中所需要的变换。
Android补间动画主要有以下5种:
xml标签 | java类 |
---|---|
scale | SacleAnimation |
alpha | AlphaAnimation |
rotate | RotateAnimation |
translate | TranslateAnimation |
set | AnimationSet |
所有动画都继承至Animation,它们都有一些共同的属性:
xml属性 | java方法 | 解释 |
---|---|---|
android:duration | setDuration(long) | 设置动画时长 |
android:fillAfter | setFillAfter | 保持动画结束状态 |
android:fillBefore | setFillBefore | 动画结束时,回到开始状态 |
android:repeatCount | setRepeatCount | 设置重复次数,[整数]/[infinite] |
android:repeatMode | setRepeatMode | 设置重复模式,[restart]/[reverse] |
android:interpolator | setInterpolator(Interpolator) | 设置插值器 |
其中只有set可以包含多个动画,并让其同时
生效,如果需要set中,某个动画延迟运行,可以指定startOffset
。注意set中repeatCount是无效的,必须对每个动画单独设置才有作用。
使用方式:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true"
android:fillAfter="true">
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:duration="2000" />
<scale
android:fromXScale="0"
android:fromYScale="0"
android:pivotX="50%p"
android:pivotY="50%p"
android:toXScale="1.4"
android:toYScale="1.4"
android:duration="2000" />
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720"
android:duration="2000" />
<translate
android:fromXDelta="0"
android:fromYDelta=0"
android:toXDelta="-80"
android:toYDelta="-80"
android:duration="2000" />
</set>
xml的scale、rotate标签中pivotX和pivotY,表示缩放起始点坐标,可以是数值、百分数、百分数p,如50、50%、50%p。
如果是50,则表示在当前视图的左上角,即原点处加上50px,作为缩放起始点的坐标;
如果是50%,则表示在当前控件的左上角加上自己宽度的50%做为缩放起始点的坐标。
如果是50%p,则表示在当前控件的左上角加上父控件宽度的50%做为缩放起始点的坐标。
当AnimationSet的shareInterpolator为true时,所有的动画都会使用定义的interpolator插值器。
private void runTweenAnimation(Context context, View target, int resId) {
Animation anim = AnimationUtils.loadAnimation(context, resId); //java的Animation直接new
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) { }
@Override
public void onAnimationRepeat(Animation animation) { }
});
target.startAnimation(anim);
}
2、Frame Animation
也叫帧动画或者逐帧动画,由字面意思可以知道,它类似电影一样,通过逐帧播放,达到动画效果。使用方式如下:
<?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=""
android:duration="" />
<item
android:drawable=""
android:duration="" />
</animation-list>
android:oneshot
:是否是一次性动画,为true则只会执行一次,false则会一直循环 。
android:drawable
:指定此帧动画所对应的图片资源。
android:duration
:此帧动画持续的时间,单位为毫秒。
将其设置为控件的资源或背景
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/anim"
android:src="@drawable/anim" /
最后通过获取控件的Drawable对象,将其转换成AnimationDrawable并调用start方法,开启动画。
private void runFrameAnimation(ImageView image) {
//AnimationDrawable anim = (AnimationDrawable) image.getBackground();
AnimationDrawable anim = (AnimationDrawable) image.getDrawable();
anim.start();
}
小技巧:当知道资源名,但不知道资源 id 时,可通过getResources().getIdentifier(资源名, 资源文件类型, 应用包名)
获得。
3、自定义Animation
如果系统提供的5种Animation满足不了你的需求,如3D效果,则可以自定义Animation。
public class Rrotate3DAnimation extends Animation {
private int mOffsetX;
private int mOffsetY;
private Camera mCamera = new Camera();
private int mRotateY = 30;
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(2000);
setFillAfter(true);
setInterpolator(new BounceInterpolator());
mOffsetX = width / 2;
mOffsetY = height / 2;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
mCamera.save();
mCamera.rotateY(mRotateY * interpolatedTime);
mCamera.getMatrix(matrix);
mCamera.restore();
matrix.preTranslate(mOffsetX, mOffsetY);
matrix.postTranslate(-mOffsetX, -mOffsetY);
}
}
三、属性动画
属性动画与视图动画的不同之处:
1、视图动画继承至Animation类,而属性动画继承至Animator类。
2、视图动画在运行过程中,并没有改变其内部属性(比如触摸区域不会随动画运动而改变)。
属性动画可以弥补视图动画的不足之处,比如按钮颜色改变动画,视图动画就难以做到。它可以添加以下监听事件:
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { }
//执行cancel()后,依旧会回调onAnimationEnd方法
@Override
public void onAnimationEnd(Animator animation) { }
@Override
public void onAnimationCancel(Animator animation) { }
@Override
public void onAnimationRepeat(Animator animation) { }
});
animator.addPauseListener(new Animator.AnimatorPauseListener() {
@Override
public void onAnimationPause(Animator animation) { }
@Override
public void onAnimationResume(Animator animation) { }
});
1、ValueAnimator
ValueAnimator是属性动画中常用的一个类,它可以不依赖控件
而执行,换句话说:它关注值的改变,而不关注控件如何去使用这个值
。
1、定义
通过资源方式去定义:
定义:res/animator
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="int"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:startOffset="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:valueType=["floatType" | "intType" | "pathType" | "colorType"]
android:interpolator=["@android:interpolator/XXX"]/>
获取:
ValueAnimator anim = (ValueAnimator) AnimationUtils.loadAnimation(context, R.animator.anim);
通过代码方式去定义:
ValueAnimator anim = ValueAnimator.ofXXX();
anim.setDuration(1000);
anim.setRepeatCount(1);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.setStartDelay(100);
anim.setInterpolator(new LinearInterpolator());
2、使用
由于ValueAnimator只负责对指定值区间进行动画运算,而不会去执行具体的动画,所有我们需要对运算过程进行监听,然后自己更新控件的属性,已达到动画的效果。
private void runValueAnimator(ValueAnimator anim) {
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 如果是单个属性动画
XXX value = (XXX) animation.getAnimatedValue();
// 如果是通过PropertyValuesHolder创建的多个属性的动画
XXX value = (XXX) animation.getAnimatedValue("propertyName");
Log.d("TAG", "value = " + value);
}
});
anim.start();
}
2、ObjectAnimator
由于ValueAnimator只能对动画的数值进行计算,如果要操作具体的控件,就需要使用监听动画过程,相比补间动画烦琐很多。而ObjectAnimator正是用来弥补这个缺点的。
ObjectAnimator继承至ValueAnimator,它通过反射
来实现对控件属性的改变:
通过资源方式去定义:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="int"
android:propertyName="string"
android:repeatCount="int"
android:repeatMode=["repeat" | "reverse"]
android:startOffset="int"
android:valueFrom="float | int | color"
android:valueTo="float | int | color"
android:valueType=["floatType" | "intType" | "pathType" | "colorType"]
android:interpolator=["@android:interpolator/XXX"]/>
获取并使用:
private void runObjectAnimator(Context context, @AnimatorRes int res, View target) {
ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(context, res);
anim.setTarget(target);
anim.start();
}
通过代码方式去定义并使用:
ObjectAnimator anim = ObjectAnimator.ofXXX(view, "属性", ......);
anim.setRepeatCount(1);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.setStartDelay(100);
anim.setInterpolator(new LinearInterpolator());
animator.start();
ObjectAnimator是通过
反射
去调用属性的get/set
方法,来完成动画效果。
当且仅当动画只有一个过渡值时,系统才会调用对应属性的get
函数来得到动画的初始值。
当不存在get
函数时,则会取动画参数类型的默认值
作为初始值,如果无默认值,将会抛出异常。
3、AnimatorSet
ValueAnimator和ObjectAnimator只能实现一个动画,假如我们需要运行组合动画,那该怎么办?
这就需要用到AnimatorSet了。
通过资源方式去定义:注意:set只能包含objectAnimator标签,不能包含animator标签
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering=["together" | "sequentially"]>
<objectAnimator
......./>
<objectAnimator
......./>
</set>
调用:
private void runAnimatorSet(Context context, @AnimatorRes int res, View target) {
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(context, res);
anim.setTarget(target);
anim.start();
}
java代码中创建:
private void runAnimatorSet() {
ObjectAnimator objectAnimator1 = ObjectAnimator.ofInt(textView1,"BackgroundColor", 0xffff00ff, 0xffffff00,0xffff00ff);
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(textView1,"translationY",0 , 300, 0);
ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(textView2, "translationY", 0, 400, 0);
AnimatorSet animatorSet = new AnimatorSet();
//一起执行
animatorSet.playTogether(objectAnimator1, objectAnimator2, objectAnimator3);
animatorSet.setDuration(1000);
animatorSet.start();
}
比较两种创建方式可以发现:通过资源文件创建的集合动画,适用于一个控件的组合动画;通过代码创建的集合动画,适用于多个控件的组合动画。
AnimatorSet包含两个函数:
//让动画一起播放
void playTogether(Animator... items)
//动画依次播放
void playSequentially(Animator... items)
//组合动画播放延时
void setStartDelay(long startDelay)
注意:这里items可以在各自的组件上执行动画。
playTogether()和playSequentially()函数在开始动画时,只是把每个控件的动画激活,至于每个控件自身的动画是否延时、是否无限循环,只与控件自身的动画设定有关,与playTogether()和playSequentially()函数无关,它们只负责激活动画。
playSequentially()函数只有在上一个控件做完动画以后,才会激活下一个控件的动画。如果上一个控件的动画是无限循环的,那么下一个控件就别指望能做动画了。
AnimatorSet可以添加监听,但监听不到repeat事件。
AnimatorSet的setDuration(long)、setInterpolator(TimeInterpolator)、setTarget(Object)都会覆盖单个ObjectAnimator的设置。
而setStartDelay()函数不会覆盖单个动画的延时,而且仅针对性地延长AnimatorSet的激活时间,单个动画所设置的setStartDelay()函数仍对单个动画起作用。
AnimatorSet 真正激活延时= AnimatorSet.startDelay +第一个动画 .startDelay
4、AnimatorSet.Builder
AnimatorSet只能实现一起动画和依次动画,但是如果有特定需求(比如:anim1和anim2同时执行,anim3在anim2执行完成后执行,anim4在anim3执行完成后执行),那AnimatorSet就难以做的,这时候就需要AnimatorSet.Builder去构建动画。
//表示要执行的动画
public Builder play(Animator anim)
//自身动画和anim动画一起执行
public Builder with(Animator anim)
//先执行完自身动画,再执行anim动画
public Builder before(Animator anim)
//anim执行完后,再执行自身动画
public Builder after(Animator anim)
//延迟delay毫秒之后执行动画
public Builder after(long delay)
5、PropertyValuesHolder与Keyframe
ValueAnimator和ObjectAnimator除了采用ofXXX()静态方法产生实例外,还可以通过ofPropertyValuesHolder()产生实例。
5.1、PropertyValuesHolder
PropertyValuesHolder保存了动画过程中所需要操作的属性和对应的值,便于在同一控件上
执行组合动画操作(当然AnimatorSet也能实现)。
private void runPropertyValuesHolder() {
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("rotation", 60f, -60f, 40f, 40f, -20f, 20f, 10f, -10f, 0f);
PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0.1f, 1f, 0.1f, 1f);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, alphaHolder);
animator.setDuration(3000);
animator.start();
}
5.2、Keyframe
Keyframe,也就是关键帧,用来表示某一时刻的控件属性的值。一个关键帧必须包含两个元素:时间点和位置。配合PropertyValuesHolder使用,对应关系为:一个ValueAnimator或ObjectAnimator可搭配多个PropertyValuesHolder,一个PropertyValuesHolder可搭配多个Keyframe。
使用Keyframe的流程:
- 生成Keyframe对象
- 利用PropertyValuesHolder.ofKeyframe()函数生成PropertyValuesHolder对象
- 利用ObjectAnimator.ofPropertyValuesHolder()函数生成对应的Animator。
四、Interpolator和Evaluator
Interpolator(插值器):
Evaluator(估值器):
1、系统自带插值器:
插值器名称 | 插值器类 | 资源ID | 解释 |
---|---|---|---|
加减速插值器 | AccelerateDecelerateInterpolator | @android:anim/ accelerate_decelerate_interpolator |
开始加速,结束减速 |
加速插值器 | AccelerateInterpolator | @android:anim/ accelerate_interpolator |
开始速率为0,一直加速 |
初始偏移插值器 | AnticipateInterpolator | @android:anim/ anticipate_interpolator |
先向后运动一段距离,再向前运动 |
初始结束偏移插值器 | AnticipateOvershootInterpolator | @android:anim/ anticipate_overshoot_interpolator |
初始偏移插值器和结束偏移插值器的合体 |
弹跳插值器 | 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 |
运动到结束位置,继续向前,再折返 |
2、自定义插值器
自定义插值器,主要通过实现TimeInterpolator接口来完成
public interface TimeInterpolator {
/**
* input参数,取值0~1,表示当前动画运行进度,0表示动画刚开始,1表示动画结束
* 返回值,表示当前实际要显示的进度,可以超出0~1的范围。小于0表示小于开始位置,大于1表示大于结束位置
*/
float getInterpolation(float input);
}
3、自定义估值器
由于Interpolator主要即使动画实际进度,与具体内容无关,当我们需要计算别的类型值时,比如Point,就需要自定义估值器了!
自定义估值器,可通过继承TypeEvaluator实现。
public class PointEvaluator implements TypeEvaluator<PointF> {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
float x = startValue.x + (endValue.x - startValue.x) * fraction;
float y = startValue.y + (endValue.y - startValue.y) * fraction;
return new PointF(x, y);
}
}
private void runAnimator() {
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), new PointF(0, 0), new PointF(200, 200))
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF point = (PointF) animation.getAnimatedValue();
Log.d("TAG", "value = " + point);
}
});
animator.setInterpolator(new LinearInterpolator());
animator.start();
}
五、ViewPropertyAnimator
由于构建一个属性动画流程比较繁琐,所以谷歌增加了ViewPropertyAnimator,这些动画依附于控件自身,通过链式调用能够轻松完成动画的创建和执行。
view.animate().rotation(720).setDuration(1000)
animate()方法会返回一个ViewPropertyAnimator对象,注意:ViewPropertyAnimator并非派生自Animator,且构建完毕后无需调用start()方法,动画会在界面刷新的时候自动启动。
由于ViewPropertyAnimator并没有像ObjectAnimator一样使用反射或JNI技术;而ViewPropertyAnimator会根据预设的每一个动画帧计算出对应的所有属性值,并设置给控件,然后调用一次invalidate()函数进行重绘,从而解决了在使用ObjectAnimator时每个属性单独计算、单独重绘的问题。使用ViewPropertyAnimator相对于ObjectAnimator和组合动画,性能有所提升。
并且要实现一个组合动画,要么采用AnimatorSet实现,要么使用PropertyValuesHolder,比较繁琐,而ViewPropertyAnimator非常方便
六、为ViewGroup内的组件添加动画
为ViewGroup内的组件添加动画,常用的有2种方法。
1、android:animateLayoutChanges属性
给ViewGroup设置该属性后,当它添加或删除子控件时,都会有默认的动画效果,但注意:该动画不能自定义。
2、LayoutTransition
强大且灵活,使用也很方便:
1、创建实例
LayoutTransition transition = new LayoutTransition();
2、创建动画并进行设置
ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotation", 0f, 90f, 0f);
transition.setAnimator(transitionType, animOut);
3、将LayoutTransition设置到ViewGroup中
viewGroup.setLayoutTransition(transition);
transitionType表示容器状态变化类型,目前系统中支持以下5种状态变化:
1、APPEARING:容器中出现一个视图。
2、DISAPPEARING:容器中消失一个视图。
3、CHANGING:布局改变导致某个视图随之改变,例如调整大小,但不包括添加或者移除视图。
4、CHANGE_APPEARING:其他视图的出现导致某个视图改变。
5、CHANGE_DISAPPEARING:其他视图的消失导致某个视图改变。
//初始化
LayoutTransition layoutTransition = new LayoutTransition();
//统一设置LayoutTransition的动画时间, ANIMATOR_DURATION定义的常量
layoutTransition.setDuration(LayoutTransition.DISAPPEARING,ANIMATOR_DURATION);
layoutTransition.setDuration(LayoutTransition.APPEARING, ANIMATOR_DURATION);
layoutTransition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, ANIMATOR_DURATION);
layoutTransition.setDuration(LayoutTransition.CHANGE_APPEARING, ANIMATOR_DURATION);
//view 动画改变时,布局中的每个子view动画的时间间隔
layoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, 1000);
layoutTransition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 1000);
//设置APPEARING行为
ObjectAnimator appearingAnimator = ObjectAnimator.ofFloat(rootView,"scaleY", 0, 1);
appearingAnimator.setDuration(layoutTransition.getDuration(LayoutTransition.APPEARING));
layoutTransition.setAnimator(LayoutTransition.APPEARING,appearingAnimator);
//设置DISAPPEARING行为
ObjectAnimator disAppearingAnimator = ObjectAnimator.ofFloat(rootView,"scaleY", 1,0);
disAppearingAnimator.setDuration(layoutTransition.getDuration(LayoutTransition.DISAPPEARING));
layoutTransition.setAnimator(LayoutTransition.DISAPPEARING,disAppearingAnimator);
PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 0);
PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 0);
//设置CHANGE_APPEARING行为
PropertyValuesHolder animator = PropertyValuesHolder.ofFloat("rotation", 0, 90, 0);
final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
this, pvhTop, pvhBottom,pvhLeft, pvhRight, animator).
setDuration(layoutTransition.getDuration(LayoutTransition.CHANGE_APPEARING));
layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
//设置CHANGE_DISAPPEARING
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofFloat("scaleX", 1, 1.5f, 1);
final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
this, pvhTop, pvhBottom,pvhLeft, pvhRight, pvhRotation).
setDuration(layoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
rootView.setLayoutTransition(layoutTransition);
通过代码设置了4中行为动画的duration,然后分别为四种行为设置动画效果。APPEARING和DISAPPEARING比较简单,就是定义了一个ObjectAnimator对象,然后通过setAnimator()方法来设置行为的动画效果。需要特别注意的是CHANGE_APPEARING和CHANGE_DISAPPEARING行为,需要注意的有这几点:
1、预先定义了left、top、right和bottom四种动画
2、CHANGE_APPEARING和CHANGE_DISAPPEARING的自定义动画效果必须用PropertyAnimator实现
3、通过ofPropertyValuesHolder方法实例化ObjectAnimator对象时,必须至少添加left、top、right和bottom中的两种动画,建议全都添加上。如果不想left、top、right和bottom属性发生改变可以在定义的时候使用0,0或者1,1这种参数。
4、自定义的CHANGE_APPEARING和CHANGE_DISAPPEARING动画效果在构建时需要指定三个参数(如:PropertyValuesHolder.ofFloat("scaleX", 1, 1.5f, 1)),且第三个参数与第一个参数只能保持一致,特别注意的是绝对不能只设置2个参数,会使动画效果无效。这样的理论基础是:View执行CHANGE_APPEARING和CHANGE_DISAPPEARING动画效果之后,不能改变其除因为View添加和移除带来的相对位置改变之外的其他属性值。如上图当Button1被移除时,其他View会有一个scaleX的放大效果,但是当动画结束时其他View不能保持被放大的效果,scaleX必须恢复到原始属性值。