public void hideMenu(View view) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      final int cx = (settingsMenuButton.getLeft() + settingsMenuButton.getRight()) / 2;
      final int cy =
          ((settingsMenuButton.getTop() + settingsMenuButton.getBottom()) / 2)
              - settingsMenu.getTop();

      Log.i("", "Circle center " + cx + ", " + cy + " width " + bottomView.getWidth());

      final Animator anim =
          ViewAnimationUtils.createCircularReveal(settingsMenu, cx, cy, bottomView.getWidth(), 0);

      anim.addListener(
          new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              super.onAnimationEnd(animation);
              settingsMenu.setVisibility(View.INVISIBLE);
              settingsMenuButton.setVisibility(View.VISIBLE);
            }
          });

      anim.start();
    } else {
      settingsMenuButton.setVisibility(View.VISIBLE);
      settingsMenu.setVisibility(View.INVISIBLE);
    }
  }
 public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
   if (mAnimator != null) {
     mAnimator.cancel();
   }
   final int w = mDetail.getWidth() - x;
   final int h = mDetail.getHeight() - y;
   int innerR = 0;
   if (x < 0 || w < 0 || y < 0 || h < 0) {
     innerR = Math.abs(x);
     innerR = Math.min(innerR, Math.abs(y));
     innerR = Math.min(innerR, Math.abs(w));
     innerR = Math.min(innerR, Math.abs(h));
   }
   int r = (int) Math.ceil(Math.sqrt(x * x + y * y));
   r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + y * y)));
   r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + h * h)));
   r = (int) Math.max(r, Math.ceil(Math.sqrt(x * x + h * h)));
   if (in) {
     mAnimator = ViewAnimationUtils.createCircularReveal(mDetail, x, y, innerR, r);
   } else {
     mAnimator = ViewAnimationUtils.createCircularReveal(mDetail, x, y, r, innerR);
   }
   mAnimator.setDuration((long) (mAnimator.getDuration() * 1.5));
   if (listener != null) {
     mAnimator.addListener(listener);
   }
   if (in) {
     mBackground.startTransition((int) (mAnimator.getDuration() * 0.6));
     mAnimator.addListener(mVisibleOnStart);
   } else {
     mDetail.postDelayed(mReverseBackground, (long) (mAnimator.getDuration() * 0.65));
     mAnimator.addListener(mGoneOnEnd);
   }
   mAnimator.start();
 }
  private void hide(final View myView) {
    // get the center for the clipping circle
    int cx = myView.getWidth() / 2;
    int cy = myView.getHeight() / 2;

    // get the initial radius for the clipping circle
    int initialRadius = myView.getWidth();

    // create the animation (the final radius is zero)
    Animator anim = null;
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
      anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
    }

    // make the view invisible when the animation is done
    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            myView.setVisibility(View.INVISIBLE);
          }
        });

    // start the animation
    if (anim != null) {
      anim.start();
    }
  }
  private Animator createCircularReveal(
      final ClipRevealFrame view, int x, int y, float startRadius, float endRadius) {
    final Animator reveal;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      reveal = ViewAnimationUtils.createCircularReveal(view, x, y, startRadius, endRadius);
    } else {
      view.setClipOutLines(true);
      view.setClipCenter(x, y);
      reveal = ObjectAnimator.ofFloat(view, "ClipRadius", startRadius, endRadius);
      reveal.addListener(
          new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {}

            @Override
            public void onAnimationEnd(Animator animation) {
              view.setClipOutLines(false);
            }

            @Override
            public void onAnimationCancel(Animator animation) {}

            @Override
            public void onAnimationRepeat(Animator animation) {}
          });
    }
    return reveal;
  }
Exemple #5
0
  // Layout showing effect(using CircularReveal)
  private void showLayout(int viewId, int viewId2) {
    final View myView = findViewById(viewId);
    View myView2 = findViewById(viewId2);

    if (android.os.Build.VERSION.SDK_INT > 20) {

      int finalRadius = Math.max(myView.getWidth(), myView.getHeight());

      int cx = (myView2.getLeft() + myView2.getRight()) / 2;
      int cy = (myView2.getTop() + myView2.getBottom()) / 2;

      Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
      anim.setDuration(500);

      anim.addListener(
          new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              super.onAnimationEnd(animation);
              if (gameStatus == GAME_GOGAME) {
                gameStatus = GAME_WAITING;
              }
            }
          });

      myView.setVisibility(View.VISIBLE);
      myView.setAlpha(1);
      anim.start();
    } else {
      myView.setVisibility(View.VISIBLE);
      if (gameStatus == GAME_GOGAME) {
        gameStatus = GAME_WAITING;
      }
    }
  }
  private void prepare(View view, FrameLayout FrameLayout) {
    int centerX = (view.getLeft() + view.getRight()) / 2;
    int centerY = (view.getTop() + view.getBottom()) / 2;
    finalRadius = (float) Math.hypot((double) centerX, (double) centerY);

    circularAnimator =
        ViewAnimationUtils.createCircularReveal(FrameLayout, centerX, centerY, 0, finalRadius);
    circularAnimator.setDuration(500);
  }
  /** Redraws default text with circle animation. */
  private void reDrawDefaultLayout() {
    int cx = mDefaultText.getMeasuredWidth() / 2;
    int cy = mDefaultText.getMeasuredHeight() / 2;

    int finalRadius = Math.max(mDefaultText.getWidth(), mDefaultText.getHeight()) / 2;

    Animator anim = ViewAnimationUtils.createCircularReveal(mDefaultText, cx, cy, 0, finalRadius);

    mDefaultText.setVisibility(View.VISIBLE);
    anim.start();
  }
  // testing animation class
  private void animateRevealShow(View viewRoot) {
    int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
    int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
    int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

    Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
    viewRoot.setVisibility(View.VISIBLE);
    anim.setDuration(1000);
    anim.setInterpolator(new AccelerateInterpolator());
    anim.start();
  }
  private void reveal(View toggleView, View btnView, int cx, int cy, int finalRadius) {
    // create the animator for this view (the start radius is zero)
    Animator anim = null;
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
      anim = ViewAnimationUtils.createCircularReveal(toggleView, cx, cy, 0, finalRadius);
    }

    // make the view visible and start the animation
    toggleView.setVisibility(View.VISIBLE);
    if (anim != null) {
      anim.start();
    }
  }
  private void showLoginPanel() {
    int cx = (loginPanel.getLeft() + loginPanel.getRight()) / 2;
    int cy = 0;

    // get the final radius for the clipping circle
    int finalRadius = loginPanel.getWidth();

    loginPanel.setVisibility(View.VISIBLE);

    // create and start the animator for this view
    // (the start radius is zero)
    ValueAnimator anim =
        ViewAnimationUtils.createCircularReveal(loginPanel, cx, cy, 0, finalRadius);
    anim.setDuration(500).start();
  }
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  private Animator circularRevealActivity(View rootView, int cx, int cy, boolean show) {
    float maxRadius = Math.max(rootView.getWidth(), rootView.getHeight());
    float startRadius = show ? 0 : maxRadius;
    float endRadius = show ? maxRadius : 0;

    // create the animator for this view (the start radius is zero)
    Animator circularReveal =
        ViewAnimationUtils.createCircularReveal(rootView, cx, cy, startRadius, endRadius);
    circularReveal.setDuration(animationTime);

    // make the view visible and start the animation
    rootView.setVisibility(View.VISIBLE);
    circularReveal.start();
    return circularReveal;
  }
  /** 为 myView 自身添加显示隐藏的动画 */
  @SuppressLint("NewApi")
  private static void actionVisible(
      final boolean isShow, final View myView, float miniRadius, long durationMills) {
    // 版本判断
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
      if (isShow) myView.setVisibility(View.VISIBLE);
      else myView.setVisibility(View.INVISIBLE);
      return;
    }

    int cx = (myView.getLeft() + myView.getRight()) / 2;
    int cy = (myView.getTop() + myView.getBottom()) / 2;

    int w = myView.getWidth();
    int h = myView.getHeight();

    // 勾股定理 & 进一法
    int maxRadius = (int) Math.sqrt(w * w + h * h) + 1;

    float startRadius, endRadius;
    if (isShow) {
      // -< 从小到大
      startRadius = miniRadius;
      endRadius = maxRadius;
    } else {
      // >- 从大到校
      startRadius = maxRadius;
      endRadius = miniRadius;
    }

    Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, startRadius, endRadius);
    myView.setVisibility(View.VISIBLE);
    anim.setDuration(durationMills);

    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            // 无论显示还是隐藏,都应当在动画结束后执行。
            if (isShow) myView.setVisibility(View.VISIBLE);
            else myView.setVisibility(View.INVISIBLE);
          }
        });

    anim.start();
  }
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  private void contractLollipop(int x, int y, float startRadius, float endRadius) {

    Animator toolbarContractAnim =
        ViewAnimationUtils.createCircularReveal(mFabExpandLayout, x, y, startRadius, endRadius);
    toolbarContractAnim.setDuration(startAnimationDuration);

    toolbarContractAnim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            contractAnimationEnd();
          }
        });

    toolbarContractAnim.start();
  }
Exemple #14
0
  // Layout hiding effect(using CircularReveal)
  private void hideLayout(int viewId, int viewId2) {

    final View myView = findViewById(viewId);
    View myView2 = findViewById(viewId2);

    if (android.os.Build.VERSION.SDK_INT > 20) {

      int initialRadius = myView.getWidth();

      int cx = (myView2.getLeft() + myView2.getRight()) / 2;
      int cy = (myView2.getTop() + myView2.getBottom()) / 2;

      Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
      // anim.setStartDelay(200);
      anim.setDuration(500);

      anim.addListener(
          new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              super.onAnimationEnd(animation);
              myView.setVisibility(View.INVISIBLE);
              if (gameStatus == GAME_LOADING) showMenu();
              else if (gameStatus == GAME_GOMENU) {
                hideView(R.id.back);
                hideView(R.id.restart);
                hideView(R.id.share);
                gameStatus = GAME_MENU;
              }
            }
          });

      anim.start();
    } else {
      myView.setVisibility(View.INVISIBLE);
      if (gameStatus == GAME_LOADING) showMenu();
      else if (gameStatus == GAME_GOMENU) {
        hideView(R.id.back);
        hideView(R.id.restart);
        hideView(R.id.share);
        gameStatus = GAME_MENU;
      }
    }
  }
  private void clearAnimation() {
    final Animator startAnimator;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      final int revealSize = resultContainerView.getWidth();
      final int[] location = new int[2];
      deleteButton.getLocationInWindow(location);
      final int centerX = location[0] + deleteButton.getWidth() / 2;
      startAnimator =
          ViewAnimationUtils.createCircularReveal(
              revealView, centerX, resultContainerView.getHeight(), 0, revealSize);
    } else {
      startAnimator =
          ObjectAnimator.ofFloat(
              revealView, View.TRANSLATION_Y, resultContainerView.getHeight(), 0);
    }
    startAnimator.setDuration(ANIMATION_DURATION_CLEAR_START);

    final Animator endAnimator = ObjectAnimator.ofFloat(revealView, View.ALPHA, 1, 0);

    final AnimatorSet animator = new AnimatorSet();
    animator.addListener(
        new Animator.AnimatorListener() {
          @Override
          public void onAnimationStart(Animator animation) {
            revealView.setVisibility(View.VISIBLE);
          }

          @Override
          public void onAnimationEnd(Animator animation) {
            revealView.setVisibility(View.GONE);
            revealView.setAlpha(1);
          }

          @Override
          public void onAnimationCancel(Animator animation) {}

          @Override
          public void onAnimationRepeat(Animator animation) {}
        });
    animator.playSequentially(startAnimator, endAnimator);
    animator.start();
  }
  private void reveal(View myView) {
    // get the center for the clipping circle
    int cx = myView.getWidth() / 2;
    int cy = myView.getHeight() / 2;

    // get the final radius for the clipping circle
    int finalRadius = Math.max(myView.getWidth(), myView.getHeight());

    // create the animator for this view (the start radius is zero)
    Animator anim = null;
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
      anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
    }

    // make the view visible and start the animation
    myView.setVisibility(View.VISIBLE);
    if (anim != null) {
      anim.start();
    }
  }
Exemple #17
0
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public static void setAnimation(View viewToAnimate, int position) {
    try {
      if (position >= Reddit.lastposition.get(Reddit.currentPosition) - 1 && Reddit.animation) {
        int cx = viewToAnimate.getWidth() / 2;
        int cy = viewToAnimate.getHeight() / 2;
        int finalRadius = Math.max(viewToAnimate.getWidth(), viewToAnimate.getHeight());

        final Animator anim =
            ViewAnimationUtils.createCircularReveal(viewToAnimate, cx, cy, 0, finalRadius);
        anim.setDuration(Reddit.enter_animation_time);
        anim.setInterpolator(new FastOutSlowInInterpolator());
        viewToAnimate.setVisibility(View.VISIBLE);
        anim.start();
        Reddit.lastposition.set(
            Reddit.currentPosition, Reddit.lastposition.get(Reddit.currentPosition) + 1);
      }
    } catch (IndexOutOfBoundsException e) {
      fixSliding(Reddit.currentPosition);
    }
  }
  public void showMenu(View view) {
    updateMenu();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      final int cx = (settingsMenuButton.getLeft() + settingsMenuButton.getRight()) / 2;
      final int cy =
          ((settingsMenuButton.getTop() + settingsMenuButton.getBottom()) / 2)
              - settingsMenu.getTop();

      Log.i(TAG, "Circle center " + cx + ", " + cy + " width " + bottomView.getWidth());

      final Animator anim =
          ViewAnimationUtils.createCircularReveal(settingsMenu, cx, cy, 0, bottomView.getWidth());

      settingsMenuButton.setVisibility(View.INVISIBLE);
      settingsMenu.setVisibility(View.VISIBLE);
      anim.start();
    } else {
      settingsMenuButton.setVisibility(View.INVISIBLE);
      settingsMenu.setVisibility(View.VISIBLE);
    }
  }
  @Override
  public void onDetach() {
    button.setClickable(true);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      Animator anim =
          ViewAnimationUtils.createCircularReveal(
                  thirdView,
                  thirdView.getWidth() / 2,
                  thirdView.getHeight() / 2,
                  0,
                  thirdView.getHeight())
              .setDuration(450);

      thirdView.setVisibility(View.VISIBLE);
      anim.start();
    } else {
      thirdView.setVisibility(View.VISIBLE);
    }

    super.onDetach();
  }
Exemple #20
0
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public static void revealHide(
      final Context context,
      int finalRadius,
      final View view,
      @ColorRes final int colorRes,
      @NonNull final OnRevealAnimationListener onRevealAnimationListener) {
    // previously invisible view

    // get the center for the clipping circle
    int cx = (view.getLeft() + view.getRight()) / 2;
    int cy = (view.getTop() + view.getBottom()) / 2;

    // get the final radius for the clipping circle
    int startRadius = Math.max(view.getWidth(), view.getHeight());

    // create the animator for this view (the start radius is zero)
    Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, startRadius, finalRadius);
    anim.setDuration(500);
    anim.setInterpolator(new AccelerateDecelerateInterpolator());
    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            onRevealAnimationListener.onAnimationStart();
            view.setBackgroundColor(ContextCompat.getColor(context, colorRes));
          }

          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            // make the view visible and start the animation
            view.setVisibility(View.INVISIBLE);
            onRevealAnimationListener.onAnimationEnd();
          }
        });
    anim.start();
  }
  private void hide(final View toggleView, View btnView, int cx, int cy, int initialRadius) {
    // create the animation (the final radius is zero)
    Animator anim = null;
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
      anim = ViewAnimationUtils.createCircularReveal(toggleView, cx, cy, initialRadius, 0);
    }

    // make the view invisible when the animation is done
    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            toggleView.setVisibility(View.GONE);
          }
        });

    // start the animation
    if (anim != null) {
      anim.start();
    }
  }
  @SuppressLint("NewApi")
  private void createReveal(final View myView) {

    // get the center for the clipping circle
    int cx = (myView.getWidth()) / 2;
    int cy = (myView.getHeight()) / 2;

    // get the final radius for the clipping circle
    int finalRadius = Math.max(myView.getWidth(), myView.getHeight());

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
      Animator animator =
          android.view.ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
      animator.setDuration(800);
      animator.start();
    } else {
      SupportAnimator animator =
          ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
      animator.setInterpolator(new AccelerateDecelerateInterpolator());
      animator.setDuration(800);
      animator.start();
    }
  }
  private void hideLoginPanelAndShowButton() {
    int cx = (loginPanel.getLeft() + loginPanel.getRight()) / 2;
    int cy = loginPanel.getTop();

    // get the final radius for the clipping circle
    int initialRadius = loginPanel.getWidth();

    // create and start the animator for this view
    // (the start radius is zero)
    ValueAnimator anim =
        ViewAnimationUtils.createCircularReveal(loginPanel, cx, cy, initialRadius, 0);
    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            loginPanel.setVisibility(View.INVISIBLE);
            showLogInButton();
          }
        });

    anim.setDuration(500).start();
  }
  protected Animator createCheckoutRevealAnimator(
      final ClipRevealFrame view, int x, int y, float startRadius, float endRadius) {
    setMenuVisibility(false);
    Animator retAnimator;
    mRadius = endRadius;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      retAnimator = ViewAnimationUtils.createCircularReveal(view, x, y, startRadius, endRadius);
    } else {
      view.setClipOutLines(true);
      view.setClipCenter(x, y);
      view.setClipRadius(startRadius);

      retAnimator = ObjectAnimator.ofFloat(view, "clipRadius", startRadius, endRadius);
    }
    retAnimator.setDuration(ANIM_DURATION);
    retAnimator.addListener(
        new Animator.AnimatorListener() {
          @Override
          public void onAnimationStart(Animator animation) {}

          @Override
          public void onAnimationEnd(Animator animation) {
            view.setClipOutLines(false);
            removeOldSideFragment();
          }

          @Override
          public void onAnimationCancel(Animator animation) {}

          @Override
          public void onAnimationRepeat(Animator animation) {}
        });

    retAnimator.setInterpolator(new AccelerateInterpolator(2.0f));
    return retAnimator;
  }
  /** 以 @triggerView 为中心为 @animView 添加显示隐藏的动画 */
  @SuppressLint("NewApi")
  private static void actionOtherVisible(
      final boolean isShow,
      final View triggerView,
      final View animView,
      float miniRadius,
      long durationMills) {
    // 版本判断
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
      if (isShow) animView.setVisibility(View.VISIBLE);
      else animView.setVisibility(View.INVISIBLE);
      return;
    }

    int[] tvLocation = new int[2];
    triggerView.getLocationInWindow(tvLocation);
    final int tvCX = tvLocation[0] + triggerView.getWidth() / 2;
    final int tvCY = tvLocation[1] + triggerView.getHeight() / 2;

    int[] avLocation = new int[2];
    animView.getLocationInWindow(avLocation);
    final int avLX = avLocation[0];
    final int avTY = avLocation[1];

    int triggerX = Math.max(avLX, tvCX);
    triggerX = Math.min(triggerX, avLX + animView.getWidth());

    int triggerY = Math.max(avTY, tvCY);
    triggerY = Math.min(triggerY, avTY + animView.getHeight());

    // 以上全为绝对坐标

    int avW = animView.getWidth();
    int avH = animView.getHeight();

    int rippleCX = triggerX - avLX;
    int rippleCY = triggerY - avTY;

    // 计算水波中心点至 @animView 边界的最大距离
    int maxW = Math.max(rippleCX, avW - rippleCX);
    int maxH = Math.max(rippleCY, avH - rippleCY);
    final int maxRadius = (int) Math.sqrt(maxW * maxW + maxH * maxH) + 1;

    float startRadius, endRadius;
    if (isShow) {
      // -< 从小到大
      startRadius = miniRadius;
      endRadius = maxRadius;
    } else {
      // >- 从大到校
      startRadius = maxRadius;
      endRadius = miniRadius;
    }

    Animator anim =
        ViewAnimationUtils.createCircularReveal(
            animView, rippleCX, rippleCY, startRadius, endRadius);
    animView.setVisibility(View.VISIBLE);
    anim.setDuration(durationMills);

    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            if (isShow) animView.setVisibility(View.VISIBLE);
            else animView.setVisibility(View.INVISIBLE);
          }
        });

    anim.start();
  }
  @SuppressLint("NewApi")
  private static void actionStarActivity(
      final int finishType,
      final Activity thisActivity,
      final Intent intent,
      final Integer requestCode,
      final Bundle bundle,
      final View triggerView,
      int colorOrImageRes,
      long durationMills) {

    // 版本判断,小于5.0则无动画.
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
      startActivityOrFinish(finishType, thisActivity, intent, requestCode, bundle);
      return;
    }

    int[] location = new int[2];
    triggerView.getLocationInWindow(location);
    final int cx = location[0] + triggerView.getWidth() / 2;
    final int cy = location[1] + triggerView.getHeight() / 2;
    final ImageView view = new ImageView(thisActivity);
    view.setScaleType(ImageView.ScaleType.CENTER_CROP);
    view.setImageResource(colorOrImageRes);
    final ViewGroup decorView = (ViewGroup) thisActivity.getWindow().getDecorView();
    int w = decorView.getWidth();
    int h = decorView.getHeight();
    decorView.addView(view, w, h);

    // 计算中心点至view边界的最大距离
    int maxW = Math.max(cx, w - cx);
    int maxH = Math.max(cy, h - cy);
    final int finalRadius = (int) Math.sqrt(maxW * maxW + maxH * maxH) + 1;

    Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
    int maxRadius = (int) Math.sqrt(w * w + h * h) + 1;
    // 若使用默认时长,则需要根据水波扩散的距离来计算实际时间
    if (durationMills == PERFECT_MILLS) {
      // 算出实际边距与最大边距的比率
      double rate = 1d * finalRadius / maxRadius;
      // 为了让用户便于感触到水波,速度应随最大边距的变小而越慢,扩散时间应随最大边距的变小而变小,因此比率应在 @rate 与 1 之间。
      durationMills = (long) (PERFECT_MILLS * Math.sqrt(rate));
    }
    final long finalDuration = durationMills;
    // 由于thisActivity.startActivity()会有所停顿,所以进入的水波动画应比退出的水波动画时间短才能保持视觉上的一致。
    anim.setDuration((long) (finalDuration * 0.9));
    anim.addListener(
        new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);

            if (requestCode == null) thisActivity.startActivity(intent);
            else if (bundle == null) thisActivity.startActivityForResult(intent, requestCode);
            else thisActivity.startActivityForResult(intent, requestCode, bundle);

            // 默认渐隐过渡动画.
            thisActivity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);

            switch (finishType) {
              case FINISH_NONE:
                // 默认显示返回至当前Activity的动画.
                triggerView.postDelayed(
                    new Runnable() {
                      @Override
                      public void run() {
                        Animator anim =
                            ViewAnimationUtils.createCircularReveal(view, cx, cy, finalRadius, 0);
                        anim.setDuration(finalDuration);
                        anim.addListener(
                            new AnimatorListenerAdapter() {
                              @Override
                              public void onAnimationEnd(Animator animation) {
                                super.onAnimationEnd(animation);
                                try {
                                  decorView.removeView(view);
                                } catch (Exception e) {
                                  e.printStackTrace();
                                }
                              }
                            });
                        anim.start();
                      }
                    },
                    1000);
                break;
              case FINISH_SINGLE:
                // finish当前activity
                thisActivity.finish();
                break;
              case FINISH_ALL:
                // finish目标activity外的所有activity
                thisActivity.finishAffinity();
                break;
            }
          }
        });
    anim.start();
  }
  @Override
  public Animator createAnimator(
      final ViewGroup sceneRoot,
      final TransitionValues startValues,
      final TransitionValues endValues) {
    if (startValues == null || endValues == null) return null;

    final Rect startBounds = (Rect) startValues.values.get(PROP_BOUNDS);
    final Rect endBounds = (Rect) endValues.values.get(PROP_BOUNDS);

    final boolean fromFab = endBounds.width() > startBounds.width();
    final View view = endValues.view;
    final Rect dialogBounds = fromFab ? endBounds : startBounds;
    final Rect fabBounds = fromFab ? startBounds : endBounds;
    final Interpolator fastOutSlowInInterpolator =
        AnimUtils.getFastOutSlowInInterpolator(sceneRoot.getContext());
    final long duration = getDuration();
    final long halfDuration = duration / 2;
    final long twoThirdsDuration = duration * 2 / 3;

    if (!fromFab) {
      // Force measure / layout the dialog back to it's original bounds
      view.measure(
          makeMeasureSpec(startBounds.width(), View.MeasureSpec.EXACTLY),
          makeMeasureSpec(startBounds.height(), View.MeasureSpec.EXACTLY));
      view.layout(startBounds.left, startBounds.top, startBounds.right, startBounds.bottom);
    }

    final int translationX = startBounds.centerX() - endBounds.centerX();
    final int translationY = startBounds.centerY() - endBounds.centerY();
    if (fromFab) {
      view.setTranslationX(translationX);
      view.setTranslationY(translationY);
    }

    // Add a color overlay to fake appearance of the FAB
    final ColorDrawable fabColor = new ColorDrawable(color);
    fabColor.setBounds(0, 0, dialogBounds.width(), dialogBounds.height());
    if (!fromFab) fabColor.setAlpha(0);
    view.getOverlay().add(fabColor);

    // Add an icon overlay again to fake the appearance of the FAB
    final Drawable fabIcon = ContextCompat.getDrawable(sceneRoot.getContext(), icon).mutate();
    final int iconLeft = (dialogBounds.width() - fabIcon.getIntrinsicWidth()) / 2;
    final int iconTop = (dialogBounds.height() - fabIcon.getIntrinsicHeight()) / 2;
    fabIcon.setBounds(
        iconLeft,
        iconTop,
        iconLeft + fabIcon.getIntrinsicWidth(),
        iconTop + fabIcon.getIntrinsicHeight());
    if (!fromFab) fabIcon.setAlpha(0);
    view.getOverlay().add(fabIcon);

    // Circular clip from/to the FAB size
    final Animator circularReveal;
    if (fromFab) {
      circularReveal =
          ViewAnimationUtils.createCircularReveal(
              view,
              view.getWidth() / 2,
              view.getHeight() / 2,
              startBounds.width() / 2,
              (float) Math.hypot(endBounds.width() / 2, endBounds.height() / 2));
      circularReveal.setInterpolator(
          AnimUtils.getFastOutLinearInInterpolator(sceneRoot.getContext()));
    } else {
      circularReveal =
          ViewAnimationUtils.createCircularReveal(
              view,
              view.getWidth() / 2,
              view.getHeight() / 2,
              (float) Math.hypot(startBounds.width() / 2, startBounds.height() / 2),
              endBounds.width() / 2);
      circularReveal.setInterpolator(
          AnimUtils.getLinearOutSlowInInterpolator(sceneRoot.getContext()));

      // Persist the end clip i.e. stay at FAB size after the reveal has run
      circularReveal.addListener(
          new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              view.setOutlineProvider(
                  new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                      final int left = (view.getWidth() - fabBounds.width()) / 2;
                      final int top = (view.getHeight() - fabBounds.height()) / 2;
                      outline.setOval(
                          left, top, left + fabBounds.width(), top + fabBounds.height());
                      view.setClipToOutline(true);
                    }
                  });
            }
          });
    }
    circularReveal.setDuration(duration);

    // Translate to end position along an arc
    final Animator translate =
        ObjectAnimator.ofFloat(
            view,
            View.TRANSLATION_X,
            View.TRANSLATION_Y,
            fromFab
                ? getPathMotion().getPath(translationX, translationY, 0, 0)
                : getPathMotion().getPath(0, 0, -translationX, -translationY));
    translate.setDuration(duration);
    translate.setInterpolator(fastOutSlowInInterpolator);

    // Fade contents of non-FAB view in/out
    List<Animator> fadeContents = null;
    if (view instanceof ViewGroup) {
      final ViewGroup vg = ((ViewGroup) view);
      fadeContents = new ArrayList<>(vg.getChildCount());
      for (int i = vg.getChildCount() - 1; i >= 0; i--) {
        final View child = vg.getChildAt(i);
        final Animator fade = ObjectAnimator.ofFloat(child, View.ALPHA, fromFab ? 1f : 0f);
        if (fromFab) {
          child.setAlpha(0f);
        }
        fade.setDuration(twoThirdsDuration);
        fade.setInterpolator(fastOutSlowInInterpolator);
        fadeContents.add(fade);
      }
    }

    // Fade in/out the fab color & icon overlays
    final Animator colorFade = ObjectAnimator.ofInt(fabColor, "alpha", fromFab ? 0 : 255);
    final Animator iconFade = ObjectAnimator.ofInt(fabIcon, "alpha", fromFab ? 0 : 255);
    if (!fromFab) {
      colorFade.setStartDelay(halfDuration);
      iconFade.setStartDelay(halfDuration);
    }
    colorFade.setDuration(halfDuration);
    iconFade.setDuration(halfDuration);
    colorFade.setInterpolator(fastOutSlowInInterpolator);
    iconFade.setInterpolator(fastOutSlowInInterpolator);

    // Work around issue with elevation shadows. At the end of the return transition the shared
    // element's shadow is drawn twice (by each activity) which is jarring. This workaround
    // still causes the shadow to snap, but it's better than seeing it double drawn.
    Animator elevation = null;
    if (!fromFab) {
      elevation = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, -view.getElevation());
      elevation.setDuration(duration);
      elevation.setInterpolator(fastOutSlowInInterpolator);
    }

    // Run all animations together
    final AnimatorSet transition = new AnimatorSet();
    transition.playTogether(circularReveal, translate, colorFade, iconFade);
    transition.playTogether(fadeContents);
    if (elevation != null) transition.play(elevation);
    if (fromFab) {
      transition.addListener(
          new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              // Clean up
              view.getOverlay().clear();
            }
          });
    }
    return new AnimUtils.NoPauseAnimator(transition);
  }