使用 RxJava 实现更灵活的 Android View 动画

作者: rain 分类: 移动 发布时间: 2016-10-19 00:10 6 条评论

pdfviewer.io 开发团队把 RxJava 和 Android 中的动画结合起来使用,从而实现更加灵活的动画并且代码看起来更加简洁。

下面来看看他们是如何做的!

Android View 动画基础

本文使用 ViewCompat.animate(targetView) 返回的 ViewPropertyAnimatorCompat 对象来演示。通过该对象可以很方便的对 View 的属性做动画。

例如下面的代码是把一个按钮缩放到0,动画完成后把该按钮删除:

注意:本文的代码为 Kotlin 语音。

上面的代码看起来很简洁,清晰易懂。但是在一些复杂的场景,需要嵌套调用 withEndAction 函数,则代码则看起来会非常杂乱无章。

使用 RxJava

使用 RxJava 则可以把这些回调函数通过事件流的方式发射到 Observer 中。例如,对于每个做动画的 View,都可以通过调用 onNext(view) 来让下面的操作函数来处理该事件。

一种方式就是创建自定义的操作函数来实现各种动画。例如 创建一个自定义操作函数来实现简单的水平或者垂直移动 View 的动画。

下面的示例演示了通过这种方式来水平移动 View。注意,下面的代码只是为了演示这种使用方式,在正式项目中,一般不会这么用。

image
点击 Animate 按钮把红色的圆圈水平移动到另外一个方块内。

继承自 Observable.Operator 来定义一个自定义的操作函数 TranslateViewOperator,在创建这个自定义操作函数 TranslateViewOperator 的时候,需要 translationX、translationY、duration、interpolator 这四个参数来确定 View 动画的信息。 然后需要做动画的 View 作为数据流发射到 onNext() 函数中。 当 TranslateViewOperator 接收到 View 数据的时候,针对该 View 做动画,当动画完成后,继续把该 View 发射到下一个数据流处理 Subscriber 中。

上面的 “TranslateViewOperator.kt” 实现是在 View 动画完成后才继续发射 View 到下一个 Subscriber,如果需要也可以修改为,动画开始执行的时候就发射到下一个 Subscriber。

这样在包含需要移动的圆圈 View (CircleView) 和 方块 View (RectangleView)的父ViewGroup 中,就可以通过如下方式来移动圆圈View了:

AnimationViewGroup.kt

上面的代码使用了 Kotlin 的扩展函数功能。

在 AnimationViewGroup.kt 中保存圆圈View和方块View:

然后在 AnimationViewGroup.kt 中再创建一个开始动画的函数:

同样再实现一个反方向移动 View 的函数:

点击 Animate 按钮动画效果如下:

image

可以看到使用自定义操作函数还是有些麻烦的,需要写不少代码。而动画常见的逻辑是这样的:先做这个移动、再做缩放、再翻转 等等。

所以这个类型比较适合使用 Completable!

Completable 闪亮登场

RxJava Wiki 是这样介绍 Completable 的:

可以把 Completable 对象当做简化版的 Observable,Completable 只发射结束事件(onError 和 onCompleted )。看起来像一个特定类型的 Observable.empty(),但是和 Observable.empty() 不一样的是 Completable 是一个活动的类。订阅到 Completable 将会带来副作用,而 Completable 就是这样用的。

所以可以用 Completable 来执行动画,当动画执行完成后,就触发 onComplete() 事件。当 Subscriber 接收到 onComplete() 事件后,可以执行另外一个动画,也可以做其他操作。

使用 Completable 就不需要把 View 当做数据在数据流中发射了。只是在动画执行完成的时候,触发 onComplete() 事件。

下面使用一个具体的示例来演示如何使用 Completable。 该示例实现了一个 setMenuItems() 函数,该函数的功能如下:

把当前 toolbar 上的菜单按钮隐藏到菜单工具栏左边,然后缩小知道消失,然后从工具栏中删除这些菜单View,然后添加新的菜单到工具栏中,新的菜单开始的缩放系数为0,然后逐渐放大为正常尺寸,然后在工具栏中展开这些新的菜单。

为了避免编写自定义 View 的代码,下面的示例使用 FloatingActionButton 和 ToolBar。

通过继承 Completable.OnSubscribe 来实现一个 Completable。该类有一些初始化的参数:
– views: List 一个 FAB 列表,代表所有需要隐藏和显示的按钮
– animationType: AnimationType 动画的类型,水平隐藏、垂直隐藏、水平显示或者垂直显示
– duration: Long 动画的时间
– interpolator: Interpolator 动画的Interpolator
– paddingPx: Int padding 值

下面是 ExpandViewsOnSubscribe.kt 的实现:

然后可以创建一些执行特定动画的工具函数,这些函数使用 Completable.create 来创建 Completable 对象:

AnimationViewGroup.kt 类:

随便添加一些测试的 View,就可以来测试上面的动画函数了:
AnimationViewGroup.kt

下图为添加一些 View 到 ViewGroup中并对这些 View 做动画的示例:

image

把动画串联起来执行

使用上面同样的方式,继承 Completable.OnSubscribe 类来实现放大缩小和旋转的动画类。这两个类的代码和上面的代码基本一样,只是最终执行动画的代码不一样而已,所以这两个类的代码就省略了。

然后定义一些执行动画的辅助函数:
AnimationViewGroup.kt 类中

然后我们的 setMenuItems() 函数就可以通过如下的方式来实现:
AnimationViewGroup.kt 类

下图为效果

image

限制

由于上面的动画是在同一个 View 上连续执行(先放大、再移动 等),所以无法使用 mergeWith() 操作函数。如果你使用 mergeWith() 的话,将会导致同一个 View 上的动画 Listener 被覆盖了,导致 mergeWith() 无法触发完成事件。如果是在不同的 View 做动画,则可以使用 mergeWith()。

要解决这个问题,可以在一个 OnSubscribe 的实现类中同时完成多个动画,比如 RotateAndScaleViewOnSubscribe 实现中同时对 View 做 Rotate 和 Scale 动画。

示例产品

PSPDFKit 2.6 for Android 中 PDF 阅读器的侧边工具栏使用了这种方式来做动画。

image

动画的代码很简洁:

慢动作来看看动画效果:
image

由于菜单和子菜单是不同的 View,所以可以在隐藏菜单的同时关闭子菜单:

image

或者使用 mergeWith

image

结论

把本文介绍的内容当做 RxJava 在安卓动画中使用的一种技巧。这只是其中的一种使用方法,充分利用您的聪明才智,则可以发现更多的奇技淫巧,让你的代码更加简洁、写代码的效率更高。

本文出自 云在千峰,转载时请注明出处及相应链接。

本文永久链接: http://blog.chengyunfeng.com/?p=1023

Ɣ回顶部