Android轉(zhuǎn)場(chǎng)動(dòng)畫深度解析
前言
所謂轉(zhuǎn)場(chǎng)動(dòng)畫,通俗的講就是一個(gè)Activity跳轉(zhuǎn)到另一個(gè)Activity是的動(dòng)畫;Activity的轉(zhuǎn)場(chǎng)動(dòng)畫很早就有了,5.0之前用的是overridePendingTransition()這個(gè)方法。在5.0之后,Google使用Material Design設(shè)計(jì)風(fēng)格,進(jìn)而有了的新的轉(zhuǎn)場(chǎng)轉(zhuǎn)場(chǎng)動(dòng)畫的誕生,效果還是挺炫酷的;今天我們就來(lái)講解下。
一、轉(zhuǎn)場(chǎng)動(dòng)畫兼容性
1、API21之前Activity過(guò)渡動(dòng)畫使用
API21之前Activity過(guò)渡動(dòng)畫通過(guò)兩種方式來(lái)實(shí)現(xiàn):style主題里面統(tǒng)一設(shè)置、使用代碼overridePendingTransition函數(shù)單獨(dú)設(shè)置;
style文件主題里面統(tǒng)一定義,全局為所有Activity設(shè)置過(guò)渡動(dòng)畫效果;
- <item name="android:windowAnimationStyle">@style/Animation.Activity.Customer</item>
- <style name="Animation.Activity.Customer" parent="@android:style/Animation.Activity">
- <!-- 進(jìn)入一個(gè)新的Activity的時(shí)候,A->B B進(jìn)入動(dòng)畫 -->
- <item name="android:activityOpenEnterAnimation">@anim/right_in</item>
- <!-- 進(jìn)入一個(gè)新的Activity的時(shí)候,A->B A退出動(dòng)畫 -->
- <item name="android:activityOpenExitAnimation">@anim/left_out</item>
- <!-- 退出一個(gè)Activity的時(shí)候,B返回到A A進(jìn)入動(dòng)畫 -->
- <item name="android:activityCloseEnterAnimation">@anim/left_in</item>
- <!-- 退出一個(gè)Activity的時(shí)候,B返回到A B退出動(dòng)畫 -->
- <item name="android:activityCloseExitAnimation">@anim/right_out</item>
- </style>
代碼overridePendingTransition(enterAnim, exitAnim);
關(guān)于overridePendingTransition函數(shù),有一個(gè)需要注意的地方就是:它必需緊挨著startActivity()或者finish()或者onBackPressed()函數(shù)調(diào)用,否則不一定有效果;
2、API 21 之后Activity過(guò)渡動(dòng)畫使用
在API 21之后google又推出了一種比之前效果更加贊的過(guò)渡動(dòng)畫;
通過(guò)ActivityOptions + Transition來(lái)實(shí)現(xiàn)Activity過(guò)渡動(dòng)畫;
所以在使用之前需要進(jìn)行版本判斷。當(dāng)版本API 大于21時(shí)使用轉(zhuǎn)場(chǎng)動(dòng)畫,否則不使用;
- // Check if we're running on Android 5.0 or higher
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- // Apply activity transition
- } else {
- // Swap without transition
- }
ActivityOptions + Transition來(lái)實(shí)現(xiàn)Activity過(guò)渡動(dòng)畫之前先來(lái)了看下ActivityOptions里面幾個(gè)函數(shù)代表啥意思;
- /**
- * 和overridePendingTransition類似,設(shè)置跳轉(zhuǎn)時(shí)候的進(jìn)入動(dòng)畫和退出動(dòng)畫
- */
- public static ActivityOptions makeCustomAnimation(Context context, int enterResId, int exitResId);
- /**
- * 通過(guò)把要進(jìn)入的Activity通過(guò)放大的效果過(guò)渡進(jìn)去
- * 舉一個(gè)簡(jiǎn)單的例子來(lái)理解source=view,startX=view.getWidth(),startY=view.getHeight(),startWidth=0,startHeight=0
- * 表明新的Activity從view的中心從無(wú)到有慢慢放大的過(guò)程
- */
- public static ActivityOptions makeScaleUpAnimation(View source, int startX, int startY, int width, int height);
- /**
- * 通過(guò)放大一個(gè)圖片過(guò)渡到新的Activity
- */
- public static ActivityOptions makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY);
- /**
- * 場(chǎng)景動(dòng)畫,體現(xiàn)在兩個(gè)Activity中的某些view協(xié)同去完成過(guò)渡動(dòng)畫效果,等下在例子中能更好的看到效果
- */
- public static ActivityOptions makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName);
- /**
- * 場(chǎng)景動(dòng)畫,同上是對(duì)多個(gè)View同時(shí)起作用
- */
- public static ActivityOptions makeSceneTransitionAnimation(Activity activity, android.util.Pair<View, String>... sharedElements);
對(duì)于Transition Activity過(guò)渡動(dòng)畫的使用,我們簡(jiǎn)單的分為三個(gè)步驟:告訴系統(tǒng)以Transition的方式啟動(dòng)Activity、定義過(guò)渡動(dòng)畫、設(shè)置過(guò)渡動(dòng)畫。
3、轉(zhuǎn)場(chǎng)動(dòng)畫的使用場(chǎng)景
Android中的轉(zhuǎn)場(chǎng)動(dòng)畫主要有三種場(chǎng)景:
- 在兩個(gè)activity之間切換時(shí)界面的過(guò)渡效果;
- 兩個(gè)activity或者Fragment之間shared elements 切換效果;
下面分別詳細(xì)的介紹。
二、轉(zhuǎn)場(chǎng)動(dòng)畫的使用場(chǎng)景
1、兩個(gè)activity之間切換時(shí)界面的過(guò)渡效果
兩個(gè)activity切換時(shí)的,有兩個(gè)動(dòng)畫,從activity A 切換到activity B時(shí),會(huì)有A的退出動(dòng)畫和B的進(jìn)入動(dòng)畫;
在Google提供的android.transition.Transition包中從activity A切換到activity B有三種方式:Explode, Slide 和Fade;
- Explode:從屏幕的中間進(jìn)入或退出;
- Slide:從屏幕的一邊向另一邊進(jìn)入或退出;
- Fade:通過(guò)改變透明度來(lái)出現(xiàn)或消失;
上面的三種動(dòng)畫有兩種實(shí)現(xiàn)方式:
①通過(guò)xml聲明;
在res目錄下新建transition文件夾在transition文件夾下新建activity_fade.xml文件;
activity_fade.xml
- <?xml version="1.0" encoding="utf-8"?>
- <fade xmlns:android="http://schemas.android.com/apk/res/"
- android:duration="1000"/>
activity_slide.xml
- <?xml version="1.0" encoding="utf-8"?>
- <slide xmlns:android="http://schemas.android.com/apk/res/"
- android:duration="1000"/>
ActivityA的代碼如下:因?yàn)閺腁ctivityA切換到ActivityB,所以ActivityA是退出動(dòng)畫使用的方法是:getWindow().setExitTransition(slide);
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_transition);
- setupWindowAnimations();
- }
- private void setupWindowAnimations() {
- Slide slide = TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
- getWindow().setExitTransition(slide);
- }
ActivityB是進(jìn)入動(dòng)畫使用方法:getWindow().setEnterTransition(fade);,ActivityB的代碼如下
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_transition);
- setupWindowAnimations();
- }
- private void setupWindowAnimations() {
- Fade fade = TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
- getWindow().setEnterTransition(fade);
- }
②代碼方式
ActivityA代碼如下:實(shí)現(xiàn)一個(gè)Slide對(duì)象并且設(shè)置時(shí)間為1000毫秒
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_transition);
- setupWindowAnimations();
- }
- private void setupWindowAnimations() {
- Slide slide = new Slide();
- slide.setDuration(1000);
- getWindow().setExitTransition(slide);
- }
ActivityB中實(shí)現(xiàn)一個(gè)Fide對(duì)象并且設(shè)置時(shí)間為1000毫秒;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_transition);
- setupWindowAnimations();
- }
- private void setupWindowAnimations() {
- Fade fade = new Fade();
- fade.setDuration(1000);
- getWindow().setEnterTransition(fade);
- }
2、兩個(gè)activity或者Fragment之間shared elements 切換效果
Shared elements轉(zhuǎn)換確定兩個(gè)Activity之間共享的視圖如何在這兩個(gè)Activity之間轉(zhuǎn)換;
如果兩個(gè)Activity在不同的位置和大小中具有相同的圖像,則通過(guò)Shared elements轉(zhuǎn)換會(huì)在這兩個(gè)Activity之間平滑地轉(zhuǎn)換和縮放圖像;
當(dāng)從Activity A跳轉(zhuǎn)到Activity B時(shí),ActivityA, ActivityB中的兩個(gè)item有動(dòng)畫變化,但是要注意的時(shí)ActivityA ,ActivityB中的item是兩個(gè)獨(dú)立的item;
shared elements轉(zhuǎn)換包括以下幾種:
- changeBounds 改變目標(biāo)布局中view的邊界
- changeClipBounds 裁剪目標(biāo)布局中view的邊界
- changeTransform 實(shí)現(xiàn)旋轉(zhuǎn)或者縮放動(dòng)畫
- changeImageTransform 實(shí)現(xiàn)目標(biāo)布局中ImageView的旋轉(zhuǎn)或者縮放動(dòng)畫
實(shí)現(xiàn)上面的效果需要三個(gè)步驟:
① Window Content Transition
設(shè)置styles.xml文件,允許windowContentTransitions如下:
- value/style.xml
- <style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
- ...
- <item name="android:windowContentTransitions">true</item
- ...
- </style>
②定義一個(gè)相同的transition名稱
分別在Activity A 和Activity B的布局文件中定義item,這兩個(gè)item的屬性可以不一樣,但是android:transitionName必須一樣。如下:
- <ImageView
- android:id="@+id/small_blue_icon"
- style="@style/MaterialAnimations.Icon.Small"
- android:src="@drawable/circle"
- android:transitionName="@string/blue_name" />
activity_b.xml
- <ImageView
- android:id="@+id/big_blue_icon"
- style="@style/MaterialAnimations.Icon.Big"
- android:src="@drawable/circle"
- android:transitionName="@string/blue_name" />
③在activity中啟動(dòng)shared element
使用ActivityOptions.makeSceneTransitionAnimation()方法
ActivityA.java
- blueIconImageView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent i = new Intent(MainActivity.this, SharedElementActivity.class);
- View sharedView = blueIconImageView;
- String transitionName = getString(R.string.blue_name);
- ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
- startActivity(i, transitionActivityOptions.toBundle());
- }
- });
Fragment之間Shared elements
Fragment之間的Shared elements的使用過(guò)程和Activity之間的類似,分為三個(gè)步驟:
①允許windowContentTransitions
- <style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
- ...
- <item name="android:windowContentTransitions">true</item>
- ...
- </style>
②定義一個(gè)共同的變換名稱
layout/fragment_a.xml
- <ImageView
- android:id="@+id/small_blue_icon"
- style="@style/MaterialAnimations.Icon.Small"
- android:src="@drawable/circle"
- android:transitionName="@string/blue_name" />
layout/fragment_b.xml
- <ImageView
- android:id="@+id/big_blue_icon"
- style="@style/MaterialAnimations.Icon.Big"
- android:src="@drawable/circle"
- android:transitionName="@string/blue_name" />
③使用FragmentTransaction
- FragmentB fragmentB = FragmentB.newInstance(sample);
- // Defines enter transition for all fragment views
- Slide slideTransition = new Slide(Gravity.RIGHT);
- slideTransition.setDuration(1000);
- sharedElementFragment2.setEnterTransition(slideTransition);
- // Defines enter transition only for shared element
- ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
- fragmentB.setSharedElementEnterTransition(changeBoundsTransition);
- getFragmentManager().beginTransaction()
- .replace(R.id.content, fragmentB)
- .addSharedElement(blueView, getString(R.string.blue_name))
- .commit();