Android 帧动画最小内存_动画效果


大家好,欢迎收看新一期《无聊的编码》,本期我们将主要聊聊看Flutter中的动画,顺便来看看,网络上盛传的Flutter动画会rebuild界面导致资源大幅耗用,是不是真的呢?


Android 帧动画最小内存_动画效果_02



开始真正揭秘之前,我们把目光放回动画的问题上。作为Flutter官方宣传的一大亮点,60帧仿佛一道鸿沟,将自己划在了原生级应用的行列。那么到底一个流畅的动画效果代表什么呢?简单的来说,就是拥有更好的用户体验,不起眼的点击,滑动,轮播,都给用户以更人性化的反馈。

归根结底来说,一副动画的产生,也就是由一帧一帧的画面拼接而成。60帧的含义也就是一秒内需要出现60幅画面,或者更直接一些来说,就是在一秒内,需要重新生成60次界面,这么听起来好像确实有点耗费资源啊,咱们接着往下看。


Android 帧动画最小内存_List_03


于是有些技术博主,开始了自己的“假设”,Flutter动画每执行一次,相当于调用了setState方法,从而导致页面整体重新build,这是一个非常耗费资源的过程所以不要用setState来刷新数据,要使用AnimatedWidget进行操作

停!一个数据的更新就导致整个界面的刷新?我怎么听着有点像十多年前的http://ASP.Net?还是那种没有使用UpdatePanel的?好像从AJAX的出现开始,就没见过这种全局重新build的事情了吧?难道Flutter走下坡路了?


Android 帧动画最小内存_动画效果_04


还有就是执行动画的时候不要使用setState进行页面rebuild,这样会对能耗产生很大的影响。这就更让人难以理解了,动画不重新build控件,如何进行界面上的更新呢?AnimatedWidget就不用重构组件,直接能够生成动画了么?

带着这样的疑问,我们打开了Flutter官网有关动画的相关教程。确实提到了addListener中调用setState的替换方法----AnimatedWidget,但是这一组件按照官方的解释是,简化Animation的生成逻辑,但是AnimatedWidget一样需要重新画组件啊,只是不需要人为设置并调用了。

这就相当于有人告诉你,最近因为非洲猪瘟,千万不要吃猪肉,猪肉荠菜的饺子没关系,因为外面包了层面皮。。。


Android 帧动画最小内存_List_05



面对这种比较奇怪的言论,自然就不应该使用同样奇怪的互喷来解决,为此,我们决定做一个小的实验,来证明,一个动画组件的整个生命周期,并不会引起其他组件的重新生成,更不用说整个页面重新生成了。

现在我们绘制了一个简单界面,在界面上放置了4个配置了动画的Icon,那么长什么样呢?咱们先看看。


Android 帧动画最小内存_Android 帧动画最小内存_06


按照网上的说法,我每执行一次动画,比如动画时间1秒,那么就需要将整个页面重构60次。为了大家可以更清晰的了解整个过程,

我们分别在Flutter应用的入口,即MaterialApp生成处,print("build MaterialApp")

在四个Icon外层包围的Scaffold中,也同样去print("build home")

最后在四个Icon上,分别设置其对应的Key,我们需要在build Icon的同时进行print(Key)

如果在第一个Icon发生动画的同时,打印的结果中包含了其他组件的build提醒,那么我们就承认这些技术博主的言论。


Android 帧动画最小内存_flutter 动画_07



实验开始。我们首先build整个项目,注意观察打印出的结果。


Android 帧动画最小内存_Android 帧动画最小内存_08


首先build MyApp,然后home,紧接着4个Icon,是我们预想中的结果,接下来进行测试,点击单一Icon,是否引起全局更新。


Android 帧动画最小内存_封装_09


点击按钮1,也就是最上方的点赞按钮,并没有提示重新build了其他组件。当然同样,对于图标2,3,4,一样不会发生所谓全局更新的问题,那么这一谬论到底从何而来呢?

在绞尽脑汁后,只有一个合适的说法,也就是对于整个Flutter界面而言,是一个非常庞大的widget,所有相关内容,不进行任何封装,全都平铺在同一parent组件下,那么这时如果调用了build方法,确实会发生整个界面的rebuild。

但是这个锅能甩到Flutter头上么?对此,只能表示呵呵一笑,绝对不抽


Android 帧动画最小内存_List_10


所以在进行Flutter的书写过程中,我们更推荐,或者说更应该做到的是,尽量对组件进行封装,并且我所指的封装,可能并不仅仅是将部分嵌套较深的代码进行抽离,重新生成组件,而是应该动动心思,如何将重复代码进行精简,将可用同一组件进行生成的内容,尽量抽离而出。

比如上方带有动画的Icon,我们就进行了相应的提取。只需定义基本的动画过程,就可利用封装后的StatefulWidget无障碍生成动画。听起来挺唬人的?一起看看源码。


class


首先我们定义了一个类,来接收动画的一些基本元素,比如一个图标的阶段初始大小,动画结束时大小,阶段所带有的颜色,以及阶段动画时长。

为什么我一直在强调阶段?因为我们所提供的封装后的widget,支持多段动画拼接。比如gif图中的第一个点赞按钮,它的生成逻辑是

1倍~0倍 白色~红色 200毫秒

0倍~1.3倍 红色~红色 300毫秒

1.3倍~1倍 红色~红色 100毫秒

你只需要定义一个List<IconAnimationStage>,并将其作为参数,传入AnimatedIconWidget中,你的动画也可以轻松实现啦。


Android 帧动画最小内存_封装_11


List


除了放大缩小的动画效果,我们还提供了什么功能?

比如,你可以在动画结束后,调用传送过来的callback,或者你想在动画结束后,有一个短暂的停顿,再执行callback,都是支持的。


AnimatedIconWidget

Github AnimatedIcon 源码github.com

Android 帧动画最小内存_flutter 动画_12