注册

View系列:动画

View Animation(视图动画)

最大的特点是:并没有改变目标实际的属性(宽高/位置等)。例如:移动后,点击原来的位置出发点击事件;移动后再旋转,还是回到原来的位置旋转。

Tween Animation(补间动画)

锚点

可以是数值、百分数、百分数p三种样式,比如50、50%、50%p。[不是只有pivotx/y才可以用这3中样式,其它变换的属性也可以]

  • 当为数值时,表示在当前View的左上角,即原点处加上50px,做为起始缩放点;
  • 如果是50%,表示在当前控件的左上角加上自己宽度的50%做为起始点;
  • 如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为起始点x轴坐标(是在目标的左上角原点加上相对于父控件宽度的距离,不是锚点在父控件的那个位置)。

fromX/toX等等类型的数据也可以用上面的3中数据 类型,只不过有的不适合。比如scale用%p就没意义了。养成好习惯,只在锚点的属性上随便用这3中类型,from/to属性分清类型用相应的数值(浮点倍数/角度...)。

从Animation继承的属性
android:duration 动画持续时间,以毫秒为单位 
android:fillAfter 如果设置为true,控件动画结束时,将保持动画最后时的状态
android:fillBefore 如果设置为true,控件动画结束时,还原到开始动画前的状态
android:fillEnabled 与android:fillBefore 效果相同,都是在动画结束时,将控件还原到初始化状态
android:repeatCount 重复次数
android:repeatMode 重复类型,有reverse和restart两个值,reverse表示倒序回放,restart表示重新放一遍,必须与repeatCount一起使用才能看到效果。因为这里的意义是重复的类型,即回放时的动作。
android:interpolator 设定插值器,其实就是指定的动作效果,比如弹跳效果等,不在这小节中讲解,后面会单独列出一单讲解。
scale
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="700"
android:fromXScale="50%" //也可以用上面的3中类型
android:fromYScale="50%"
android:toXScale="200%"
android:toYScale="200%"
android:pivotX="0.5"
android:pivotY="0.5"
android:repeatCount = "2"
android:repeatMode = "reverse"
android:fillAfter = "true"
/>

alpha
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="0.1"
android:toAlpha="1"
android:duration="1500"
android:repeatMode = "reverse"
android:repeatCount = "2"
android:fillAfter = "true"
>

</alpha>
rotate
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromDegrees="0"
android:toDegrees="270"
android:pivotX="50%"
android:pivotY="50%"
android:duration="700"
android:repeatMode = "reverse"
android:repeatCount = "3"
android:fillAfter = "true"
>

</rotate>
translate
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="700"
android:fillAfter="true"
android:fromXDelta="50"
android:fromYDelta="50%p"
android:repeatCount="3"
android:repeatMode="reverse"
android:toXDelta="70%p"
android:toYDelta="80%p">

</translate>
AnimationSet animSet = new AnimationSet(false);
Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.scale_anim); //资源文件
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotate_anim);
AlphaAnimation alphaAnim = new AlphaAnimation(0.2f, 1.0f); //代码生成
//valueType 3中类型的数据(px, 自身%, 父类%p),这里已自身为参照物。
TranslateAnimation traslateAnim = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.2f,
Animation.RELATIVE_TO_SELF, 3.0f,
Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 1.0f);
ivTarget.startAnimation(animSet);
自定义Animation
private class MoveAnimation extends Animation {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
mInterpolatedTime = interpolatedTime;
invalidate();
}
}

Frame Animation(逐帧动画)

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
//false 一直重复执行,true执行一次。
<item
android:duration="200"
android:drawable="@drawable/frame_anim_1"/>

<item
android:duration="200"
android:drawable="@drawable/frame_anim_2"/>

<item
android:duration="200"
android:drawable="@drawable/frame_anim_3"/>

<item
android:duration="200"
android:drawable="@drawable/frame_anim_4"/>

<item
android:duration="200"
android:drawable="@drawable/frame_anim_4"/>

</animation-list>
  • 需要注意的是,动画的启动需要在view和window建立连接后才可以绘制,比如上面代码是在用户触摸后启动。如果我们需要打开界面就启动动画的话,则可以在Activity的onWindowFocusChanged()方法中启动。

Property Animation(属性动画)

属性动画是指通过改变View属性来实现动画效果,包括:ValueAnimator、ObjectAnimator、TimeAnimator

ValueAnimator

该类主要针对数值进行改变,不对View进行操作

ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//拿到监听结果,自己处理。
int curValue = (int)animation.getAnimatedValue();
tvTextView.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());
}
});
animator.setInterpolator(new LinearInterpolator());
animator.start();

监听:

/**
* 监听器一:监听动画变化时的实时值
* 添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
*/

public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
/**
* 监听器二:监听动画变化时四个状态
* 添加方法为: public void addListener(AnimatorListener listener)
*/

public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}


/**
* 移除AnimatorUpdateListener
*/

void removeUpdateListener(AnimatorUpdateListener listener);
void removeAllUpdateListeners();
/**
* 移除AnimatorListener
*/

void removeListener(AnimatorListener listener);
void removeAllListeners();

ObjectAnimator

ValueAnimator只能对数值进行计算,不能直接操作View,需要我们在监听器中自己去操作控件。这样就有点麻烦了,于是Google在ValueAmimator的基础上又派生出了ObjerctAnimator类,让动画直接与控件关联起来。

 	ObjectAnimator rotateObject = ObjectAnimator.ofFloat(tvPropertyTarget, 
"Rotation",
0, 20, -20, 40, -40, 0);
rotateObject.setDuration(2000);
rotateObject.start();
setter/getter 属性名

在View中已经实现了一些属性的setter/getter方法,在构造动画时可以直接对控件使用。

  • 要使用一个属性,必须在控件中有对应的setter/getter方法,属性setter/getter方法的命名必须以驼峰方式
  • ObjectAnimator在使用该属性的时候,会把setter/getter和属性第一个字母大写转换后的字段拼接成方法名,通过反射的方式调用该方法传值。 所以,上文中"Rotation/rotation"可以首字母可以大小写都行
//1、透明度:alpha  
public void setAlpha(float alpha)

//2、旋转度数:rotation、rotationX、rotationY
public void setRotation(float rotation) //围绕Z轴旋转
public void setRotationX(float rotationX)
public void setRotationY(float rotationY)

//3、平移:translationX、translationY
public void setTranslationX(float translationX)
public void setTranslationY(float translationY)

//缩放:scaleX、scaleY
public void setScaleX(float scaleX)
public void setScaleY(float scaleY)

image-20210603130556023

自定义属性做动画
public class PointView extends View {
private float mRadius = 0;
public PointView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
}

public void setRadius(float radius){
this.mRadius = radius;
invalidate();
}

public float getRadius(){
return mRadius;
}
}

//radius属性首字母大小写无所谓,最后都是要转成大些的。
ObjectAnimator pointAnim = ObjectAnimator.ofFloat(pointPropertyAnim,
"Radius",
10, 40, 40, 80, 60, 100, 80, 120,60);
pointAnim.start();

什么时候需要用到get方法呢? 前面构造动画时传入的取值范围都是多个参数,Animator知道是从哪个值变化到哪个值。当只传入一个参数的时候,Animator怎么知道哪里是起点?这时通过get方法找到初始值。 如果没有找到get方法,会用该参数类型的默认初始值复制。如:ofInt方法传入一个值,找不到get方法时,默认给的初始值是Int类型的初始值0.

原理

image-20210603131108900ObjectAnimator的方便之处在于:

ValueAnimator只负责把数值给监听器,ObjectAnimator只负责调用set方法。至于实现,都是靠我们自己或者set中的方法。

插值器

设置动画运行过程中的进度比例,类似匀速变化、加速变化、回弹等

  • 参数input:是一个float类型,它取值范围是0到1,表示当前动画的进度,取0时表示动画刚开始,取1时表示动画结束,取0.5时表示动画中间的位置,其它类推。
  • 返回值:表示当前实际想要显示的进度。取值可以超过1也可以小于0,超过1表示已经超过目标值,小于0表示小于开始位置。(给估值器使用
  • 插值器默认每10ms刷新一次
public class PointInterpolator implements Interpolator {
/**
* input 是实际动画执行的时间比例 0~1
* newInput 你想让动画已经执行的比例 0~1。
* 注意:都是比例,而不是实际的值。
*
* setDuration(1000)情况下:前200ms走了3/4的路程比例,后800ms走了1/4的路程比例。
*/

@Override
public float getInterpolation(float input) {
if (input <= 0.2) {//后1/4的时间,输出3/4的比例
float newInput = input*4;
return newInput;
}else {//后3/4的时间,输出1/4的比例
float newInput = (float) (input - 0.2)/4 + 0.8f;
return newInput;
}
}
}

使用方式和默认插值器

在xml和代码中使用插值器,省略代码中使用方式

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
// 通过资源ID设置插值器
android:interpolator="@android:anim/overshoot_interpolator"
android:duration="3000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2"
android:toYScale="2" />

内置插值器动画展示

Android动画之Interpolator

Android动画插值器

作用资源ID对应的Java类
动画加速进行@android:anim/accelerte_interpolatorAcceleraterplator
快速完成动画,超出再回到到结束样式@android:anim/overshoot_interpolatorOvershootInterpolator
先加速再减速@android:anim/accelerate_decelerate_interpolatorAccelerateDecelerateInterpolator
先退后再加速前进@android:anim/anticipate_interpolatorAnticipateInterpolator
先退后再加速前进,超出终点后再回终点@android:anim/anticipate_overshoot_interpolatorAnticipateOvershootInterpolator
最后阶段弹球效果@android:anim/bounce_interpolatorBounceInterpolator
周期运动@android:anim/cycle_interpolatorCycleInterpolator
减速@android:anim/decelerate_interpolatorDecelerateInterpolator
匀速@android:anim/linear_interpolatorLinearInterpolator

估值器

设置 属性值 从初始值过渡到结束值 的变化具体数值

  • 参数fraction: 表示当前动画的进度(插值器返回值
  • 返回值:表示当前对应类型的取值,也就是UpdateListener接口方法中传入的值
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int radius = (int) (startValue.getRadius() +
fraction*(endValue.getRadius() - startValue.getRadius()));
return new Point(radius);
}
}

自定义插值器、估值器、属性的使用:

public void doAnimation(){
//ObjectAnimator animator = ObjectAnimator.ofInt(mView, "Radius", 20, 80);
ValueAnimator animatior = new ValueAnimator();
animatior.setObjectValues(new Point(20), new Point(80));
animatior.setInterpolator(new PointInterpolator());
animatior.setEvaluator(new PointEvaluator());

animatior.setDuration(2000);
animatior.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
animatior.start();
}

PropertyValuesHolder

它其中保存了动画过程中所需要操作的属性和对应的值

通过ObjectAnimator.ofFloat(Object target, String propertyName, float… values)构造的动画,ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态,后期的各种操作也是以PropertyValuesHolder为主。

//将需要操作的多个属性和值封装起来,一起放到ObjectAnimator中,相当于set操作。
PropertyValuesHolder rotateHolder = PropertyValuesHolder.ofFloat("Rotation", 0, 360, 0);
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 1, 2, 1,2,1);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 1, 2, 1,2,1);
ObjectAnimator objectAnim = ObjectAnimator.ofPropertyValuesHolder(ivHolderTarget,
rotateHolder,
scaleXHolder,
scaleYHolder);
objectAnim.setDuration(2000);
objectAnim.setInterpolator(new LinearInterpolator());
objectAnim.start();

KeyFrame(主要帧)

如果想要更精确的控制动画,想要控制整个动画过程的某个点或某个时段达到的值,可以通过自定义插值器或估值器来实现,但是那样又有些费事,并且不容易计算这段时间内值的变化。 这时可以用Keyframe来实现,即设置好某个时间点和值,系统会自动计算该点和上个点之间,值的变化。

/***
* 实现左右摇晃,每边最后有震动的效果。
* 摇晃角度100度:0.2f/0.2~0.4/0.4~0.5,分别设置不同的角度和加速器。
* 每个比例点达到哪个角度,这在估值器中也能做到,但是需要自己算每个时间段内值的变化过程。
* KeyFrame可以设置好 比例-值 以后,系统根据默认或设置的加速器改变:上个点和该点内的值如何变换。
* 这样可以更精确的控制动画过程,同时也不用自己费劲去计算值因该如何变换。
*/

Keyframe kfRotation1 = Keyframe.ofFloat(0, 0); //第一帧,如果没有该帧,会直接跳到第二帧开始动画。
//第二帧 0.2f时达到60度,线性加速应该作用于从0~0.2f的这段时间,而不是作用在0.2~0.4f这段。因为已经定好60度是要的结果了,那么实现就应该在前面这段。
Keyframe kfRotation2 = Keyframe.ofFloat(0.2f, 60);
kfRotation2.setInterpolator(new LinearInterpolator());
Keyframe kfRotation3 = Keyframe.ofFloat(0.4f, 100);
kfRotation3.setInterpolator(new BounceInterpolator());
Keyframe kfRotation4 = Keyframe.ofFloat(0.5f, 0);
kfRotation4.setInterpolator(new LinearInterpolator()); //最少有2帧
Keyframe kfRotation5 = Keyframe.ofFloat(0.7f, -60);
kfRotation5.setInterpolator(new LinearInterpolator());
Keyframe kfRotation6 = Keyframe.ofFloat(0.9f, -100);
kfRotation6.setInterpolator(new BounceInterpolator());
Keyframe kfRotation7 = Keyframe.ofFloat(1f, 0);//最后一帧,如果没有该帧,会以最后一个KeyFrame做结尾
kfRotation7.setInterpolator(new LinearInterpolator());

Keyframe kfScaleX1 = Keyframe.ofFloat(0, 1);
Keyframe kfScaleX2 = Keyframe.ofFloat(0.01f,2.8f);
Keyframe kfScaleX3 = Keyframe.ofFloat(0.8f,2.0f);
Keyframe kfScaleX4 = Keyframe.ofFloat(1f,1.0f);

Keyframe kfScaleY1 = Keyframe.ofFloat(0, 1);
Keyframe kfScaleY2 = Keyframe.ofFloat(0.01f,2.8f);
Keyframe kfScaleY4 = Keyframe.ofFloat(0.8f,2.0f);
Keyframe kfScaleY5 = Keyframe.ofFloat(1f,1.0f);

PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofKeyframe("rotation", kfRotation1, kfRotation2, kfRotation3,kfRotation4, kfRotation5, kfRotation6, kfRotation7);
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofKeyframe("scaleX", kfScaleX1, kfScaleX2, kfScaleX3, kfScaleX4);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofKeyframe("scaleY", kfScaleY1, kfScaleY2, kfScaleY4, kfScaleY5);


ObjectAnimator objectAnim = ObjectAnimator.ofPropertyValuesHolder(ivHolderTarget,
rotationHolder,
scaleXHolder,
scaleYHolder);
objectAnim.setDuration(1500);

AnimatorSet

AnimatorSet针对ValueAnimator和ObjectAnimator都是适用的,但一般而言,我们不会用到ValueAnimator的组合动画。

playTogether/playSequentially

无论是playTogether还是playSequentially方法,它们只是,仅仅是激活了动画什么时候开始,并不参与动画的具体操作。 例如:如果是playTogether,它只负责这个动画什么时候一起激活,至于anim1/anim2/anim3...哪个马上开始,哪个有延迟,哪个会无限重复,set都不管,只负责一起激活。 如果是playSequentially,它只负责什么时候开始激活第一个(因为有可能set设置延迟),并在第一个动画结束的时候,激活第二个,以此类推。

ObjectAnimator anim1 = ObjectAnimator.ofInt(mTv1, "BackgroundColor",  0xffff00ff, 0xffffff00, 0xffff00ff);

ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
anima2.setStartDelay(2000);
anima2.setRepeatCount(ValueAnimator.INFINITE);

ObjectAnimator anim3 = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
anim3.setStartDelay(2000);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(anim1, anim2, anim3);//playSequentially(按次序播放)
animatorSet.setDuration(2000);
animatorSet.setStartDelay(2000);
animatorSet.start();
play(x).with(x)
  • play(anim1).with(anim2):2000ms后set开始激活动画,anim1启动,再过2000ms后anim2启动。
  • play(anim2).with(anim1):2000ms后set开始激活动画,再过2000ms后启动anim2,并且启动anim1.
set监听

addListener监听的是AnimatorSet的start/end/cacle/repeat。不会监听anim1/anim2的动画状态的。

联合动画XML实现
单独设置和Set中设置
  • 以set为准:
//设置单次动画时长
public AnimatorSet setDuration(long duration);
//设置加速器
public void setInterpolator(TimeInterpolator interpolator)
//设置ObjectAnimator动画目标控件
public void setTarget(Object target)
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
anim1.setDuration(500000000);

ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
anim2.setDuration(3000);//每次3000,而不是3次3000ms
anim2.setRepeatCount(3);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv2TranslateY).with(tv1TranslateY);
animatorSet.setDuration(2000);//以Set为准
animatorSet.start();

setDuration()是指单个动画的时间,并不是指总共做完这个动画过程的时间。比如:anim2中设置了3000ms,重复3次。是指每次3000ms,不是3次3000ms。
另外animatorSet设置了时间以后,anim1/anim2虽然也设置了,但是这时以set为准。即,anim1/anim2的单个动画时间为2000ms。只不过anim2是每次2000ms,重复3次,共6000ms。

  • 不以set为准:setStartDelay
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
anim2.setStartDelay(2000);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new Animator.AnimatorListener(){...});
animatorSet.play(anim1).with(anim2);
animatorSet.setStartDelay(3000);//指的是Set的激活延迟,而不是动画延迟
animatorSet.setDuration(2000);
animatorSet.start();

setStartDelay不会覆盖单个动画的该方法,只会延长set的激活时间。所以,上面代码中动画的启动过程是:3000ms后set开始激活动画,anim1启动,再过2000ms后anim2启动。

ViewPropertyAnimator

属性动画已不再是针对于View而进行设计的了,而是一种对数值不断操作的过程,我们将属性动画对数值的操作过程设置到指定对象的属性上来,从而形成一种动画的效果。 虽然属性动画给我们提供了ValueAnimator类和ObjectAnimator类,在正常情况下,基本都能满足我们对动画操作的需求,但ValueAnimator类和ObjectAnimator类本身并不是针对View对象的而设计的,而我们在大多数情况下主要都还是对View进行动画操作的。

因此Google官方在Android 3.1系统中补充了ViewPropertyAnimator类,这个类便是专门为View动画而设计的。

  • 专门针对View对象动画而操作的类
  • 更简洁的链式调用设置多个属性动画,这些动画可以同时进行
  • 拥有更好的性能,多个属性动画是一次同时变化,只执行一次UI刷新(也就是只调用一次invalidate,而n个ObjectAnimator就会进行n次属性变化,就有n次invalidate)
  • 每个属性提供两种类型方法设置。scaleX()/scaleXBy()
  • 该类只能通过View的animate()获取其实例对象的引用
  • 自动调用start
btn.animate()
.alpha(0.5f)
.rotation(360)
.scaleX(1.5f).scaleY(1.5f)
.translationX(50).translationY(50)
.setDuration(5000);

image-20210604100429893

layoutAnimation

布局动画,api1,该属性只对创建ViewGroup时,对其子View有动画。已经创建过了该ViewGroup的话,再向其添加子View不会有动画。

  • onCreat创建加载布局时:
//anim -> rotate_anim.xml
<?xml version="1.0" encoding="utf-8"?>


// layoutAnimation标签
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="1"
android:animationOrder="normal"
android:animation="@anim/rotate_anim">

</layoutAnimation>

//定义在LinearLayout上,在该界面生成时,Button显示动画。但是,后面在LinearLayout中添加Button时,不再有动画。
<LinearLayout
android:id="@+id/ll_tips_target_animation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/layout_animation"
android:tag="在xml中设置的layoutAnimation"
android:orientation="vertical">

<Button
style="@style/base_button"
android:text="ViewGroup初始化时,子View有动画"/>

</LinearLayout>
  • 代码中动态设置layoutAnimation,添加View
        //代码生成ViewGroup
LinearLayout linear = new LinearLayout(this);

Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate_anim);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(1);
//动画模式,正常/倒叙/随机
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
//设置layoutAnimation
linear.setLayoutAnimation(controller);
linear.setLayoutAnimationListener(new Animation.AnimationListener() {

});

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
linear.setLayoutParams(params);
//给该ViewGroup添加子View,子View会有动画。
addVeiw(linear,null);
llTargetAnim.addView(linear, 0);

使用场景:

该属性只有ViewGroup创建的时候才能有效果,所以不适合动态添加子View的操作显示动画。一般做界面显示的时候的入场动画,比如打开一个界面,多个固定不变的item有动画的显示出来。(进入设置界面,信息展示界面)。

android:animateLayoutChanges属性:

Api11后,添加/移除子View时所带的默认动画,在Xml中设置。不能自定义动画,只能使用默认的。所以,使用范围较小。

<LinearLayout
android:animateLayoutChanges="true"
/>

image


0 个评论

要回复文章请先登录注册