一、前言
Circular Reveal Animations,官方称之为循环揭露动画效果,是一种用来显示/隐藏一组UI界面元素的动画效果,它是在API 21引入的,对应的类是ViewAnimationUtils
。
循环揭露动画效果可以和共享元素变换动画组合,用来创造一些有意义的动画效果,自然地告诉用户这个app有些什么东西,将会产生怎样的效果。
二、效果图
三、实现
在上面的例子中,依次发生了:
- 橘色的圆是一个共享元素,从
MainActivity
变换到CircularRevealActivity
; - 在
CircularRevealActivity
中有一个监听器(listener),用来监听共享元素转换动画的结束,当动画结束时,做了这么两件事:- 为Toolbar执行了一个循环揭露动画
- 为
CircularRevealActivity
中的视图(Views)执行了一个放大动画,使用的是以前的ViewPropertyAnimator
类
监听共享元素进入动画的结束
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
animateRevealShow(mToolbar);
animateButtonsIn();
}
...
});
animateRevealShow(mToolbar)
private void animateRevealShow(View viewRoot) {
int centerX = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int centerY = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
int endRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());
Animator animator = ViewAnimationUtils.createCircularReveal(viewRoot, centerX, centerY, 0, endRadius);
viewRoot.setVisibility(View.VISIBLE);
animator.setDuration(1000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
}
上述方法的重点是createCircularReveal (View view, int centerX, int centerY, float startRadius, float endRadius)
:
view
:要执行循环揭露动画的View
centerX
:循环揭露动画中心位置的X坐标
centerY
:循环揭露动画中心位置的Y坐标
startRadius
:循环揭露动画的起始半径
endRadius
:循环揭露动画的结束半径
animateButtonsIn()
private void animateButtonsIn() {
for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
View child = bgViewGroup.getChildAt(i);
child.animate()
.setStartDelay(100 + i*DELAY)
.setInterpolator(interpolator)
.alpha(1)
.scaleX(1)
.scaleY(1);
}
}
上述方法为底部的4个圆执行了一个放大动画,使用ViewPropertyAnimator
类。
四、更多
还有一些不同的方式来创建循环揭露动画,关键是使用动画效果让用户更好地理解这个app有些什么东西,将会产生怎样的效果。
1. 从目标视图的中心创建循环揭露动画
public void revealGreenAtMiddle(View view) {
int centerX = (bgViewGroup.getLeft() + bgViewGroup.getRight()) / 2;
int centerY = (bgViewGroup.getTop() + bgViewGroup.getBottom()) / 2;
int endRadius = (int) Math.hypot(bgViewGroup.getWidth()/2, bgViewGroup.getHeight()/2);
Animator animator = ViewAnimationUtils.createCircularReveal(bgViewGroup, centerX, centerY, 0, endRadius);
bgViewGroup.setBackgroundResource(R.color.green);
animator.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
2. 从目标视图的顶部创建循环揭露动画+底部按钮动画
public void revealBlueAtTop(View view) {
animateButtonsOut();
int centerX = (bgViewGroup.getLeft() + bgViewGroup.getRight()) / 2;
int centerY = 0;
int endRadius = (int) Math.hypot(bgViewGroup.getWidth()/2, bgViewGroup.getHeight());
Animator animator = ViewAnimationUtils.createCircularReveal(bgViewGroup, centerX, centerY, 0, endRadius);
bgViewGroup.setBackgroundResource(R.color.blue);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
animateButtonsIn();
}
...
});
animator.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
此处动画效果经历了以下3个步骤:
- 隐藏底部按钮(通过控制按钮的透明度、缩放比例)
- 从顶部执行循环揭露动画
- 监听器监听到揭露动画执行完后,显示底部按钮(还是通过控制按钮的透明度、缩放比例)
3. 在点击位置创建循环揭露动画
首先,给橘色圆添加触摸监听事件,获取点击到的橘色圆的位置坐标:
findViewById(R.id.iv_square_orange).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.iv_square_orange) {
revealOrangeAtPoint(event.getRawX(), event.getRawY());
}
return false;
}
});
接着,就跟前面一样了,根据获取到的坐标位置创建循环揭露动画:
private void revealOrangeAtPoint(float rawX, float rawY) {
int centerX = (int) rawX;
int centerY = (int) rawY;
int endRadius = (int) Math.hypot(bgViewGroup.getWidth(), bgViewGroup.getHeight());
Animator animator = ViewAnimationUtils.createCircularReveal(bgViewGroup, centerX, centerY, 0, endRadius);
bgViewGroup.setBackgroundResource(R.color.orange);
animator.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
4. 属性变化动画+循环揭露动画
这个会难那么一丢丢,毕竟是两个动画效果的组合技,但是只要抓住上一篇讲的属性变化
动画和上面讲的循环揭露
动画这两个点,就不难理解了。
private void revealRedAtCenter() {
final ViewGroup.LayoutParams originalParams = ivSquareRed.getLayoutParams();
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
int centerX = (bgViewGroup.getLeft() + bgViewGroup.getRight()) / 2;
int centerY = (bgViewGroup.getTop() + bgViewGroup.getBottom()) / 2;
int endRadius = (int) Math.hypot(bgViewGroup.getWidth(), bgViewGroup.getHeight());
Animator animator = ViewAnimationUtils.createCircularReveal(bgViewGroup, centerX, centerY, 0, endRadius);
bgViewGroup.setBackgroundResource(R.color.red);
animator.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
ivSquareRed.setLayoutParams(originalParams);
}
...
});
TransitionManager.beginDelayedTransition(bgViewGroup, transition);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
ivSquareRed.setLayoutParams(params);
}
五、总结
本篇的重点就1个内容:
createCircularReveal (View view, int centerX, int centerY, float startRadius, float endRadius)
只要抓住这两条主线,其它的内容都可以按主线来抽丝拨茧,一切难题都可以迎刃而解。
项目代码已分享到Github:https://github.com/SherlockShi/AndroidMaterialAnimationPractise
六、参考资料
PS:欢迎关注SherlockShi博客
网友评论