参考文献:
1. 概述
在 Android 5.0 及更高的版本中,加入了一种全新的视觉动画效果,就是揭露动画。揭露动画在系统中很常见,就是类似波纹的效果, 从某一个点向四周展开或者从四周向某一点聚合起来,本文实现的效果如下所示,可以用在 Activity 里面的 View 动画效果, 也可以使用在 Activity 跳转过渡动画中,如下图使用在 View 的显示隐藏的效果图:
此图需要对按钮做点击处理,对蓝色背景视图做揭露处理
2. 基本使用
使用揭露动画非常简单,Android Sdk 中已经帮我们提供了一个工具类 ViewAnimationUtils 来创建揭露动画。ViewAnimationUtils 里面只有一个静态方法 createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius)
, 返回一个 Animator 动画对象。
ViewAnimationUtils.createCircularReveal()
方法能够为裁剪区域添加动画以揭露或隐藏视图。我们主要使用 createCircularReveal 方法, 该方法有五个参数:
- 第一个参数是执行揭露动画的 View 视图(上面的蓝色 View)
- 第二个参数是相对于视图 View 的坐标系,动画圆的中心的 x 坐标(button 中点横坐标)
- 第三个参数是相对于视图 View 的坐标系,动画圆的中心的y坐标(button 中点纵坐标)
- 第四个参数是动画圆的起始半径
- 第五个参数动画圆的结束半径。
揭露动画有两种效果,一种是显示一组 UI 元素,另一种是隐藏一组 UI 元素:
- 以中心点为轴点,当开始半径小于结束半径时,从开始半径处向外扩大到结束半径处显示 View
- 以中心点为轴点,当开始半径大于结束半径时,从开始半径处向内缩小到结束半径处隐藏 View
注意:揭露动画对象只能使用一次,不能被重新使用,也就是说每次使用揭露动画都要调用 ViewAnimationUtils.createCircularReveal() 返回一个揭露动画对象使用,同时一旦开始了动画就不能暂停或重新开始。揭露动画是一种异步动画,可以自动运行在 UI 线程上。 当揭露动画结束后,如果设置了 Animator.AnimatorListener 监听器,那么监听器的 onAnimationEnd(Animator) 方法会被调用, 但可能会被延迟调用,这取决于线程的响应能力。
3. 实践
通过这个分析图,其实应该就很容易理解了,我们要做的所有事情就是确定 ViewAnimationUtils.createCircularReveal()
这 个方法的四个参数。
这里查看具体的代码:
Fab 即为按钮视图,mPuppet 即为揭露视图
fab.setOnClickListener(... {
launchRevealAnimation();
});
private void launchRevealAnimation() {
//求出第2个和第3个参数
int[] vLocation = new int[2];
fab.getLocationInWindow(vLocation);
int centerX = vLocation[0] + fab.getWidth() / 2;
int centerY = vLocation[1] + fab.getHeight() / 2;
//求出要揭露 View 的对角线,来作为扩散圆的最大半径
int hypotenuse = (int) Math.hypot(mPuppet.getWidth(), mPuppet.getHeight());
if (flag) {//隐藏 揭露对象
Animator circularReveal = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, hypotenuse, 0);
circularReveal.setDuration(2000);
circularReveal.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
mPuppet.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
circularReveal.start();
flag = false;
} else {//显示 揭露对象
Animator circularReveal = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, 0, hypotenuse);
circularReveal.setDuration(2000);
//注意:这里显示 mPuppet 调用并没有在监听方法里,并且是在动画开始前调用。
mPuppet.setVisibility(View.VISIBLE);
circularReveal.start();
flag = true;
}
}
上面就是实现一个揭露对象显示与隐藏的具体代码。没有任何难点,唯一要注意的是:显示揭露对象时并不是在动画监听方法的 onAnimationEnd
里 进行的。为什么要这样呢(我也是栽过这个坑里好久,后来才理解)?
- 首先揭露对象要先于动画开始前显示,因为如果动画开始时,要揭露的对象处于隐藏状态,那么动画就不会有效果,因为隐藏状态是看不到效果的。所以是不能 在动画结束的回调里才设置 View VISIBLE,这时动画已经结束了不会看到任何效果了。
- 另一方面,为什么不在动画开始的回调方法里设置 View.VISIBLE ,在这里调用也是可以的,一般这样设置也不会有任何问题。但我们在前面就说过揭露动画 是一个异步动画,它的回调方法都不能保证在准确的时间里调用(可能延迟调用,虽然很短),所以我们建议直接在调用动画开始方法 start() 直接设置 View.VISIBLE 为好。