做者:Nick Butcher, Android 设计师 + 开发project师, Googlecss
在为 Google I/O 2018 Android 团队工做期间。咱们的主要做品之中的一个就是这个官方的应用,赞成与会者和远程人员了解会议细节。创建个性化的时间表,并在会场预订座位。html
咱们在应用中构建了不少有趣的动效,而且咱们也开源了这个应用的代码。如下我想强调一些这些设计实例,以及一些有趣的实现细节,但愿能给你们平常的动效设计带来灵感。json
一般咱们会在应用中使用 3 种类型的动效:canvas
主动效:用于强化品牌视觉。并带来使人惊喜的视觉焦点后端
屏幕切换布局
状态变化post
接下来,我想具体介绍一下这些动效的设计细节。动画
倒计时动画spa
可以这么说,Google I/O 官方应用的当中一部分做用就是为会议带来兴奋感和期待感。所以,咱们在首屏幕和信息页面都显示了大型的倒计时动画。这也是将大会的活动品牌视觉进行呈现的绝佳机会,为应用增添了很是多特点。.net
△ 主屏幕抓眼球的倒计时动画
这个动效是由一位动效设计师设计的。并以一系列的 Lottie json 文件交到咱们手中:每秒显示一个数字,让它以动效的形式 “in” 而后 “out”。Lottie 格式的文件可以轻松地放入 assets,甚至提供了便利的操做方法,如 setMinAndMaxProgress。它赞成咱们仅仅播放动效的前半部分或后半部分 (从而仅仅显示动效的进场或出场)。
真正好玩的地方是将多个单独的动画文件布局成整体的倒计时画面。为此。咱们建立了 CountdownView,一个至关复杂的本身定义 ConstraintLayout。当中包括了多个 LottieAnimationViews。在这里。咱们建立了一个 Kotlin 托付来封装启动适当的动效。
这样咱们就可以简单地为每个应该显示的数字的托付分配一个用来显示的 Int,随后托付就会完毕设置并启动动效。
咱们扩展了 ObservableProperty,确保咱们仅仅在数字更改时才执行动效。咱们的动效循环每秒仅仅公布一个可执行状态 (在和视图关联起来的时候),这个状态计算每个视图应该显示哪一个数字并对托付进行更新。
预定会议
应用的关键操做之中的一个是让与会者预约座位。
所以,咱们在会议详情屏幕上的 FAB 中突出显示此操做。咱们以为应该仅当预约操做在后端成功完毕后再进行提示 (不像那些不过重要的操做,好比 “关注” 一场演讲,咱们就差点儿是同步在界面上显示关注成功)。等待来自后端的响应可能需要一些时间,为了这个预约的过程感受更快捷,咱们使用动效图标,代表咱们正在对预约进行处理,而后再平滑过渡到成功的状态中。
△ 预约的动画,有展现后端操做的动画过程
这个图标需要反映的状态很是复杂:会议多是可预订的,可能已经预留了座位,假设座位已满,则可能产生等候名单。他们也可能出现在等待列表上。或是在会议即将開始时预定功能被禁用了。这会致使应用需要显示多种动效以表明各类不一样的状态。
为了简化这些转场效果,咱们决定始终显示 “后端操做” 状态,也就是上面动画中的沙漏。所以。每次动效切换实际上都是成对的:状态 1 → 后端操做 & 后端操做 → 状态 2。这样就简化了很是多事情 (否则您可以想象可能出现的排列组合会有多少)。
接下来咱们使用 shapeshifter 构建了所有这些动效。
为了显示这些动效,咱们使用了本身定义视图和 AnimatedStateListDrawable (ASLD)。假设你还没实用过 ASLD,咱们简介一下:正如其名。它是动效版的 StateListDrawable,让你不只可以为每个状态提供不一样的 Drawable,还可以提供过渡状态之间的转场 (以 AnimatedVectorDrawable 或 AnimationDrawable 的形式)。
咱们建立了一个本身定义视图来支持这些本身定义的预约状态。视图提供一些标准状态,如已按下或已选中。相同。您也可以定义本身的状态,并依据状态显示不一样的 Drawable。咱们自行定义了 state_reservable,state_reserved 等,随后咱们会再为这些不一样的状态建立一个枚举。封装视图状态以及不论什么相关的属性,如相关的内容描写叙述。而后。咱们的业务逻辑可以简单地从视图上的这个枚举中设置适当的值 (经过数据绑定)。这将更新 Drawable 的状态,而后经过 ASLD 启动动效。
本身定义状态和 AnimatedStateListDrawable 是实现这一点的一种巧妙方法,将多个状态保留在声明层中,从而产生最少的视图代码。
演讲者动效
不少屏幕间的转场动画都可以直接用标准窗体动效。咱们另辟蹊径的地方是,前往演讲者介绍信息屏幕的转场效果。例如如下图,演讲者头像在演讲信息和演讲者信息两个屏幕之间共享。是典型的共享元素转场动画,这样也有助于理解先后两个屏幕间的内容关联。
△ 演讲者头像在两个屏幕间共享
这里是一个很是标准的共享元素转换,在 ImageView 上使用了 ChangeBounds 和 ArcMotion 类。
更有意思的是,启动这个转场效果自己也会归入咱们的导航设计/开发模式中来。大致上讲,这样的模式将输入事件 (如点击一位演讲者) 与导航事件分离,让 ViewModel 来负责怎样响应输入。在这样的状况下。这样的解耦意味着,ViewModel 将会暴露出 Events 的 LiveData。它仅仅知道需要导航到哪一个 ID 的演讲者。启动共享元素转场效果需要共享 View,而在这一瞬间这个 View 尚未。咱们解决问题的方式是,在绑定时将演讲者的 ID 做为标签存储在视图上,以便稍后当咱们需要导航到特定的演讲者细节屏幕时检索该视图。
过滤器
会议应用的核心功能之中的一个是帮助用户从不少会议中过滤出感兴趣的那些。
每个话题都有一个与之相关的颜色。以便识别。
咱们收到的设计方案基于 “Chip” 动画。便于用户与各个话题进行操做。
△ Chip 列表很是适合用来挑选感兴趣的话题
Material Components 里提供了默认的 Chip 控件,但咱们决定使用本身定义视图,以便更好地控制各个状态的显示内容和动效。咱们使用 canvas 进行画图并使用 StaticLayout 来显示文本。
该视图具备单一的 progress 属性。即 0 - 未选中 / 1 - 已选中。
在状态切换的时候。咱们会同一时候改动视图中的显示元素,并使用线性插值来决定每一帧的形状和颜色。
最初在实现这个控件时,我让视图实现了 Checkable 接口。并在 setChecked 方法被调用来给状态赋新值的时候启动动效。只是当咱们在 RecyclerView 中显示多个过滤器时,会出现用户的操做触发的动效和数据绑定触发的动效冲突的状况。为了解决问题,咱们单独提供了一个触发动效的方法来避免这样的冲突。
此外。当咱们刚刚给应用里引入这个动画效果时。咱们发现它有丢帧。
是个人动效代码有问题吗?这些控件位于一个 BottomSheet 中,且被放置在会议日程安排画面的前面。
当过滤器里的值被改动时,咱们会执行过滤会议造成的业务逻辑,而且更新关注列表里的关注内容,这些看来都没什么问题。
后来通过排查。咱们肯定问题在于,当过滤器里的内容被改动的时候,负责显示日程的 RecyclerViews 的 ViewPager 尽职尽责地启动了,并依据新提供的数据进行了更新。这致使不少视图出现了因为内容添加而“膨胀”的现象,更因为这个视图内容的添加而触发了自适应布局的逻辑,来又一次渲染当中的内容。所有的这些逻辑都是本身主动的。也没问题……除了会撑爆咱们的刷新率。但关注列表事实上在过滤器的 “后面” (被遮住了)。因而咱们决定延迟执行关注列表里的内容更新 (直到过滤器里的值都被肯定,且动效開始执行时才更新),咱们牺牲了一些实现的复杂性。换取了更加顺畅的用户体验。最初我使用 postDelayed 实现它,但这致使了 UI 測试时出现故障。因而,咱们改动了启动动效的方法。赞成它接受一个 lambda 表达式。这样一来,咱们就能在保持用户操做的动效和高效的測试之间找到一个平衡。
一块儿动起来吧!
总的来讲。我以为动效确实有助于改善应用的体验。以及提高品牌表现力。但愿这篇文章能让您明确咱们在各个环节使用动效的动机,以及具体实现它们的作法。
更重要的是,咱们但愿您的应用中也能出现丰富多彩的动效,在抓人眼球的同一时候也能很是好地表达品牌信息和交互意图。
点击屏末 | 阅读原文 | 获取 “Google I/O 官方应用” 开源码。
推荐阅读: