前言
很早的時候算是看過一些關於Android 5.0的新特性,但是當時也就是記錄一下用法,也沒後續去看,前段時間看到一個動畫切換效果,我還傻乎乎的去寫,後來發現居然是Android自帶的,找了些資料算是補習了!
本篇包括Transition
、Shared Element
、Circular Reveal
的內容。
Transition
Transition是Android 5.0新加入的過渡動畫效果,包括Explode
、Slide
和Fade
Explode | Slide | Fade |
---|---|---|
從中心移入或移出 | 從邊緣移入或移出 | 調整透明度產生漸變 |
在講解這些功能之前我們需要知道幾個方法
setEnterTransition(Transition transition)
設置當前頁面進入的過渡動畫setReturnTransition(Transition transition)
設置當前頁面返回的過渡動畫setExitTransition(Transition transition)
設置當前頁面退出的過渡動畫setReenterTransition(Transition transition)
設置當前頁面重啓的過渡動畫
這幾個方法是設置過渡動畫的時候需要注意的。
Explode
我們這邊需要做一個A -> B 然後B進行Explode過渡動畫的效果設置。
邏輯如下:
A頁面進入B頁面 B頁面需要有過渡動畫 所以只要對B頁面設置setEnterTransition
方法和setReturnTransition
方法即
可。
調用方法也要傳入對應的代碼纔有效
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Explode");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
當直接使用ActivityOptions.makeSceneTransitionAnimation(activity)
的時候在低於5.0以下的時候軟件會直接崩潰,所以可以使用ActivityOptionsCompat
來兼容低版本,但是這樣設置只是讓低版本不會崩潰,並不會有過渡動畫效果,後面我會用ActivityOptionsCompat
做演示。
在接受到的頁面加入以下判斷
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
case "Explode":
Explode explode = new Explode();
setDuration(explode);
getWindow().setEnterTransition(explode);
break;
}
}
至此就實現了功能,不設置setReturnTransition
系統會自動調用進入的反向動畫來實現!
Slide
和上面的Explode一樣我們只要加入以下代碼
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
在需要展示的頁面加入判斷,不過由於Slide是需要設置到底從哪個方向的所以可以加入方向
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
...
case "Slide":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
setDuration(slide);
getWindow().setEnterTransition(slide);
break;
}
}
Fade
這個過渡也沒有特別的直接調用以下代碼就可以了
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Fade");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
設置展示
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
...
case "Fade":
Fade fade = new Fade();
setDuration(fade);
getWindow().setEnterTransition(fade);
break;
}
}
補充setReturnTransition
至此可以發現使用特別簡單。下面我們設置setReturnTransition
方法來實現進入和退出不一樣的效果
可以看到進入是一個有回彈效果的,並且退出的方向也和進入的相同了!
打開頁面的代碼也沒有特別的變化
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide2");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
展示的代碼
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
...
case "Slide2":
Slide slide2 = new Slide();
slide2.setSlideEdge(Gravity.RIGHT);
setDuration(slide2);
//回彈效果
slide2.setInterpolator(new BounceInterpolator());
getWindow().setEnterTransition(slide2);
break;
}
}
前面的代碼都沒有特別的變化!
只是在最後返回的時候對setReturnTransition
進行了修改。
@Override
public void onBackPressed() {
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 如果沒有 return transition 被定義,將使用反進入的動畫
switch (transition) {
case "Slide2":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.LEFT);
setDuration(slide);
getWindow().setReturnTransition(slide);
break;
}
//一定要調用finishAfterTransition
finishAfterTransition();
} else {
super.onBackPressed();
}
}
這邊需要注意的就是調用的關閉頁面方法一定要是finishAfterTransition
纔會執行過渡動畫。
Shared Element
上面講完了Transition
現在來說下Shared Element
就是共享元素。
首先我們看下效果
這就是元素共享的界面過渡動畫效果,下面我們看下如何實現!
單獨元素並且不傳遞圖片
這次我們使用ActivityCompat
和 ActivityOptionsCompat
來實現 這是爲了兼容低版本,讓低版本不崩潰而已!效果是沒辦法實現!
首先佈局頁面代碼爲
<TextView
android:id="@+id/tv_1_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="@android:color/holo_blue_bright"
android:gravity="center"
android:text="點擊單獨放大"
android:transitionName="Open"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
我們發現佈局上面加入了一段android:transitionName="Open"
這是爲了讓元素進行共享的起點。
打開頁面的代碼如下
tv1 = findViewById(R.id.tv_1_ase);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, tv1,
ViewCompat.getTransitionName(tv1));
ActivityCompat.startActivity(activity, intent, compat.toBundle());
我們對比上面的代碼會發現也是調用makeSceneTransitionAnimation
方法,不過傳遞的不在是一個Explode
這類對象了,而是傳遞了一個view 並且還傳遞了TransitionName
,這個可以直接寫或者取!
之後是我們需要展示效果的頁面
需要進行過渡動畫的View佈局代碼
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="200dp"
android:transitionName="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/homepage_banner2" />
這樣我們兩邊就都有了一個android:transitionName="Open"
這樣進行跳轉之後系統就會幫我們綁定兩個View
對於展示頁面我們可以不需要寫任何代碼!
多個元素並且不傳遞圖片
多個元素其實也是一樣的,只不過傳遞的view多了而已!
首先還是佈局,我們寫入了兩個transitionName
進入前的佈局
<TextView
android:id="@+id/tv_1_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="@android:color/holo_blue_bright"
android:gravity="center"
android:text="點擊單獨放大"
android:transitionName="Open"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_2_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:text="點擊放大2個"
android:transitionName="Open2"
app:layout_constraintStart_toEndOf="@+id/tv_1_ase"
app:layout_constraintTop_toTopOf="@+id/tv_1_ase" />
進入後的佈局
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="200dp"
android:transitionName="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/homepage_banner2" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:transitionName="Open2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:srcCompat="@mipmap/homepage_banner1" />
這邊各自對應了各自的android:transitionName
跳轉代碼
Pair<View, String> p1 = new Pair<View, String>(tv1, ViewCompat.getTransitionName(tv1));
Pair<View, String> p2 = new Pair<View, String>(tv2, ViewCompat.getTransitionName(tv2));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, p1, p2);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
我們創建多個Pair
對象在傳入就可以了
元素傳遞圖片
上面都是內部本身就有了圖片,外部只是一個位置提供而已,那麼能夠在外部傳入圖片進去麼。是可以的,其實效果只是告訴第二個頁面進入之後你的圖片需要設置什麼而已,實際其實是一樣的!
我們新增兩個按鈕佈局
<TextView
android:id="@+id/tv_3_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定義圖片1"
android:transitionName="Open3"
app:layout_constraintStart_toStartOf="@+id/tv_1_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_1_ase" />
<TextView
android:id="@+id/tv_4_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定義圖片2"
android:transitionName="Open3"
app:layout_constraintEnd_toEndOf="@+id/tv_2_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_2_ase" />
並且在跳轉頁面也加入一個佈局用於展示
<ImageView
android:id="@+id/imageView3"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:transitionName="Open3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
這是一個沒有設置圖片的View,當進入之後才設置圖片
跳轉代碼如下
tv3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv3, ViewCompat.getTransitionName(tv3))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher_round);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv4, ViewCompat.getTransitionName(tv4))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
這次我們就需要設置圖片了,所以展示頁面代碼如下
img3 = findViewById(R.id.imageView3);
imgId = getIntent().getIntExtra("imgid", -1);
if (imgId != -1) {
Glide.with(this).load(imgId).into(img3);
}
這樣設置之後我們進入之後就是傳遞的圖片了!
注:當然也可以是網絡圖片,不過由於網絡的原因,所以建議是直接先傳在跳轉前的圖片,進入之後等網絡自己刷新新的圖片就好了!
補充1 監聽兩個跳轉頁面直接的關聯
有可能會做一個輪播的點擊跳轉的,然後內部切換之後返回回來就會發現位置明顯不對或者外部的輪播圖片並未改變,這個時候我們只要加入監聽就好了!
邏輯如下:
A 在a圖片的時候 進入 B 並且打開了a圖片,這個時候B 切換了下圖片變成了b圖片,這個時候返回需要告訴A圖片換了。
首先在A的onCreate中加入監聽
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (bundle != null) {
int id = bundle.getInt("imgId");
String url = bundle.getString("imgUrl");
Log.e("-s-", "id = " + id);
Log.e("-s-", "url = " + url);
bundle = null;
}
}
});
然後實現Activity的onActivityReenter
方法,即頁面跳轉回來之後判斷傳回來的data
@Override
public void onActivityReenter(int resultCode, Intent data) {
Log.e("-s-", "resultCode = " + resultCode);
if (resultCode == 101) {
bundle = new Bundle(data.getExtras());
}
}
至此A頁面的監聽寫完了,再來看下B頁面的傳遞信息
將傳遞信息的代碼寫在finishAfterTransition
方法中
@Override
public void finishAfterTransition() {
Intent data = new Intent();
data.putExtra("imgId", imgId);
data.putExtra("imgUrl", imgUrl);
setResult(101, data);
super.finishAfterTransition();
}
這樣就實現了兩個Activity之間的傳遞信息功能!
補充2 將Transition
和Shared Element
結合下
實現多種結合的過渡動畫,效果如下
跳轉代碼
tv9.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("transition", "Slide+");
intent.putExtra("imgurl", imgs[0]);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv9, ViewCompat.getTransitionName(tv9))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
和上面比就是加入了一些參數而已。
實現頁面代碼
img3 = findViewById(R.id.imageView3);
imgId = getIntent().getIntExtra("imgid", -1);
imgUrl = getIntent().getStringExtra("imgurl");
transition = getIntent().getStringExtra("transition");
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switch (transition) {
case "Slide+":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
getWindow().setEnterTransition(slide);
break;
}
}
if (imgUrl != null) {
Glide.with(this).load(imgUrl).into(img3);
} else if (imgId != -1) {
Glide.with(this).load(imgId).into(img3);
}
就是將Transition和SharedElement簡單結合而已。
Circular Reveal
這個效果是在當前頁面進行一個擴散過渡。但是可以做到加入到跳轉動畫中!
效果如下
基本說明
先說下最基本的使用說明
其實就是調用ViewAnimationUtils.createCircularReveal
創建一個Animator
,說白了就是一個Animator
,只不過官方提供了一個擴散效果動畫給你而已_(:з」∠)_
createCircularReveal
提供了5個參數View view, int centerX, int centerY, float startRadius, float endRadius
,分別代表,需要執行的動畫是哪個View,起始位置的X,Y, 開始圓的半徑, 結束圓的半徑!
這邊擴散整個佈局的代碼
private void show(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
效果的邏輯很簡單
A 以SharedElement打開 B ,之後立馬執行CircularReveal擴散整個根佈局, 退出B的時候必須先執行完CircularReveal動畫在關閉頁面。
代碼如下
跳轉代碼
tv10.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, CircularRevealActivity.class);
intent.putExtra("CircularReveal", "green");
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv10, ViewCompat.getTransitionName(tv10))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
執行效果頁面代碼
CircularRevealActivity.java
package com.gjn.testproject;
import android.animation.Animator;
import android.app.SharedElementCallback;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CircularRevealActivity extends AppCompatActivity {
View root;
View v1, v2, v3;
boolean in = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circular_reveal);
in = false;
root = findViewById(R.id.root_acr);
v1 = findViewById(R.id.v1);
v2 = findViewById(R.id.v2);
v3 = findViewById(R.id.v3);
String circularReveal = getIntent().getStringExtra("CircularReveal");
if (circularReveal != null) {
switch (circularReveal) {
case "green":
root.setBackgroundResource(android.R.color.holo_green_light);
break;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
if (!in) {
in = true;
show(v1);
}
}
});
}
}
v1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_green_light);
show(v1);
}
});
v2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_red_dark);
show(v2);
}
});
v3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_blue_light);
show(v3);
}
});
}
@Override
public void onBackPressed() {
if (in && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
hide(v1);
} else {
super.onBackPressed();
}
}
private void show(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
private void hide(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, r, 0);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
root.setBackgroundColor(Color.TRANSPARENT);
finishAfterTransition();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
具體就看代碼吧!其實就是做了兩個動畫而已!
全部代碼
activity_shared_element.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gjn.testproject.SharedElementActivity">
<TextView
android:id="@+id/tv_1_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="@android:color/holo_blue_bright"
android:gravity="center"
android:text="點擊單獨放大"
android:transitionName="Open"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_2_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:text="點擊放大2個"
android:transitionName="Open2"
app:layout_constraintStart_toEndOf="@+id/tv_1_ase"
app:layout_constraintTop_toTopOf="@+id/tv_1_ase" />
<TextView
android:id="@+id/tv_3_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定義圖片1"
android:transitionName="Open3"
app:layout_constraintStart_toStartOf="@+id/tv_1_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_1_ase" />
<TextView
android:id="@+id/tv_4_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定義圖片2"
android:transitionName="Open3"
app:layout_constraintEnd_toEndOf="@+id/tv_2_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_2_ase" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_ase"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_3_ase" />
<TextView
android:id="@+id/tv_5_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:gravity="center"
android:text="Explode"
app:layout_constraintStart_toEndOf="@+id/tv_2_ase"
app:layout_constraintTop_toTopOf="@+id/tv_2_ase" />
<TextView
android:id="@+id/tv_6_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:gravity="center"
android:text="Slide1"
app:layout_constraintStart_toEndOf="@+id/tv_5_ase"
app:layout_constraintTop_toTopOf="@+id/tv_5_ase" />
<TextView
android:id="@+id/tv_7_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:gravity="center"
android:text="Fade"
app:layout_constraintStart_toStartOf="@+id/tv_5_ase"
app:layout_constraintTop_toTopOf="@+id/tv_4_ase" />
<TextView
android:id="@+id/tv_8_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:gravity="center"
android:text="Slide2"
app:layout_constraintStart_toStartOf="@+id/tv_6_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_6_ase" />
<TextView
android:id="@+id/tv_9_ase"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:transitionName="Open3"
android:text="Slide+SharedElements"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rv_ase" />
<TextView
android:id="@+id/tv_10_ase"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:text="CircularReveal1"
android:transitionName="Open4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_9_ase" />
</android.support.constraint.ConstraintLayout>
activity_shared.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gjn.testproject.SharedActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="200dp"
android:transitionName="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/homepage_banner2" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:transitionName="Open2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:srcCompat="@mipmap/homepage_banner1" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:transitionName="Open3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
activity_circular_reveal.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_acr"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gjn.testproject.CircularRevealActivity">
<TextView
android:id="@+id/v1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="150dp"
android:transitionName="Open4"
android:background="@android:color/holo_green_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/v2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="32dp"
android:layout_marginEnd="32dp"
android:background="@android:color/holo_red_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/v3"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginEnd="24dp"
android:background="@android:color/holo_blue_light"
app:layout_constraintBottom_toBottomOf="@+id/v2"
app:layout_constraintEnd_toStartOf="@+id/v2" />
</android.support.constraint.ConstraintLayout>
SharedElementActivity.java
package com.gjn.testproject;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.SharedElementCallback;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.gjn.baserecycleradapterlibrary.BaseRecyclerAdapter;
import com.gjn.baserecycleradapterlibrary.RecyclerViewHolder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class SharedElementActivity extends AppCompatActivity {
TextView tv1, tv2, tv3, tv4, tv5, tv6, tv7, tv8, tv9, tv10;
Activity activity;
String[] imgs = {"https://ws1.sinaimg.cn/large/0065oQSqgy1fxd7vcz86nj30qo0ybqc1.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqgy1fwyf0wr8hhj30ie0nhq6p.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqgy1fwgzx8n1syj30sg15h7ew.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqly1fw8wzdua6rj30sg0yc7gp.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqly1fuh5fsvlqcj30sg10onjk.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqly1fubd0blrbuj30ia0qp0yi.jpg"};
Bundle bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// //設置允許通過ActivityOptions.makeSceneTransitionAnimation發送或者接收Bundle
// getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
// //設置使用TransistionManager進行動畫,不設置的話系統會使用一個默認的TransitionManager
// getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_shared_element);
activity = this;
tv1 = findViewById(R.id.tv_1_ase);
tv2 = findViewById(R.id.tv_2_ase);
tv3 = findViewById(R.id.tv_3_ase);
tv4 = findViewById(R.id.tv_4_ase);
tv5 = findViewById(R.id.tv_5_ase);
tv6 = findViewById(R.id.tv_6_ase);
tv7 = findViewById(R.id.tv_7_ase);
tv8 = findViewById(R.id.tv_8_ase);
tv9 = findViewById(R.id.tv_9_ase);
tv10 = findViewById(R.id.tv_10_ase);
RecyclerView RV = findViewById(R.id.rv_ase);
List<String> list = new ArrayList<>();
list.add("圖片1");
list.add("圖片2");
list.add("圖片3");
list.add("圖片4");
list.add("圖片5");
list.add("圖片6");
RV.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
BaseRecyclerAdapter<String> adapter = new BaseRecyclerAdapter<String>(activity, R.layout.item_ase, list) {
@Override
public void bindData(RecyclerViewHolder holder, String s, int i) {
holder.setTextViewText(R.id.tv_ia, s);
}
};
RV.setAdapter(adapter);
adapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int i) {
TextView textView = view.findViewById(R.id.tv_ia);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(textView, ViewCompat.getTransitionName(textView))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgurl", imgs[i]);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
setClick();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (bundle != null) {
int id = bundle.getInt("imgId");
String url = bundle.getString("imgUrl");
Log.e("-s-", "id = " + id);
Log.e("-s-", "url = " + url);
bundle = null;
}
}
});
}
}
@Override
public void onActivityReenter(int resultCode, Intent data) {
Log.e("-s-", "resultCode = " + resultCode);
if (resultCode == 101) {
bundle = new Bundle(data.getExtras());
}
}
private void setClick() {
tv1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, SharedActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity, tv1,
ViewCompat.getTransitionName(tv1)).toBundle());
}
});
tv2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Pair<View, String> p1 = new Pair<View, String>(tv1, ViewCompat.getTransitionName(tv1));
Pair<View, String> p2 = new Pair<View, String>(tv2, ViewCompat.getTransitionName(tv2));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, p1, p2);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv3, ViewCompat.getTransitionName(tv3))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher_round);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv4, ViewCompat.getTransitionName(tv4))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Explode");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Fade");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv8.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide2");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv9.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("transition", "Slide+");
intent.putExtra("imgurl", imgs[0]);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv9, ViewCompat.getTransitionName(tv9))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv10.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, CircularRevealActivity.class);
intent.putExtra("CircularReveal", "green");
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv10, ViewCompat.getTransitionName(tv10))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
}
}
TransitionActivity.java
package com.gjn.testproject;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.transition.Explode;
import android.transition.Fade;
import android.transition.Slide;
import android.transition.Transition;
import android.view.Gravity;
import android.view.animation.BounceInterpolator;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class TransitionActivity extends AppCompatActivity {
private String transition;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
case "Explode":
Explode explode = new Explode();
setDuration(explode);
getWindow().setEnterTransition(explode);
break;
case "Slide":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
setDuration(slide);
getWindow().setEnterTransition(slide);
break;
case "Slide2":
Slide slide2 = new Slide();
slide2.setSlideEdge(Gravity.RIGHT);
setDuration(slide2);
//回彈效果
slide2.setInterpolator(new BounceInterpolator());
getWindow().setEnterTransition(slide2);
break;
case "Fade":
Fade fade = new Fade();
setDuration(fade);
getWindow().setEnterTransition(fade);
break;
}
}
}
private void setDuration(Transition transition) {
transition.setDuration(800);
}
@Override
public void onBackPressed() {
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 如果沒有 return transition 被定義,將使用反進入的動畫
switch (transition) {
// case "Explode":
// Explode explode = new Explode();
// getWindow().setReturnTransition(explode);
// break;
// case "Slide":
// Slide slide = new Slide();
// slide.setSlideEdge(Gravity.RIGHT);
// getWindow().setReturnTransition(slide);
// break;
case "Slide2":
// Slide slide2 = new Slide();
// slide2.setSlideEdge(Gravity.RIGHT);
// //回彈效果
// slide2.setInterpolator(new BounceInterpolator());
// getWindow().setReturnTransition(slide2);
Slide slide = new Slide();
slide.setSlideEdge(Gravity.LEFT);
setDuration(slide);
getWindow().setReturnTransition(slide);
break;
// case "Fade":
// Fade fade = new Fade();
// getWindow().setReturnTransition(fade);
// break;
}
//一定要調用finishAfterTransition
finishAfterTransition();
} else {
super.onBackPressed();
}
}
}
SharedActivity.java
package com.gjn.testproject;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.transition.Slide;
import android.view.Gravity;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
public class SharedActivity extends AppCompatActivity {
int imgId;
String imgUrl;
String transition;
ImageView img3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared);
img3 = findViewById(R.id.imageView3);
imgId = getIntent().getIntExtra("imgid", -1);
imgUrl = getIntent().getStringExtra("imgurl");
transition = getIntent().getStringExtra("transition");
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switch (transition) {
case "Slide+":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
getWindow().setEnterTransition(slide);
break;
}
}
if (imgUrl != null) {
Glide.with(this).load(imgUrl).into(img3);
} else if (imgId != -1) {
Glide.with(this).load(imgId).into(img3);
}
}
@Override
public void finishAfterTransition() {
Intent data = new Intent();
data.putExtra("imgId", imgId);
data.putExtra("imgUrl", imgUrl);
setResult(101, data);
super.finishAfterTransition();
}
}
CircularRevealActivity.java
package com.gjn.testproject;
import android.animation.Animator;
import android.app.SharedElementCallback;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CircularRevealActivity extends AppCompatActivity {
View root;
View v1, v2, v3;
boolean in = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circular_reveal);
in = false;
root = findViewById(R.id.root_acr);
v1 = findViewById(R.id.v1);
v2 = findViewById(R.id.v2);
v3 = findViewById(R.id.v3);
String circularReveal = getIntent().getStringExtra("CircularReveal");
if (circularReveal != null) {
switch (circularReveal) {
case "green":
root.setBackgroundResource(android.R.color.holo_green_light);
break;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
if (!in) {
in = true;
show(v1);
}
}
});
}
}
v1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_green_light);
show(v1);
}
});
v2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_red_dark);
show(v2);
}
});
v3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_blue_light);
show(v3);
}
});
}
@Override
public void onBackPressed() {
if (in && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
hide(v1);
} else {
super.onBackPressed();
}
}
private void show(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
private void hide(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, r, 0);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
root.setBackgroundColor(Color.TRANSPARENT);
finishAfterTransition();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
資料
【Transition】Android炫酷的Activity切換效果,共享元素
Activity間的跳轉動畫—Transition
使用Circular Reveal爲你的應用添加揭露動畫效果