在開始之前吐槽下簡書markdown竟然不支持生成目錄列表,弄半天沒弄出來,如果哪位知道,煩請告知。這里就簡單的截圖一下目錄,講究著看吧....

1. 轉(zhuǎn)場動(dòng)畫
轉(zhuǎn)場動(dòng)畫就是Activity通過元素之間的轉(zhuǎn)換提供不同狀態(tài)之間的視覺連接。你可以為進(jìn)入和退出轉(zhuǎn)換以及Activity之間共享元素的轉(zhuǎn)換指定定制動(dòng)畫
1.1 Api21之前如何實(shí)現(xiàn)轉(zhuǎn)場動(dòng)畫?
Api21之前我們實(shí)現(xiàn)轉(zhuǎn)場動(dòng)畫有兩種方式。
1.1.1 使用 overridePendingTransition
進(jìn)入動(dòng)畫
startActivity(intent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
退出動(dòng)畫
finish();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
注意:
1.overridePendingTransition方法必須在startActivity()或者finish()方法的后面。
2.如果參數(shù)是0,表示沒有動(dòng)畫。
1.1.2 使用 Activity主題Style配置
假設(shè)有兩個(gè)Activity A和B。
A->B activityOpenEnterAnimation
B->A activityOpenExitAnimation
B退出A從新進(jìn)入 activityCloseEnterAnimation
A退出 activityCloseExitAnimation
主題配置
<style name="AppThisTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowAnimationStyle">@style/activityAnimation</item>
</style>
<style name="activityAnimation" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/slide_right_in</item>
<item name="android:activityOpenExitAnimation">@anim/slide_left_out</item>
<item name="android:activityCloseEnterAnimation">@anim/slide_left_in</item>
<item name="android:activityCloseExitAnimation">@anim/slide_right_out</item>
</style>
1.2 Api21之后如何實(shí)現(xiàn)轉(zhuǎn)場動(dòng)畫?
在說Api21之后實(shí)現(xiàn)轉(zhuǎn)場動(dòng)畫之前先來看一張圖,來理清跳轉(zhuǎn)Activity之間跳轉(zhuǎn)設(shè)置的動(dòng)畫方法。
1.2.1 Activity轉(zhuǎn)換動(dòng)畫原理

從圖中可以看到Activity之間跳轉(zhuǎn)可以有4個(gè)不同動(dòng)作。
一般如果沒有特殊需求,指定兩個(gè)就可以了,
exitTransition和enterTransition。 設(shè)置進(jìn)入和退出動(dòng)畫時(shí),在進(jìn)行returnTransition時(shí),如果沒有設(shè)置就會(huì)用renterTransition動(dòng)畫設(shè)置的值動(dòng)作相反,同理在進(jìn)行reenterTransition時(shí),如果沒有設(shè)置就會(huì)用exitTransition動(dòng)畫設(shè)置的值動(dòng)作相反。
上面四個(gè)動(dòng)作其實(shí)是View INVISIBLE 到 VISIBLE 或者 VISIBLE 到 INVISIBLE 轉(zhuǎn)換過程。
exitTransition: A退出錢先獲取試圖為VISIBLE場景,設(shè)置試圖為INVISIBLE獲取INVISIBLE場景,根據(jù)transition差異的不同創(chuàng)建執(zhí)行動(dòng)畫。
enterTransition:B進(jìn)入時(shí)會(huì)把B中試圖設(shè)置為INVISIBLE獲取INVISIBLE 場景,然后將視圖設(shè)置為VISIBLE,獲取VISIBLE時(shí)的場景,根據(jù)transition差異的不同創(chuàng)建執(zhí)行動(dòng)畫。
同理returnTransition 和 renterTransition 原理一樣。
根據(jù)上面描述Activity場景轉(zhuǎn)換動(dòng)畫時(shí)建立在Visibility基礎(chǔ)上,支持Visibility有三種:
explode:爆炸效果(將視圖從場景的中心移出或移出)
slide:移動(dòng)效果(將視圖從場景的一個(gè)邊緣移動(dòng)或移出,類似于目前項(xiàng)目中常用的activity切換動(dòng)畫,從右邊進(jìn),從左邊出。Slide還支持上面和下面進(jìn)出)
fade :淡入淡出 。
Api21之后實(shí)現(xiàn)轉(zhuǎn)場動(dòng)畫也有兩種方式
1.2.2 使用 Activity主題Style配置
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<!--必須制定該屬性不然動(dòng)畫不起作用-->
<item name="android:windowActivityTransitions">true</item>
<!--activity進(jìn)入動(dòng)畫-->
<item name="android:windowEnterTransition">@transition/slide_right</item>
<!--activity退出動(dòng)畫-->
<item name="android:windowExitTransition">@transition/slide_left</item>
<!--是否同時(shí)執(zhí)行,如果同時(shí)執(zhí)行A頁面動(dòng)畫還沒退出,B頁面已經(jīng)開始動(dòng)畫,感覺不是很和諧-->
<item name="android:windowAllowReturnTransitionOverlap">false</item>
<item name="android:windowAllowEnterTransitionOverlap">false</item>
</style>
注意:
- 如果使用主題繼承自android:Theme.Material系列主題,則無需指定windowActivityTransitions,默認(rèn)windowActivityTransitions屬性為true。
- 使用主題指定activity進(jìn)入和退出動(dòng)畫創(chuàng)建的xml 在 res/transition/ 文件下。
- 啟動(dòng)一個(gè)activity應(yīng)使用兼容方式啟動(dòng)
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this);
ActivityCompat.startActivity(MainActivity.this,
new Intent(MainActivity.this, TransitionSlideActivity.class), optionsCompat.toBundle());
啟動(dòng)一個(gè)Activity使用Api21之前的方法是沒有任何效果轉(zhuǎn)換動(dòng)畫效果的。
4.退出一個(gè)Activity使用兼容方式退出
ActivityCompat.finishAfterTransition(TransitionSlideActivity.this);
/res/transition/slide_left 文件內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:duration="@android:integer/config_shortAnimTime"
android:slideEdge="left">
<targets>
<target
android:excludeId="@android:id/statusBarBackground"
tools:targetApi="lollipop" />
</targets>
</slide>
/res/transition/slide_right 文件內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:duration="@android:integer/config_shortAnimTime"
android:slideEdge="right">
<targets>
<target
android:excludeId="@android:id/statusBarBackground"
tools:targetApi="lollipop" />
</targets>
</slide>
duration:表示動(dòng)畫時(shí)長
slideEdge:表示從哪邊進(jìn)入或退出取值有l(wèi)eft|top|right|bottom|
targets:標(biāo)記作用,例如上面例子中標(biāo)記排除系統(tǒng)狀態(tài)欄,其他View都應(yīng)用于轉(zhuǎn)場動(dòng)畫中。除了 排除 某個(gè)View,還有targetId 只針對某個(gè)View,其他View都不作用于轉(zhuǎn)場動(dòng)畫。同理explode和fade也支持排除和針對某個(gè)View。
slide|explode|fade 之間還可以兩兩組合,比如退出的動(dòng)畫使用從左邊退出和淡出。
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:duration="@android:integer/config_longAnimTime"
android:transitionOrdering="together">
<fade android:fadingMode="fade_in_out" />
<slide android:slideEdge="right" />
<targets>
<target
android:excludeId="@android:id/statusBarBackground"
tools:targetApi="lollipop" />
</targets>
</transitionSet>
效果圖如下:

<center> 右進(jìn)左出并且?guī)в械龅胄Ч?lt;/center >
1.2.3 代碼設(shè)置轉(zhuǎn)場動(dòng)畫
Activity A:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupWindowAnimations();
}
findViewById(R.id.btn_transition_slide_animation).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this);
ActivityCompat.startActivity(MainActivity.this,
new Intent(MainActivity.this, TransitionSlideActivity.class), optionsCompat.toBundle());
}
});
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void setupWindowAnimations() {
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(300);
//一起動(dòng)畫
transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
Slide slideTransition = new Slide();
slideTransition.setSlideEdge(Gravity.LEFT);
transitionSet.addTransition(slideTransition);
Fade fadeTransition = new Fade();
transitionSet.addTransition(fadeTransition);
//排除狀態(tài)欄
transitionSet.excludeTarget(android.R.id.statusBarBackground, true);
//是否同時(shí)執(zhí)行
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
//退出這個(gè)界面
getWindow().setExitTransition(slideTransition);
}
Activity B:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slide_transition);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupWindowAnimations();
}
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.finishAfterTransition(TransitionSlideActivity.this);
}
});
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void setupWindowAnimations() {
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(300);
//一起動(dòng)畫
transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
Slide slideTransition = new Slide();
slideTransition.setSlideEdge(Gravity.RIGHT);
transitionSet.addTransition(slideTransition);
Fade fadeTransition = new Fade();
transitionSet.addTransition(fadeTransition);
//排除狀態(tài)欄
transitionSet.excludeTarget(android.R.id.statusBarBackground, true);
//是否同時(shí)執(zhí)行
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
//進(jìn)入
getWindow().setEnterTransition(slideTransition);
}
具體explode效果代碼和效果圖就不貼了可以自己去嘗試看看。
2. 共享元素動(dòng)畫
在說Activity之間共享動(dòng)畫之前還是來一張官方圖

2.1 描述
共享動(dòng)畫是分析兩個(gè)界面共享view的尺寸,位置,樣式的不同創(chuàng)建動(dòng)畫化的。從上圖看出Activity1到Activity2,Android小機(jī)器人是一個(gè)被共享的元素。由于兩個(gè)頁面共享元素尺寸位置不一樣,所以實(shí)現(xiàn)效果就是Activity1小機(jī)器人被放大然后顯示在Activity2上,當(dāng)然還可以有Fade效果。關(guān)于尺寸改變動(dòng)畫可以使用ChangeBounds,另外還有
ChangeTransform,ChangeClipBounds以及ChangeImageTransform。
關(guān)于四種ChangeXXX解釋如下:
- ChangeBounds:檢測view的位置邊界創(chuàng)建移動(dòng)和縮放動(dòng)畫
- ChangeTransform:檢測view的scale和rotation創(chuàng)建縮放和旋轉(zhuǎn)動(dòng)畫
- ChangeClipBounds:檢測view的剪切區(qū)域的位置邊界,和ChangeBounds類似。不過ChangeBounds針對的是view而ChangeClipBounds針對的是view的剪切區(qū)域(setClipBound(Rect rect) 中的rect)。如果沒有設(shè)置則沒有動(dòng)畫效果
- ChangeImageTransform:檢測ImageView(這里是專指ImageView)的尺寸,位置以及ScaleType,并創(chuàng)建相應(yīng)動(dòng)畫。
說那么多不如來張上面效果圖的動(dòng)圖:

2.2 實(shí)現(xiàn)共享動(dòng)畫
一般情況下兩個(gè)Activity ShareElementActivity 和 ShareElement1Activity ,假如ShareElementActivity->ShareElement1Activity,配置ShareElement1Activity 中 進(jìn)入的共享動(dòng)畫,ShareElementActivity中配置退出轉(zhuǎn)場動(dòng)畫即可。
假設(shè)兩個(gè)Activity:ShareElementActivity 和 ShareElement1Activity。
ShareElementActivity 示例代碼
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_element);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupWindowAnimations();
}
final ImageView shareElement = findViewById(R.id.iv_share_element);
shareElement.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//共享shareElement這個(gè)View
ActivityOptionsCompat activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(ShareElementActivity.this, shareElement,
"shareElement");
ActivityCompat.startActivity(ShareElementActivity.this,
new Intent(ShareElementActivity.this, ShareElement1Activity.class), activityOptionsCompat.toBundle());
}
});
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.finishAfterTransition(ShareElementActivity.this);
}
});
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void setupWindowAnimations() {
//爆炸效果進(jìn)入進(jìn)出
Explode explodeTransition = new Explode();
explodeTransition.setDuration(300);
//排除狀態(tài)欄
explodeTransition.excludeTarget(android.R.id.statusBarBackground, true);
//是否同時(shí)執(zhí)行
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
//進(jìn)入
getWindow().setEnterTransition(explodeTransition);
}
上述代碼是從ShareElementActivity到ShareElement1Activity 其中共享的View是
shareElement,transitionName是shareElement,這個(gè)transitionName是開啟共享動(dòng)畫的重要因素,通過,transitionName 指定的值來匹配下個(gè)頁面共享的View。
ShareElement1Activity 示例代碼
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_element1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupWindowAnimations();
}
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.finishAfterTransition(ShareElement1Activity.this);
}
});
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void setupWindowAnimations() {
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(300);
//排除狀態(tài)欄
changeBounds.excludeTarget(android.R.id.statusBarBackground, true);
//是否同時(shí)執(zhí)行
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
//進(jìn)入
getWindow().setEnterTransition(changeBounds);
}
@Override
public void onBackPressed() {
ActivityCompat.finishAfterTransition(ShareElement1Activity.this);
}
可以看到幾乎都類似,只不過兩個(gè)Activity中進(jìn)入退出動(dòng)畫不一樣,需要注意的地方就是返回該頁面不能用finish,而是用finishAfterTransition,不然動(dòng)畫不起作用,還有在ShareElement1Activity的布局文件中必須制定共享View的transitionName屬性。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical"
tools:context=".share.ShareElement1Activity">
...
<ImageView
android:id="@+id/iv_share_element1"
...
android:transitionName="shareElement"
tools:targetApi="lollipop" />
...
</RelativeLayout>
如果實(shí)現(xiàn)多個(gè)共享View動(dòng)畫使用以下偽代碼獲取ActivityOptions
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
Pair.create(view1, "transitionName1"),
Pair.create(view2, "transitionName2"));
當(dāng)然Activity設(shè)置主題屬性也可以實(shí)現(xiàn)共享動(dòng)畫只需在該Activity主題中配置
<item name="android:windowSharedElementEnterTransition">..</item>
<item name="android:windowSharedElementExitTransition">...</item>
3. 實(shí)戰(zhàn)
可以看到這張gif圖item跳轉(zhuǎn)用到共享元素動(dòng)畫,在第二個(gè)詳情頁面共享動(dòng)畫進(jìn)入后開啟頁面內(nèi)其他元素透明+放大 動(dòng)畫。關(guān)閉詳情頁面依次反向執(zhí)行開啟的流程:詳情頁面描述和標(biāo)題還有圖片左上角的關(guān)閉按鈕依次執(zhí)行透明+縮放動(dòng)畫,等這些操作執(zhí)行完畢,執(zhí)行finishAfterTransition,共享元素自會(huì)回到之前列表中開啟的位置。

實(shí)現(xiàn)代碼就不貼了,點(diǎn)我查看代碼