一、Android 动画知识总结(View动画)

动画的意义:
视觉效果
引导用户

一、简介

Android动画可分为View Animation(视图动画)和Property Animation(属性动画)。
View Animation包括Tween Animation(补间动画)和Frame Animation(帧动画)。
Property Animation包括ValueAnimatorObjectAnimator

在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的流程:

  1. 生成Keyframe对象
  2. 利用PropertyValuesHolder.ofKeyframe()函数生成PropertyValuesHolder对象
  3. 利用ObjectAnimator.ofPropertyValuesHolder()函数生成对应的Animator。

四、Interpolator和Evaluator

Interpolator(插值器):
Evaluator(估值器):

image.png

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必须恢复到原始属性值。


七、DynamicAnimation

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容

  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,705评论 0 10
  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 6,150评论 1 38
  • 转载一篇高质量博文,原地址请戳这里转载下来方便今后查看。1 背景不能只分析源码呀,分析的同时也要整理归纳基础知识,...
    Elder阅读 1,942评论 0 24
  • Animation Animation类是所有动画(scale、alpha、translate、rotate)的基...
    四月一号阅读 1,916评论 0 10
  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    lisx_阅读 964评论 0 0