参考文献:

揭露动画
使用揭露动画,让你的应用特效更进一步!

1. 概述

在 Android 5.0 及更高的版本中,加入了一种全新的视觉动画效果,就是揭露动画。揭露动画在系统中很常见,就是类似波纹的效果, 从某一个点向四周展开或者从四周向某一点聚合起来,本文实现的效果如下所示,可以用在 Activity 里面的 View 动画效果, 也可以使用在 Activity 跳转过渡动画中,如下图使用在 View 的显示隐藏的效果图:

revealanimation.gif

此图需要对按钮做点击处理,对蓝色背景视图做揭露处理

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 中点纵坐标)
  • 第四个参数是动画圆的起始半径
  • 第五个参数动画圆的结束半径。

createCircularReveal.jpg

揭露动画有两种效果,一种是显示一组 UI 元素,另一种是隐藏一组 UI 元素:

  • 以中心点为轴点,当开始半径小于结束半径时,从开始半径处向外扩大到结束半径处显示 View
  • 以中心点为轴点,当开始半径大于结束半径时,从开始半径处向内缩小到结束半径处隐藏 View

注意:揭露动画对象只能使用一次,不能被重新使用,也就是说每次使用揭露动画都要调用 ViewAnimationUtils.createCircularReveal() 返回一个揭露动画对象使用,同时一旦开始了动画就不能暂停或重新开始。揭露动画是一种异步动画,可以自动运行在 UI 线程上。 当揭露动画结束后,如果设置了 Animator.AnimatorListener 监听器,那么监听器的 onAnimationEnd(Animator) 方法会被调用, 但可能会被延迟调用,这取决于线程的响应能力。

3. 实践

revealanimationdemo.png

通过这个分析图,其实应该就很容易理解了,我们要做的所有事情就是确定 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 为好。