Android自定义View - 动画

747 阅读11分钟

动画分类

  • View Animation(视图动画) Tween Animation(补间动画) Frame Animation(逐帧动画)
  • Property Animation(属性动画) Value Animation Object Animation

View Animation

视图动画分为

  • alpha
  • scale
  • translate
  • rotate
  • set

xml配置动画

<scale
    android:fromXScale = "0.0"
    android:toXScale = "1.4"
    android:fromYScale = "0.0"
    android:toYScale = "1.4"
    android:duration = "700"/>

动画文件一半放在res/anim或者res/drawable下

调用的时候:

Animation animation = AnimationUtils.loadAnimation(context,R.anim.animation)
view.startAnimation(animation)

scale

android:pivotX|pivoY:缩放起始点X轴坐标,可以是数值,百分数,百分数P三个方式

  • 数值:如果是50,表示视图左上角加上50px
  • 百分数:50%表示当前控件左上角加上自己宽度的50%
  • 百分数P:50%p表示当前控件左上角加上父控件宽度的50%

Animation 继承属性

Animation是所有动画的基类,他自己有一些共用的动画属性,所有子类都可以使用。

1649805745899.jpg

alpha标签

  • android:fromAplha:动画开始时候的透明度,0.0为完全透明,1.0为不透明
  • android:toAlpha:动画结束时候的透明度,0.0为完全透明,1.0为不透明

rotate

  • android:fromDegress:开始旋转时的角度,正值代表顺时针方向
  • android:toDegress:结束旋转时的角度,正值代表顺时针方向
  • android:pivotX|pivotY:旋转点的坐标,可以是数值,百分数,百分数P三个方式,默认是控件原点。

translate标签

  • android:fromXDelta:起始点X轴坐标,可以是数值,百分数,百分数P三个方式
  • android:fromYDelta:起始点Y轴坐标
  • android:toXDelta:终点X轴坐标
  • android:toYDelta:终点Y轴坐标

set标签

set标签是容器标签,用于定义动画集,可以把前面介绍的动画组合起来,完成一个共同的动画。

set标签中repeateCount属性无效,必须对每个动画单独设置。

View Animation代码实现

在xml中的方法在代码中都有对应的方法。在设置重复模式的时候,传入的值变成了Animation.RESTART,Animation,REVERSE。当需要表示无限循环的时候,传入的值为Animation.INFINITE.

ScaleAnimation

构造函数:

public ScaleAnimation(Context context, AttributeSet attrs)
public ScaleAnimation(float fromX, float toX, float fromY, float toY)
public ScaleAnimation(float fromX, float toX, float fromY, float toY,float pivotX,float pivotY)
public ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

四个构造函数中,第一个构造函数是从本地XML加载动画的时候用。其他三个是用代码实现的时候用。第四个构造函数中有pivotXType和pivotYType两个入参。分别对应的是数值,百分数,百分数P。如果是数值,就传入Animation.ABSOLUTE,如果是百分数,就传入Animation.RELATIVE_TO_SELF,如果是百分数p,就传入Animation.RELATIVE_TO_PARENT。

AlphaAnimation,RotateAnimation,TranslateAnimation和ScaleAnimation差不多,这里就不说了。

AnimationSet

public AnimationSet(boolean shareInterpolator)

经常使用的构造函数中有一个参数,如果传true,AnimationSet会定义一个插值器,里面的所有动画都会使用该插值器。

增加动画的函数为:

public void addAnimation(Animation a)

插值器

插值器是Interpolator类,他是一个接口,通过实现这个接口可以自定义动画的变化速率,系统有一个默认实现类。

1649807427384.jpg 插值器可以通过android:interpolator在xml中设置,也可以通过Animation的setInterpolator方法从代码中设置。

使用插值器:

Animation anim = AnimationUtils.loadAnimation();
//设置插值器
anim.setInterpolator(new AccelerateDecelerateInterpolator())
view.startAnimation(anim)

AnticipateInterpolator

动画图像:

1649893568139.jpg 先往回,然后再开始动画。还有一个构造函数:

public AnticipateInterpolator(float rension)

rension是动画回拉的力度,默认是2,如果设置的越大,动画最开始往回就越多,后面速度就越快。 1649893568135.jpg

OvershootInterpolator

和AnticipateInterpolator相反,结束的时候,超出去,然后再回来。

同样构造函数可以传入tension

AnticipateOvershootInterpolator

是AnticipateInterpolator和OvershootInterpolator的结合体。

同样有一个rension入参。

还有一个extraTension,表示额外张力。

逐帧动画

一帧一帧的播放图片动画

主要会用到AnimationDrawable

可以通过xml或者代码设置

属性动画

属性动画在android.animation下,而视图动画在android.view.animation下

视图动画从名字可以看出是作用于View的,而属性动画可以作用于单独的一个属性,比如控件颜色,他可以将控件颜色从一个颜色变到另一个颜色,这个视图是无法完成的。

视图动画使用的类都是xxxAnimation,属性动画使用的类都是xxxAnimator

ValueAnimator

ValueAnimator常用的方法有ofInt,ofFloat。他们都可以传入可变长度的参数,加入传入10,100,20。那么动画之行的时候就会返回10到100,然后再到20

setDuration(long duration)设置的是动画的时间

ValueAnimation在无限循环动画的时候,如果你退出Activity,那么需要调用cancel方法结束动画,否则会一直执行动画导致内存泄漏。

ValueAnimator有两个监听器,一个是AnimatorUpdateListener,这个是用来监听变化值的,添加的方法是addUpdateListener。一个是AnimatorListener,用来监听动画的开始结束等信息,添加方法是addListener。 移除两个监听器的方法:

removeUpdateListener(updateListener AnimatorUpdateListener)
removeUpdateListeners()

removeListener(listener AnimatorListener)
removeListeners()

在移除AnimatorListener后动画依然执行。

setStartDelay(long startDelay)延迟多久开始动画

clone()克隆一个ValueAnimator,包括监听器和设置。对原来ValueAnimator的任何操作不会影响到克隆出来的ValueAnimator。

自定义插值器与Evaluator

自定义插值起

自定义插值器

public interface TimeInterpolator{
    float getInterpolation(float input);
}

自定义需要实现TimeInterpolator,TimeInterpolator中只有一个getInterpolation方法。

input表示当前动画的进度,为0表示动画刚刚开始,为1表示动画完成。返回值表示实际需要显示的目标值,可以大于1和小于0.

input只和时间有关,加入设置了动画时间是1000ms,那么在1000ms之间input返回就是0到1。

Evaluator

1650324741718.jpg 我们从图中可以看出Evalotor是一个转换器,讲小数进度转换为数值进度回调出去。

Evaluator很多种,当我们使用ofInt的时候,使用的是IntEvaluator,当我们使用ofFloat的时候,使用的是FloatEvaluator。我们使用这两个方法的时候,系统会自动为我们设置Evaluator,所以我们不用自己设置。

public class IntEvaluator implements TypeEvaluator<Integer> {

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

fraction就是返回的动画进度值,0-1。

startValue和endValue分别对应的是动画设置的起始和结束的值。

返回值就是进度对应的具体值。

使用Evaluator的时候我们调用:animator.setEvaluator方法可以设置。

ArgbEvaluator

这个ArgbEvaluator是用来颜色转换的

ValueAnimator animator = ValueAnimator.ofInt(0xffffff00,0xff0000ff)
animator.setEvaluator(new ArgbEvaluator())

在动画监听中就会返回颜色的变化。这里必须使用ofInt方法来定义颜色的值,值必须包含argb4个值。

ValueAnimator ofObject()

ValueAnimator还有一个ofObject方法,该方法可以传入任何对象。

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setObjectValues(values);
    anim.setEvaluator(evaluator);
    return anim;
}

方法需要传入两个参数,一个是自定义的Evaluator,一个是可变长度的参数。我们将在Evaluator中自己写逻辑。

ObjectAnimator

ObjectAnimator派生自ValueAnimator

public final class ObjectAnimator extends ValueAnimator

在ofInt,ofFloat方法上也做了修改

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)

我们可以看到现在需要传入2个参数和一个可变参数

  • 第一个参数用于指定动画要操作哪个控件
  • 第二个参数是指定动画要操作控件的哪个属性
  • 第三个参数值属性如何变化

操作的属性有alpha,rotation,translation,scale。

在改变控件对应的属性的时候,都是寻找控件中相关的set方法来设置的属性。加入我们修改控件的alpha,那么就会调用控件的setAlpha方法来设置alpha的值。

1650412238103.jpg

旋转

  • setRotationX() 围绕X轴旋转
  • setRotationY() 围绕Y轴旋转
  • setRotation() 围绕Z轴旋转

平移

  • setTranslationX X轴平移
  • setTranslationY Y轴平移

缩放

  • setScaleX X轴缩放
  • setScaleY Y轴缩放

在我们使用ObjectAnimator的时候系统会自动为我们取调用控件的setXXX方法,这里用的是反射的方式来调用的,加入我们的ObjectAnimator传入了scale,那么系统就会自动去找该控件的setScale方法,通过反射获取到方法后再调用,setScale的具体实现就在控件中了。如果反射没有找到方法,控件将不会有变化。在反射方法的时候参数也是根据ObjectAnimator传入的参数来的,如果使用ofFloat,那么就需要找setScale(float xxx)这样的函数。

自定义属性

ObjectAnimator中我们也可以自定义属性,现在的属性有alpha之类的,我们可以定义一个自己的,比如drag,那么在执行动画的时候,如果我们的自定义View中有setDrag方法,那么就会执行这个方法。

get方法的调用

当我们调用ObjectAnimator的时候一般会传入多个参数,这样动画会从参数a到参数b等。但是当我们只传入一个参数的时候,动画不知道起始参数是什么,就会调用对应属性的get方法去获取控件目前的参数,然后变化到我们传入的参数。

加入我们使用scale属性,只传入了6,这个时候,系统就会调用控件的getScale方法来获取控件目前的值,然后变化到6。如果控件中没有对应的get方法,那么就会默认起始值为0,就会从0变化到6。

当我们使用ofFloat和ofInt的时候,get方法没有,默认值都是0。但是当我们使用ofObject的时候,没有get方法,没办法获取到初始值,因为传入的参数不确定,所以就会报错。

ObjectAnimator的其他属性和ValueAnimator差不多,这里就不多说了。

IMG_20220421_080036.jpg

IMG_20220421_080119.jpg

组合动画 AnimatorSet

AnimatorSet我们主要还是针对ObjectAnimator来使用

AnimatorSet有两个函数:

  • playSequentially 所有动画依次播放,如果上一个动画没有执行完成,不会执行下一个动画,每一个动画的延时也算动画没有执行完成。
  • playTogethre 所有动画同时播放,不管其中动画有没有延时。

AnimatorSet.Builder

AnimatorSet前面介绍的两个方法只能依次或者同时进行动画,加入我们有这样一个需求,先执行动画A,然后同时执行动画B,C,他们就不能实现了。

要使用AnimatorSet.Buidler,我们需要先生成AnimatorSet,然后调用play方法,传入一个Animator,该方法会返回一个Animator.Buidler。

Animator.Buidler有很多方法。他们都是针对play方法设置的动画。

和前面的动画一起播放

public Builder with(Animator anim)

先执行这个动画,在执行前面的动画

public Builder before(Animator anim)

前面动画执行完成后再执行

public Builder after(Animator anim)

延迟n毫秒后执行动画

public Builder after(long delay)

AnimatorSet也可以添加AnimatorListener监听器,这个监听器是用来监听AnimatorSet的状态,并不是添加的动画的状态,加入动画设置了无限循环,AnimatorSet还是只会回调一次onAnimatorStart,并不会回调onAnimatorRepeat。

AnimatorSet还有其他方法:

public void setTarget(Object target)
public void setInterpolator(TimeInterpolator interpolator) 
public AnimatorSet setDuration(long duration)

如果没有设置,则会已Animator设置的为准,如果设置了,就会覆盖Animator中设置的参数。

setTarget是用来设置目标控件的,设置过后,单个Animator设置的执行动画的控件就没有效果了。

前面的方法会覆盖Animator中的设置,但是setStartDelay并不会,它只会针对AnimatorSet启动的时间进行延迟。

public void setStartDelay(long startDelay)

AnimatorSet动画执行的时机是自身设置的延时+第一个动画设置的延时。加入自己设置2000ms延时,第一个动画设置2000ms延时,第二个动画用with设置和第一个动画一起执行,并且第二个动画没有设置延迟,那么都会在4000ms过后执行。

加入第二个动画也设置了延迟2000ms,那么第二个动画会在第一个动画开始后,再等2000ms才开始执行。

不过在android 6 以后的版本中AnimatorSet动画执行的时机已经改成了自身设置的延时,和第一个动画无关。

Animator XML实现

Animator也可以用XML来实现,具体实现就不说了,在完成XML后可以调用AnimatorInflater.loadAnimator方法来加载动画,在调用start方法就可以执行动画。

:对应ValueAnimator

:对应ObjectAnimator

:对应AnimatorSet

1650585409052.jpg

1650585409048.jpg

1650585409043.jpg