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

简介: 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的比例。

转换的结果如下表:

顺序 duration fromXScale toXScale fromYScale toYScale 转换后的fromXScale 转换后的toXScale 转换后的fromYScale 转换后的toYScale
1 200 1.0 0.9 1.0 0.9 1.0 1.0 0.9 0.9
2 400 0.9 1.1 0.9 1.1 1.0 1.0 1.2222222222222223 1.2222222222222223
3 400 1.1 0.9 1.1 0.9 1.0 1.0 0.8181818181818181 0.8181818181818181
4 200 0.9 1.0 0.9 1.0 1.0 1.0 1.1111111111111112 1.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#onAnimationStart)和Animation.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

相关文章
|
运维 Kubernetes Java
nacos常见问题之nacos的内存太高如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
|
机器学习/深度学习 编解码 算法
人脸识别的主要流程
【1月更文挑战第4天】
|
Android开发 开发者
什么是Android Jetpack,它包括哪些组件?
什么是Android Jetpack,它包括哪些组件?
525 0
|
开发工具 git Windows
经验:停止 cherry-pick,请开始 merge!
cherry-pick 是一个比较常用的 git 操作,可以将一个分支上的 commit “精选”到另一个分支上。然而在最近的开发过程中,却时不时的遇到 merge 冲突。在下文中,我将会详细的分析 cherry-pick 造成冲突的原因,以及 cherry-pick 可能造成的其他更严重问题。
7991 0
经验:停止 cherry-pick,请开始 merge!
|
API Android开发 容器
33. 【Android教程】悬浮窗:PopupWindow
33. 【Android教程】悬浮窗:PopupWindow
2105 2
|
12月前
|
存储 监控 安全
网络安全视角:从地域到账号的阿里云日志审计实践
日志审计的必要性在于其能够帮助企业和组织落实法律要求,打破信息孤岛和应对安全威胁。选择 SLS 下日志审计应用,一方面是选择国家网络安全专用认证的日志分析产品,另一方面可以快速帮助大型公司统一管理多组地域、多个账号的日志数据。除了在日志服务中存储、查看和分析日志外,还可通过报表分析和告警配置,主动发现潜在的安全威胁,增强云上资产安全。
871 43
|
机器学习/深度学习 人工智能 Cloud Native
.NET现在可以做什么,有哪些公司在用的?
.NET是一个开源(MIT License)、免费、跨平台的开发人员平台框架,用于生成多种类型的应用程序。.NET 可以运行使用多种语言(C#、Visual Basic、F#)编写的程序,其中C#是最常用的语言。
442 0
|
Java 编译器 Spring
面试突击78:@Autowired 和 @Resource 有什么区别?
面试突击78:@Autowired 和 @Resource 有什么区别?
16415 6
|
Java 开发工具 Android开发
Android经典面试题之开发中常见的内存泄漏,以及如何避免和防范
本文介绍Android开发中内存泄漏的概念及其危害,并列举了四种常见泄漏原因:静态变量持有Context、非静态内部类、资源未释放及监听器未注销。提供了具体代码示例和防范措施,如使用ApplicationContext、弱引用、适时释放资源及利用工具检测泄漏。通过遵循这些建议,开发者可以有效提高应用稳定性和性能。
201 0
|
XML 搜索推荐 Java
Android TextView的字体设置
【5月更文挑战第13天】
994 0