譯文的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來實現
Activity B
<?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里面獲取在屏幕位置好像會有偏差
當然只是粗糙簡單實現一下,細節沒有去深究,效果比原版差不少,重要的是看到別人的動畫時,培養如何思考的這個過程。