译文的GitHub地址:低版本实现共享元素动画(二):格瓦拉动画
译者注:好久没折腾动画了,一开始就停不下来了
刚写完低版本实现共享元素动画,ios的同事丢过来手机告诉我格瓦拉app的一个动画很好看,我一看还真不错,就问android版本的实现呢,打开他的香槟金小米5以为是和ios一样的实现,可惜只实现了一半,盯得动画看了一会,发现有点像是android material设计动画,然后他又给我看了好几个app的动画,一看我就发现了,ios好多app动画都是android material设计里面的,而且不少是material的错误示范,哈哈
像格瓦拉app这个动画,看过google的material设计,会很熟悉,但是这个动画 用户等待内容的时间太长了,体验并不是那么的好,google不推荐。先不讨论对不对了,我们来简单实现一下吧
主要步骤
主要流程其实和上一篇动画差不多,只不过B页面多了一个Reveal动画,这个动画我们用一个三方库来实现
compile ('com.github.ozodrukh:CircularReveal:2.0.1@aar') {
transitive = true;
}
我们来分析一下B页面,可以看到有好几层,在android中这个可以用RelativeLayout来实现
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--layer 1-->
<LinearLayout
android:id="@+id/bg_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="160dp"
android:background="@color/gray">
</ImageView>
</LinearLayout>
<!--layer 2-->
<io.codetail.widget.RevealFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/container_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/blue"
android:orientation="vertical"
android:visibility="invisible">
<ImageView
android:layout_width="match_parent"
android:layout_height="160dp"
android:background="@color/colorPrimary">
</ImageView>
</LinearLayout>
</io.codetail.widget.RevealFrameLayout>
<!--layer 3-->
<ImageView
android:id="@+id/iv_detail"
android:layout_width="80dp"
android:layout_height="120dp"
android:layout_marginLeft="40dp"
android:layout_marginTop="100dp"
android:background="@color/colorAccent"/>
</RelativeLayout>
动画的流程是
- layer 2默认不可见
- layer 3 共享元素移动
- layer 2 开始reveal动画
也就是在上一篇的基础上再加个动画
private void runEnterAnimation() {
destinationView.setVisibility(View.VISIBLE);
destinationView.animate()
.setDuration(DEFAULT_DURATION)
.setInterpolator(DEFAULT_INTERPOLATOR)
.scaleX(1f)
.scaleY(1f)
.translationX(0)
.translationY(0)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
revealOn();//添加reval动画
}
})
.start();
}
private void revealOn() {
int cx = destinationView.getRight() - destinationView.getWidth() / 2;
int cy = destinationView.getTop() + destinationView.getHeight() / 2;
float radius = (float) Math.hypot(containerView.getWidth(), containerView.getHeight());
Animator animator = ViewAnimationUtils.createCircularReveal(containerView, cx, cy, 0, radius);
animator.setInterpolator(DEFAULT_INTERPOLATOR);
animator.setDuration(900);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
containerView.setVisibility(View.VISIBLE);
}
});
animator.start();
}
退出的话 以进入动画相反顺序执行
@Override
public void onBackPressed() {
revealOff();
}
public void revealOff() {
int cx = destinationView.getRight() - destinationView.getWidth() / 2;
int cy = destinationView.getTop() + destinationView.getHeight() / 2;
float radius = (float) Math.hypot(containerView.getWidth(), containerView.getHeight());
Animator animator = ViewAnimationUtils.createCircularReveal(containerView, cx, cy, radius, 0);
animator.setInterpolator(DEFAULT_INTERPOLATOR);
animator.setDuration(900);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
containerView.setVisibility(View.INVISIBLE);
runExitAnimation();
}
});
animator.start();
}
private void runExitAnimation() {
destinationView.animate()
.setDuration(DEFAULT_DURATION)
.setInterpolator(DEFAULT_INTERPOLATOR)
.scaleX(scaleX)
.scaleY(scaleY)
.translationX(deltaX)
.translationY(deltaY)
/* .setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
bgView.setVisibility(View.INVISIBLE);
finish();
overridePendingTransition(0, R.anim.fade_exit);
}
})*/
.withEndAction(new Runnable() {
@Override
public void run() {
bgView.setVisibility(View.INVISIBLE);
finish();
overridePendingTransition(0, R.anim.fade_exit);
}
})
.start();
}
最后效果会发现
- activity b退出的时候效果还是不理想 这可能也是格瓦拉android不实现退出动画的原因,我觉得这个真想实现的话,通过alpha退出是可以实现和ios一样效果的
- 动画差值器尤其重要,上面退出动画其实控制好也可以实现 我全部用的AccelerateDecelerateInterpolator 效果很生硬
- RecycleView Item里面获取在屏幕位置好像会有偏差
当然只是粗糙简单实现一下,细节没有去深究,效果比原版差不少,重要的是看到别人的动画时,培养如何思考的这个过程。