Animation组合动画踩坑-实现循环播放动画,可控制次数

先上效果图:
在这里插入图片描述

Animation组合动画踩坑-实现循环播放动画,可控制次数

比如说期望如下:
在这里插入图片描述

如果使用View动画,那么很自然的就想到了通过res/anim下的xml文件来实现,组合动画的话使用set标签即可。

直接这样做真的能生效么?且让我们一步一步实践。先提前剧透下,官网的demo也是有问题的。

赶时间只想看解决方式的同学,可以直接移步到最后一步的demo。

1、使用res/anim下的xml文件,实现组合动画顺序执行的坑。

动画的顺序执行是依靠的startOffset属性,它的值等于前面所有动画的duration之和。我们的最直观的实现代码如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">

    <scale
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="@integer/breath_anim_repeat_count"
        android:toXScale="0.9"
        android:toYScale="0.9" />

    <scale
        android:duration="400"
        android:fromXScale="0.9"
        android:fromYScale="0.9"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="@integer/breath_anim_repeat_count"
        android:startOffset="200"
        android:toXScale="1.1"
        android:toYScale="1.1" />

    <scale
        android:duration="400"
        android:fromXScale="1.1"
        android:fromYScale="1.1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="@integer/breath_anim_repeat_count"
        android:startOffset="600"
        android:toXScale="0.9"
        android:toYScale="0.9" />

    <scale
        android:duration="200"
        android:fromXScale="0.9"
        android:fromYScale="0.9"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="@integer/breath_anim_repeat_count"
        android:startOffset="1000"
        android:toXScale="1.0"
        android:toYScale="1.0" />

</set>

但是这段动画实际执行起来是有问题的,第二段scale代码的fromXScalefromYScale会在set动画一开始就同时作用于View上,而不是按照我们的期望,在startOffset时间到了之后再生效。

虽然startOffset属性会延时动画的执行,但是fromXScalefromYScale的值不会延时生效,会从动画开始就影响set系列动画的初始状态。

官网demo也有同样的问题:https://developer.android.com/guide/topics/graphics/view-animation?hl=zh-cn#java

解决方式:

将后续动画的fromXScale和fromYScale修改为1.0,不然会影响动画的初始状态。
然后动态转换下对应动画的toXScale和toYScale的比例。

转换的结果如下表:

顺序durationfromXScaletoXScalefromYScaletoYScale转换后的fromXScale转换后的toXScale转换后的fromYScale转换后的toYScale
12001.00.91.00.91.01.00.90.9
24000.91.10.91.11.01.01.22222222222222231.2222222222222223
34001.10.91.10.91.01.00.81818181818181810.8181818181818181
42000.91.00.91.01.01.01.11111111111111121.1111111111111112

2、重复次数-repeatCount的坑:

①给set设置repeatCount无效。
②给set中的各个元素设置repeatCount的话,各个动画独立执行自己的repeatCount,不会按照我们的期望,等到动画顺序执行完第一遍以后,再执行下一遍动画;而是每个scale元素立即执行自身的下一次动画,这样动画看起来就会很卡顿,不符合预期

解决方式:
将每个单独元素scale的repeatCount设置为0,具体的repeatCount逻辑依赖代码动态实现

3、取消、停止动画的坑。

取消动画的方式有三种:Animation#cancel()View#clearAnimation()View#setAnimation(null),它们各有优缺点。
Animation#cancel()会触发Animation.AnimationListener#onAnimationEnd回调,不适合在Animation.AnimationListener#onAnimationEnd回调中调用。
View#clearAnimation()也会触发Animation.AnimationListener#onAnimationEnd回调,同样不适合在Animation.AnimationListener#onAnimationEnd回调中调用。
View#setAnimation(null)不会触发Animation.AnimationListener#onAnimationEnd回调,但是它可能会导致下次调用Animation#start失败。

4、Animation.AnimationListener监听动画结束的回调不靠谱。

Animation.AnimationListener#onAnimationEnd方法不靠谱,主要是调用时机不符合预期。

参考:https://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-called-onanimationstart-works-fine

Animation#cancel()View#clearAnimation()都会触发Animation.AnimationListener#onAnimationEnd回调,并且在Animation.AnimationListener#onAnimationEnd方法中,Animation#hasEnded()方法返回的是false,跟期望的true不一致。

②在Animation.AnimationListener#onAnimationEnd中调用Animation#start方法开启下一次动画时,会立即调用Animation.AnimationListener#onAnimationStartAnimation.AnimationListener#onAnimationEnd回调,中间间隔只有1ms,这就会导致少执行一次动画,跟期望不符。

解决方式:
不建议使用Animation.AnimationListener来监听动画结束,建议使用Handler#postDelay方法 + View#clearAnimation方法来实现RepeatCount功能。

最终解决方式:
①res/anim中的xml文件中的set动画,顺序执行时除了使用startOffset保证执行顺序,还需要对fromXScalefromYScaletoXScaletoYScale进行相应的转换,保证fromXScalefromYScale始终为1.0
②使用Handler#postDelay方法来实现repeatCount功能。
③使用View#clearAnimation方法来清除动画。
④再次启动动画时,先使用Animation#reset()方法重置状态,再使用Animation#start()方法启动动画。

最终解决方式:自定义BreathAnimHelper。

具体实现参考BreathAnimHelper

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tinyvampirepudge

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值