Android - 动画

文章详细介绍了Android中的属性动画和视图动画,包括补间动画和帧动画的概念,以及属性动画中的插值器和类型估值器。属性动画如ViewPropertyAnimator和ObjectAnimator的使用方法,以及如何设置动画监听和控制动画行为。同时,提到了视图动画的效果和配置,如透明度、缩放、平移和旋转,以及帧动画的配置和使用。

官方页面:动画简介

官方页面:属性动画概览

官方页面:使用动画显示或隐藏视图

一、概念

需要对一个非 View 对象进行操作(如Point对象),而且超出移动、缩放、旋转、透明度的效果需求(如对背景色动态改变),且需要发生真实的改变(补间动画将按钮移到右下角,但点击左上角才会触发),就需要使用属性动画了。

Android 3.0 之前

传统动画(淘汰别用了)

视图动画(补间动画)ViewAnimation(Tween):作用对象是 View。只是视觉效果上的改变,并没有修改控件的真实的位置和属性。基于 Framework 的绘制转变。

帧动画 DrawableAnimation(Frame):依次播放动画过程中每帧对应的静态图片。

Android 3.0 之后

属性动画 PeopertyAnimation:View根据执行的动画发生真实的改变,通过不断修改目标的属性值实现。
物理动画 DynamicAnimation:使用插值器也能弹来弹去,但轨迹并不符合物理学上的弹跳效果,新的API使用起来也会更加简单。它有两个子类分别是 SpringAnimation(弹簧动画)抛物线动画FlingAnimation(投掷动画)

1.1 插值器 Interpolator

即数度模型,作用是控制动画的变化速率,算法对应不同时间点动画完成度的百分比(根据时间流失的百分比来计算属性值改变的百分比),例如实现非线性运动的动画效果(动画改变的速率不是一成不变的,像加速运动以及减速运动)。

对于基于物理特性的动画(加速曲线)需要使用 SpringAnimation(插值器实现起来简单但不符合物理轨迹)。

LinearInterpolator匀速。
AccelerateInterpolator持续加速(构造中可调节变速系数)。主要用在出场效果中。
AccelerateDecelerateInterpolator先加速再减速。默认效果,最符合现实物理模型。
DecelerateInterpolator持续减速到0。主要用于入场效果。
AnticipateInterpolator先反效果一点点(平移就是先回拉,放大就是先缩小),再进行正常动画轨迹。
OvershottInterpolator超过目标值一些再弹回来。
AnticipateOvershootInterpolator两种效果的结合体,先反效果一点点,最后超过目标值一些再回弹。
BounceInterpolator在目标值处弹跳几下。
CycleInterpolator自定义动画循环播放特定次数。
PathInterpolator自定义任何想要的速度模型(动画完成度 ÷ 时间完成度)。使用一个 Path 对象来绘制出想要的曲线(y 为动画完成度,x 为时间完成度)。

FastOutLinearInInterpolator

持续加速(贝塞尔曲线,初始加速更快,肉眼并不明显)。
FastOutSlowInInterpolator先加速再减速(贝塞尔曲线,加速减速更快)。
LinearOutSlowInInterpolator持续减速(初始速度更快)。

三、属性动画 PeopertyAnimation

3.1 ViewPropertyAnimator

并不是在3.0引入的,而是在3.1作为补充。属性动画不再仅针对于 View 而设计的了,但多数情况下还是对 View 进行动画操作,因此提供了更便捷的用法。

public ViewPropertyAnimator animate()

通过 view.animate() 返回一个 ViewPropertyAnimator 对象,设置效果后会自动执行,局限性在于只能调用以下方法设置属性动画。

3.1.1 动画模式 

  • 这些方法调用就会执行动画,在播放中途再次调用会先停留在原处,等进度赶上了再继续播放。当动画播放完毕后,不带by的方法再次调用不会执行,任何时候调用 start() 都无效果,调用 cancel() 会打断动画并会停留在原处,再次这些方法等进度赶上了再继续播放。
  • 默认动画时长0.3s,通过 setDuration() 自定义。
  • 默认速度模型开始加速结束减速(AccelerateDecelerateInterpolator),通过 setInterpolator() 自定义。

textview.animate()
    .x(580).y(580)
    .setDuration(5000)
    .setInterpolator(new BounceInterpolator());

3.1.2 动画监听

设置监听

public @NonNull ViewPropertyAnimator setListener(@Nullable Animator.AnimatorListener listener)

重写onAnimationStart动画开始、onAnimationEnd动画结束、onAnimationCancel动画取消(对应调用了cancel时)、onAnimationRepeat动画重复(对应通过repeat播放时)。

更新监听

public @NonNull ViewPropertyAnimator setUpdateListener(@Nullable ValueAnimator.AnimatorUpdateListener listener)

重写onAnimationUpdate每当属性值更新时,参数ValueAnimator可以获取当前动画完成度、属性值等。

一次性监听

public @NonNull ViewPropertyAnimator withStartAction(Runnable runnable) 

public @NonNull ViewPropertyAnimator withEndAction(Runnable runnable) 

重用ViewPropertyAnimator 执行别的动画也不会再执行,动画被调用cancel取消时不会再执行。

3.3 ObjectAnimator

属性动画的运行机制是通过不断地对值进行操作来实现的,只需要将初始值和结束值提供给 ValueAnimator 并且告诉它动画所需运行的时长,就会自动计算出从初始值平滑地过渡到结束值这样的效果(通过TypeEvaluator,这里是一个FloatEvaluator)。它还负责动画的播放次数、播放模式、以及对动画设置监听器等。ObjectAnimator 就是继承了它。

ofFloat(Object target, String propertyName, float... values)
ofInt(Object target, String propertyName, int... values)
ofArgb(Object target, String propertyName, int... values)
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
ofMultiFloat(Object target, String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, T... values)
ofMultiInt(Object target, String propertyName, TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, T... values)

通过 ObjectAnimator.ofXXX() 创建对象,XXX具体选什么类型根据该属性设置方法的参数类型决定。target是目标View,propertyName是View的哪个属性(不是找到该名称的属性去设置,而是找到对应的 get/set 方法进行调用,例如 TextView 中并没有 alpha 属性,传入 alpha 就是调用的 setAlpha() 和 getAlpha(),找不到IDE会提示。若是自定义 View 就需要注意提供属性的 get/set 方法(说的就是JAVA写法),并在最后一行调用 invalidate() 触发重绘),values是目标值(填一个就是目标值,填两个就是起始值和目标值,填多个就是增加中间转接点值。若是自定义View,只填一个值的情况就需要该属性提供getter方法,会获取当前值当作起始值)。调用 start() 才会执行动画。

class MyView2 : View {
    var progress = 0F
    set(value) {
        field = value
        invalidate()    //重写getter触发重绘即调用onDraw()
    }
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawArc(...)
    }
}

UI {
    val animator = ObjectAnimator.ofFloat(myView, "progress", 0, 65)
    animator.start()
}

3.2.1 动画集合

after(Animator anim)将现有动画插入到传入的动画之后执行
after(long delay)将现有动画延迟指定毫秒后执行
before(Animator anim)将现有动画插入到传入的动画之前执行
with(Animator anim)将现有动画和传入的动画同时执行
//先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作
ObjectAnimator moveIn = 0bjectAnimator.ofFloat(textview, "translationX",-500f,0f);
ObjectAnimator rotate = 0bjectAnimator.ofFloat(textview, "rotation", af, 360f);
ObjectAnimator fadeInOut = 0bjectAnimator.ofFloat(textview, "alpha", 1f, af, 1f);
Animatorset animset =new Animatorset();
animset.play(rotate).with(fadeInOut).after(moveIn);
animset.setDuration(5000);
animset.start();

3.2.2 动画监听

设置监听

public void addListener(AnimatorListener listener)

重写onAnimationStart动画开始、onAnimationEnd动画结束、onAnimationCancel动画取消(对应调用了cancel时)、onAnimationRepeat动画重复(对应通过repeat播放时)。如果不想要这么多监听,可以传入 AnimatorListenerAdapter 对象,它对以上方法都做了空实现,选择重写自己需要的就行。

更新监听

public void addUpdateListener(AnimatorUpdateListener listener) 

重写onAnimationUpdate每当属性值更新时,参数ValueAnimator可以获取当前动画完成度、属性值等。

暂停监听public void addPauseListener(AnimatorPauseListener listener)
var boolean = false
onCreate {
    //对View启用动画
    val animate = imagineView.animate().apply {
        //动画类型不能写在里面因为调用就会执行
        duration = 2000 //设置动画时长
        interpolator = AccelerateDecelerateInterpolator()   //设置速度模型
        startDelay = 2000   //延迟播放
        setListener(object : Animator.AnimatorListener{
            override fun onAnimationStart(animation: Animator) { }  //动画开始
            override fun onAnimationEnd(animation: Animator) { }    //动画结束
            override fun onAnimationCancel(animation: Animator) { } //动画取消
            override fun onAnimationRepeat(animation: Animator) { } //动画重复
        })
        setUpdateListener { valueAnimator -> } //动画更新
        withStartAction { Runnable {  } }  //动画开始
        withEndAction { Runnable {  } }    //动画结束
    }
    button1.setOnClickListener {
        //中途再次点击会停留在原地等进度赶上了继续播放
        animate.translationXBy(200F)    //每次点击都有位移效果
        animate.rotation(180F)  //只执行一次,播放完后再次点击无旋转效果

    }
    button2.setOnClickListener {
        //每次点击在strat和cancel切换,测试结果:start()任何时候都无效,cancel()打断动画停在原地
        if (boolean) animate.start() else animate.cancel()
        boolean = !boolean  
    }
}

3.3 TypeEvaluator 类型估值器

就是告诉动画系统如何从初始值过度到结束值。

3.3.1 浮点或整数型计算 

//系统内置的FloatEvaluator
//用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。
public class FloatEvaluator implements TypeEvaluator {
    //参数fraction:表示动画的完成度的,根据它来计算当前动画的值应该是多少
    //参数startValue、endValue:动画的初始值和结束值
    public Object evaluate(float fraction, Object startValue, object endValue){
        float startFloat =((Number)startValue).floatValue();
        return startFloat + fraction*(((Number)endValue).floatValue()- startFloat);
    }
}

3.3.2 任意对象计算

详见

3.4 TimeInterpolator

详见

兼容之前的 Interpolator。编写自定义Interpolator最主要的难度都是在于数学计算方面的。

public interface TimeInterpolator {

        float getInterpolation(float input);

}

参数 input 随着动画运行,在开始的0到结束的1之间匀速变化。根据 input 的值来计算出一个返回值,而这个返回值就是 TypeEvaluator 中的 fraction 了。input 和 fraction 相同时动画就是匀速的,

anim,setInterpolator(new AccelerateInterpolator(2f));

四、切换界面动画 Transition

官方页面

五、视图动画 ViewAnimation

视图动画的作用对象是View,又叫补间动画,仅仅是呈现呈现效果,便不会改变控件任何的真实情况(例如Button即便动画移到了下边也不能点击,其实还停留在原处可点击)。可以通过 xml 或代码动态创建(建议使用 xml 因为具有更高的可读性和重用性)。

  • 数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点 

5.1 动画模式

透明度

AlphaAnimation(

    float fromAlpha,        //动画开始的透明度

    float toAlpha        //动画结束的透明度

)

透明度取值范围范围 0~1。

缩放

public ScaleAnimation(
    float fromX,        //初始X轴缩放比例,1.0表示无变化
    float toX,        //结束X轴缩放比例
    float fromY,        //初始Y轴缩放比例
    float toY,        //结束Y轴缩放比例
    float pivotX,        //缩放起点X轴坐标
    float pivotY        //缩放起点Y轴坐标
)

pivotX/pivotY:缩放的中轴点坐标,即距离自身左边缘的位置,比如50%就是以图像的中心为中轴点。

平移

public TranslateAnimation(
    float fromXDelta,        //起始点X轴坐标
    float toXDelta,        //结束点X轴坐标
    float fromYDelta,        //起始点Y轴坐标
    float toYDelta        //结束点Y轴坐标
)

旋转

public RotateAnimation(
    float fromDegrees,        //旋转的起始角度
    float toDegrees,        //旋转的结束角度
    float pivotX,        //缩放起点X坐标

    float pivotY        //缩放起点Y坐标
)

旋转角度:正代表顺时针度数,负代表逆时针度数。

动画集合

AnimationSet(boolean shareInterpolator)

可同时播放以上动画。shareInterpolator:true都用一样的插值器,false用各自的插值器。通过调用addAnimation(Animation a) 将动画添加进集合。

5.2 动画配置

持续时间public void setDuration(long durationMillis)
重复次数

public void setRepeatCount(int repeatCount) 

值为-1或者infinite时,表示动画永不停止。

重复模式

public void setRepeatMode(int repeatMode)

默认restart,但只有当repeatCount大于0或者infinite或-1时 才有效。还可以设置成reverse,表示偶数次显示动画时会做方向相反的运动。

设置速度模型

public void setInterpolator(Interpolator i)

停留结果

public void setFillAfter(boolean fillAfter)

设为 true 则动画结束后 view 会停留在当前效果。

开始动画

public void startAnimation(Animation animation)

由 view 调用,传入要播放的动画。

5.3 动画监听

public void setAnimationListener(AnimationListener listener)

用动画对象调用,重写三个方法:onAnimationStart动画开始时、onAnimationEnd动画结束时、onAnimationRepeat动画重复时。

5.4 使用方式

5.4.1 xml配置动画

res目录下新建anim文件夹,创建xml文件,可选节点有alpha、rotate、scale、translate、set。

<translate xmlns:android="http://schemas.android.com/apk/res/android"  
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
    android:fromXDelta="0"  
    android:toXDelta="320"  
    android:fromYDelta="0"  
    android:toYDelta="0"  
    android:duration="2000"/>
AnimationUtils.loadAnimation(this, R.anim.translate)    //加载Xml文件中的动画
imagineView.startAnimation(translate)    //将动画设置到指定的View上

5.4.2 代码配置动画

val imagineView = binding.imagineView
val button = binding.button
//透明
val alphaAnimation = AlphaAnimation(1.0F, 0.2F)
//旋转
val rotateAnimation = RotateAnimation(0F, 360F, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F)
//缩放
val scaleAnimation = ScaleAnimation(1.0F, 5.0F, 1.0F, 5.0F, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F)
//平移
val translateAnimation = TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0F, Animation.RELATIVE_TO_PARENT, 0F,Animation.RELATIVE_TO_PARENT, 0F, Animation.RELATIVE_TO_PARENT, 0.4F)
//动画集合
val set = AnimationSet(true).apply {
    addAnimation(alphaAnimation)
    addAnimation(rotateAnimation)
    addAnimation(scaleAnimation)
    addAnimation(translateAnimation)
    duration = 1     //持续时间
    repeatCount = 10    //重复次数
    repeatMode = Animation.RESTART  //重复方式
    interpolator = AccelerateDecelerateInterpolator() //速度模型
    fillAfter = true //播放完毕后停留在原地
}
//动画监听
set.setAnimationListener(object : AnimationListener {
    override fun onAnimationStart(animation: Animation?) { }    //动画开始
    override fun onAnimationEnd(animation: Animation?) { }    //动画结束
    override fun onAnimationRepeat(animation: Animation?) { }    //动画重复
})
button.setOnClickListener {
    imagineView.startAnimation(set) //开始动画
}

六、帧动画 DrawableAnimation(Frame)

容易引起OOM。

6.1 xml配置

res/drawable目录下创建animation-list文件

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">	//true表示只播放一边,默认就是false可不写
    <item android:drawable="@drawable/girl_1" android:duration="100" />
    <item android:drawable="@drawable/girl_2" android:duration="100" />
    <item android:drawable="@drawable/girl_3" android:duration="100" />
</animation-list>

6.2 代码调用

imagineView.setBackgroundResource(R.drawable.anim_frame)
val animationDrawable = imagineView.background as AnimationDrawable
animationDrawable.start()

七、MotionLayout

官方页面

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值