1. 梗概
!!! 拆分一個(gè)頁(yè)面的不同元素view,實(shí)現(xiàn)不同的animation...但是元素是 非共享的 !!!
content transition決定了非共享view元素在activity和fragment切換期間是如何進(jìn)入或者退出場(chǎng)景的峭火。根據(jù)google最新的Material Design設(shè)計(jì)語(yǔ)言,content transition讓我們毫不費(fèi)力的去協(xié)調(diào)Activity/Fragment切換過(guò)程中view的進(jìn)入和退出智嚷,讓這個(gè)過(guò)程更流暢卖丸。在5.0之后content transition可以通過(guò)調(diào)用Window和Fragment的如下代碼來(lái)設(shè)置:
setExitTransition() //當(dāng)A start B時(shí),使A中的View退出場(chǎng)景的transition
setEnterTransition() //當(dāng)A start B時(shí)盏道,使B中的View進(jìn)入場(chǎng)景的transition
setReturnTransition() //當(dāng)B 返回 A時(shí)稍浆,使B中的View退出場(chǎng)景的transition
setReenterTransition() //當(dāng)B 返回 A時(shí),使A中的View進(jìn)入場(chǎng)景的transition
//也可以直接在xml設(shè)置,通常都這么做
<item name="android:windowEnterTransition"></item>
<item name="android:windowExitTransition"></item>
<item name="android:windowReenterTransition"></item>
<item name="android:windowReturnTransition"></item>
以下圖為例猜嘱,演示了google play Games app如何通過(guò)content transition實(shí)現(xiàn)activity之間的平滑切換衅枫。當(dāng)?shù)诙€(gè)activity開(kāi)始的時(shí)候,enter transition讓用戶(hù)的頭像從底部邊緣慢慢滑入朗伶。而在activity退出的時(shí)候弦撩,屏幕被分成兩半,各自消失在上下邊緣论皆。
2. 那么怎么使用Content Transition呢?
2.1 xml實(shí)現(xiàn)
定義 界面指定元素的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
三益楼、定義 界面指定元素 或界面間共享元素 的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)基礎(chǔ)在Manifest.xml為相應(yīng)的Activity設(shè)置相對(duì)于的style
<style name="BaseAnimationAppTheme" parent="android:Theme.Material">
<!-- 1. 開(kāi)啟過(guò)渡效果-->
<item name="android:windowContentTransitions">true</item>
<!-- 2. 指定 界面元素 進(jìn)入/退出的動(dòng)畫(huà)效果,可以不全部實(shí)現(xiàn)(當(dāng)然可以在代碼中設(shè)置,前文已提)-->
<item name="android:windowEnterTransition">@anim/search_enter</item>
<item name="android:windowExitTransition">...</item>
<item name="android:windowReenterTransition">...</item>
<item name="android:windowReturnTransition">...</item>
/**還可以設(shè)置是否同步執(zhí)行還是順序執(zhí)行
*默認(rèn)情況下,material主題的應(yīng)用中enter/return的content transition
*會(huì)在exit/reenter的content transitions結(jié)束之前開(kāi)始播放(只是稍微早于)
*/
<!--A退出的動(dòng)畫(huà)和B進(jìn)入的動(dòng)畫(huà)同步進(jìn)行,代碼中setWindowAllowEnterTransitionOverlap()實(shí)現(xiàn)-->
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<!--B返回的動(dòng)畫(huà)和A重新進(jìn)入的動(dòng)畫(huà)同步進(jìn)行,代碼中setWindowAllowReturnTransitionOverlap()實(shí)現(xiàn)-->
<item name="android:windowAllowReturnTransitionOverlap">true</item>
</style>
- 啟動(dòng)Activity
ActivityOptionsCompat optionsCompat=ActivityOptionsCompat.makeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());
4.退出時(shí)調(diào)用finishAfterTransition()
注意
1点晴、Material主題默認(rèn)會(huì)將exit的transition設(shè)置成null偏形,enter的transition設(shè)置成Fade 。
2觉鼻、如果reenter 或者 return transition沒(méi)有明確設(shè)置俊扭,則將用exit 和enter的共享元素transition替代
2.2 Java代碼實(shí)現(xiàn)(不推薦)
1、activity的style中開(kāi)啟內(nèi)容過(guò)渡效果,并設(shè)置相應(yīng)的theme
<item name="android:windowContentTransitions">true</item>
<!--A退出的動(dòng)畫(huà)和B進(jìn)入的動(dòng)畫(huà)同步進(jìn)行-->
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<!--B返回的動(dòng)畫(huà)和A重新進(jìn)入的動(dòng)畫(huà)同步進(jìn)行-->
<item name="android:windowAllowReturnTransitionOverlap">true</item
2.啟動(dòng)activity B
ActivityOptionsCompat optionsCompat=ActivityOptionsCompat.makeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());
3坠陈、在B中使用內(nèi)容變換
Slide slide=new Slide(Gravity.BOTTOM);
slide.setDuration(500);
//內(nèi)容變換萨惑,不包括底部導(dǎo)航欄和狀態(tài)欄
slide.excludeTarget(android.R.id.navigationBarBackground, true);
slide.excludeTarget(android.R.id.statusBarBackground, true);
slide.excludeTarget(R.id.appBarLayout, true);
getWindow().setEnterTransition(slide);
getWindow().setReturnTransition(slide);
//也可以在xml文件設(shè)置transition,使用TransitionInflater得到Transition仇矾,如下:
Transition slide=TransitionInflater.from(this).inflateTransition(R.transition.slide_anim);
slide.setDuration(500);
slide.excludeTarget(R.id.appBarLayout, true);
getWindow().setEnterTransition(slide);
getWindow().setReturnTransition(slide);
4.return時(shí)調(diào)用finishAfterTransition() :非強(qiáng)制的
3. 深入分析
3.1 Activity A 調(diào)用startActivity()
1.framework遍歷A的View樹(shù)庸蔼,確定當(dāng)A的exit transition運(yùn)行時(shí)哪些view會(huì)退出場(chǎng)景(即哪些view是transitioning view)。
2.A的exit transition捕獲A中transitioning view的開(kāi)始狀態(tài)贮匕。
3.framework將A中所有的transitioning view設(shè)置為INVISIBLE姐仅。
4.A的exit transition捕獲到A中transitioning view的結(jié)束狀態(tài)。
5.A的exit transition比較每個(gè)transitioning view的開(kāi)始和結(jié)束狀態(tài)刻盐,然后根據(jù)前后狀態(tài)的區(qū)別創(chuàng)建一個(gè)Animator掏膏。Animator開(kāi)始運(yùn)行,同時(shí)transitioning view退出場(chǎng)景敦锌。
3.2 Activity B啟動(dòng)
1.framework遍歷B的View樹(shù)馒疹,確定當(dāng)B的enter transition運(yùn)行時(shí)哪些view會(huì)進(jìn)入場(chǎng)景,transitioning view會(huì)被初始化為INVISIBLE乙墙。
2.B的enter transition捕獲B中transitioning view的開(kāi)始狀態(tài)颖变。
3.framework將B中所有的transitioning view設(shè)置為VISIBLE生均。
4.B的enter transition捕獲到B中transitioning view的結(jié)束狀態(tài)。
5.B的enter transition比較每個(gè)transitioning view的開(kāi)始和結(jié)束狀態(tài)腥刹,然后根據(jù)前后狀態(tài)的區(qū)別創(chuàng)建一個(gè)Animator马胧。Animator開(kāi)始運(yùn)行,同時(shí)transitioning view進(jìn)入場(chǎng)景衔峰。
通過(guò)在每個(gè)transitioning view中來(lái)回切換INVISIBLE 和VISIBLE漓雅,framework確保content transition得到創(chuàng)建animation(期望的animation)所需的狀態(tài)信息。
3.3 監(jiān)聽(tīng)動(dòng)畫(huà)的變化過(guò)程
//當(dāng)然也可以監(jiān)聽(tīng) Return,Exit,Reenter時(shí)的動(dòng)畫(huà)
getWindow().getEnterTransition().addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
下面來(lái)看一個(gè)簡(jiǎn)單的實(shí)現(xiàn),先看效果
//AndroidManifest.xml
<activity
android:name=".ContentTransitionElementsActivity"
android:label="ContentTransitionElements"
android:theme="@style/AppTheme.ContentTransition" />
//style.xml
<style name="AppTheme.ContentTransition">
<item name="android:windowContentTransitions">true</item>
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowEnterTransition">@transition/enter_content_transition</item>
<item name="android:windowReturnTransition">@transition/return_content_transition</item>
</style>
enter_content_transition.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<targets >
<target android:excludeId="@android:id/statusBarBackground"/> <!--狀態(tài)欄-->
<target android:excludeId="@android:id/navigationBarBackground"/> <!--導(dǎo)航欄-->
</targets>
<!-- <fade android:duration="500">
<targets>
<target android:targetId="@android:id/statusBarBackground"/>
</targets>
</fade>-->
<slide android:slideEdge="left" android:startDelay="500">
<targets >
<target android:targetId="@id/tv_show"/>
</targets>
</slide>
<slide android:startDelay="700">
<targets >
<target android:targetId="@id/iv_left"/>
</targets>
</slide>
<slide android:startDelay="900">
<targets >
<target android:targetId="@id/iv_right"/>
</targets>
</slide>
</transitionSet>
return_content_transition.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="800">
<slide android:slideEdge="top">
<targets>
<target android:targetId="@id/relay_flag" />
<!--<target android:targetId="@id/fab"/>
<target android:targetId="@id/icon_gg"/>
<target android:targetId="@id/image_bg"/>-->
</targets>
</slide>
<slide android:slideEdge="bottom">
<targets>
<target android:targetId="@id/bottom_container" />
<target android:targetId="@id/tv_show"/>
<target android:targetId="@id/iv_left"/>
<target android:targetId="@id/iv_right"/>
</targets>
</slide>
<fade>
<targets >
<target android:targetId="@android:id/statusBarBackground"/>
</targets>
</fade>
</transitionSet>
public class ContentTransitionElementsActivity extends AppCompatActivity {
FloatingActionButton fab;
View image_bg;
ImageView icon_gg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_transition);
fab=findViewById(R.id.fab);
image_bg=findViewById(R.id.image_bg);
icon_gg=findViewById(R.id.icon_gg);
getWindow().getEnterTransition().addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
Animator circularReveal = ViewAnimationUtils.createCircularReveal(image_bg, image_bg.getWidth() / 2, image_bg.getHeight() / 2
, icon_gg.getWidth()/2, Math.max(image_bg.getWidth(), image_bg.getHeight()));
image_bg.setBackgroundColor(Color.BLACK);
circularReveal.setDuration(600);
circularReveal.start();
}
@Override
public void onTransitionEnd(Transition transition) {
fab.animate()
.scaleY(1)
.scaleX(1)
.start();
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
}
@Override
public void onBackPressed() {
finishAfterTransition();
super.onBackPressed();
}
}
由此可見(jiàn) :動(dòng)畫(huà)效果都在XML中實(shí)現(xiàn)了,Activity只要加載先關(guān)布局即可
代碼:animatedTransitionsLearn-master
Transition系列文章
一朽色、初識(shí)Transition—實(shí)現(xiàn)兩個(gè)場(chǎng)景的變換
二邻吞、番外篇 Transition之ViewOverlay
三、定義 界面指定元素 或界面間共享元素 的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)基礎(chǔ)
四葫男、Content Transition實(shí)現(xiàn)非共享元素轉(zhuǎn)場(chǎng)
五抱冷、SharedElementTransition之Activity間的轉(zhuǎn)場(chǎng)
六、SharedElementTransition之Fragment間的轉(zhuǎn)場(chǎng)
七梢褐、番外篇- 自定義Visibility
八旺遮、5.0以下實(shí)現(xiàn)共享轉(zhuǎn)場(chǎng)
本篇參考 :
深入理解Content Transition :建議去了解一下
animatedTransitionsLearn-master
Android轉(zhuǎn)場(chǎng)動(dòng)畫(huà)