1. 程式人生 > >Android 使用ActivityOptions實現Activity轉場動畫

Android 使用ActivityOptions實現Activity轉場動畫

之前一直都是用這種方式實現Activity的轉場動畫:

// MainActivity
overridePendingTransition(enterAnim, exitAnim);

從Android5.0之後,Google提供了一種新的方式來實現:ActivityOptions。

前提
在使用前,需要宣告允許使用ActivityOptions。
在styles.xml檔案,設定App主題時,新增android:windowContentTransitions的屬性,屬性值為true:

<?xml version="1.0" encoding="utf-8"?>
<resources> <style name="AppTheme" parent="Theme.AppCompat.Light"> <item name="android:windowContentTransitions">true</item> </style> </resources>

注意:這個是5.0才支援的屬性,應該放到values-v21資料夾下。不過,即使我不提醒你,編譯器也不會放過你的,哈哈!!

使用及效果
ActivityOptionsCompat是ActivityOptions的相容包,這個類可以幫助我們實現Activity轉場動畫。

ActivityOptionsCompat的方法不多,有如下幾個:

makeCustomAnimation(Context context, int enterResId, int exitResId)

makeScaleUpAnimation(View source, int startX, int startY, int startWidth, int startHeight)

makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)

makeClipRevealAnimation(View source
, int startX, int startY, int width, int height) makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName) makeSceneTransitionAnimation(Activity activity, Pair<View, String>… sharedElements)

接下來我們一一介紹這幾個方法的使用方法和效果吧。

makeCustomAnimation
轉場效果:

使用者自己定義,看下去就明白了。
1
使用方法:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.anim_activity_in, R.anim.anim_activity_out);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());

makeCustomAnimation方法需要3個引數(結合例子):

this:Context型別,也就是Activity。
R.anim.anim_activity_in:int型別,新Activity顯示動畫。
R.anim.anim_activity_out:int型別,當前Activity退出動畫。
這後2個引數有木有想到什麼,沒錯,相當於Activity的:

overridePendingTransition(enterAnim, exitAnim);

如果你要使用這種方式,還不如用Activity原生的overridePendingTransition方法轉場。

makeScaleUpAnimation
轉場效果:

新Activity會以某個點為中心,從某個大小開始逐漸放大到最大。
1
使用方法:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeScaleUpAnimation(view, view.getWidth() / 2, view.getHeight() / 2, 0, 0);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());

makeScaleUpAnimation需要5個引數(結合例子):

view:View型別,指定從哪個View的座標開始放大。
view.getWidth() / 2:int型別,指定以View的X座標為放大中心的X座標。
view.getHeight() / 2:int型別,指定以View的Y座標為放大中心的Y座標。
0:int型別,指定放大前新Activity是多寬。
0:int型別,指定放大前新Activity是多高。
makeThumbnailScaleUpAnimation
轉場效果:

放大一個圖片,最後過度到一個新activity(我測試的時候,效果不明顯)
1
使用方法:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeThumbnailScaleUpAnimation(view, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 0, 0);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());

makeThumbnailScaleUpAnimation需要4個引數(結合例子):

view:View型別,要放大的圖片從哪個View的左上角的座標作為中心點放大。
BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher):Bitmap型別,指定要放大的圖片。
0:放大前圖片的初始寬度。
0:放大前圖片的初始高度。
makeClipRevealAnimation
轉場效果:

與makeScaleUpAnimation效果差不多,可能是時間太短的問題,看不清楚
1
使用方式:

ActivityOptionsCompat compat4 = ActivityOptionsCompat.makeClipRevealAnimation(view, view.getWidth() / 2, view.getHeight() / 2, 0, 0);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat4.toBundle());

引數同makeScaleUpAnimation相同,不做詳解。

makeSceneTransitionAnimation(重點)
轉場效果:

兩個Activity的兩個View協同完成轉場,也就是原Activity的View過度到新Activity的View,原新兩個Activity的View的transitionName相同。
1
使用例項:

// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context="com.johan.demo.MainActivity">

    ...

    <ImageView
        android:id="@+id/ImgView1"
        android:layout_width="160dp"
        android:layout_height="90dp"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" <!-- 注意 -->
        />

</LinearLayout>

// activity_second.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    ...

    <ImageView
        android:id="@+id/ImgView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" <!-- 注意 -->
        />

</LinearLayout>

// strings.xml
<resources>
    <string name="tu">tu</string>
</resources>

// MainActivity使用ActivityOptions
...
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(this, imgView, getString(R.string.tu));
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());
...

注意,activity_main.xml和activity_second.xml檔案都有一個ImageView,他們的android:transitionName的值是一樣的(@string/tu),當使用makeSceneTransitionAnimation進行轉場時,ImgView1會慢慢的過渡到ImgView2。過渡的效果,有很多種情況,例子中的過渡效果是ImgView1會逐漸放大,並移動到ImgView2的位置,同時顯示SecondActivity(新Activity)。

makeSceneTransitionAnimation方法需要3個引數(結合例子):

this:Context型別,指定Activity。
imgView:View型別,指定從哪裡開始過渡。例子中是ImgView1過渡到ImgView2,所以是ImgView1。
getString(R.string.tu):String型別,指定android:transitionName的值,過渡View的標誌。
我想說,這種效果很酷的,一定要自己試一下!!!

makeSceneTransitionAnimation(重點)
轉場效果:

makeSceneTransitionAnimation是單個View協作,makeSceneTransitionAnimation允許多個View協作,效果和makeSceneTransitionAnimation像相似。
1
使用例項:

// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context="com.johan.demo.MainActivity">

    ...

    <ImageView
        android:id="@+id/ImgView1"
        android:layout_width="160dp"
        android:layout_height="90dp"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" <!-- 注意 -->
        />

    <TextView
        android:id="@+id/TxtView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#00ff00"
        android:textSize="16sp"
        android:text="SceneTransitionAnimation2 Text String"
        android:transitionName="@string/zi" <!-- 注意 -->
        />

</LinearLayout>

// activity_second.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    ...

    <ImageView
        android:id="@+id/ImgView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:src="@drawable/tu"
        android:transitionName="@string/tu" <!-- 注意 -->
        />

    <TextView
        android:id="@+id/TxtView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#0000ff"
        android:textSize="16sp"
        android:text="SceneTransitionAnimation2 Text String"
        android:transitionName="@string/zi" <!-- 注意 -->
        />

</LinearLayout>

// strings.xml
<resources>
    <string name="tu">tu</string>
    <string name="zi">zi</string>
</resources>

// MainActivity使用ActivityOptions
...
Pair<View, String> imgViewPair = Pair.create(imgView, getString(R.string.tu));
Pair<View, String> txtViewPair = Pair.create(txtView, getString(R.string.zi));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(this, imgViewPair, txtViewPair);
ActivityCompat.startActivity(this, new Intent(this, SecondActivity.class), compat.toBundle());
...

注意,transitionName值一定要一一對應哦!!

定義轉場動畫
除了上面動畫,Android還支援我們定義轉場動畫。

目前支援3種Activity進退場// explode(分解)
從場景中心移入或移出檢視

// slide(滑動)
從場景邊緣移入或移出檢視

// fade(淡出)
通過調整透明度在場景中增添或移除檢視支援4種共享元素(也就是transitionName相同的View [ 不同的Activity ])過渡動畫:

// changeBounds
改變目標檢視的佈局邊界

// changeClipBounds
裁剪目標檢視邊界

// changeTransform
改變目標檢視的縮放比例和旋轉角度

// changeImageTransform
改變目標圖片的大小和縮放比例

使用例項
使用方式有2種:

  • 通過xml設定
  • 通過程式碼設定

通過xml設定
(1)定義轉場和過渡動畫的xml

在res資料夾下新建transition資料夾,然後在res/transition資料夾下新建xml檔案:

// transition_explode.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
</transitionSet>

// transition_fade.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <fade android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
</transitionSet>

上面我定義了2個轉場動畫,當然你還可以定義共享元素的過渡動畫,這裡只是簡單定義了一下。

(2)設定SecondActivity的轉場動畫

因為將要啟動的SecondActivity,那我們就定義SecondActivity主題時,設定SecondActivity進出場動畫:

<!-- 繼承AppTheme,因為裡面定義了android:windowContentTransitions屬性,允許我們使用ActivityOptions -->
<style name="SecondActivityTheme" parent="AppTheme">
    <!-- 進場(Activity進入時)動畫 -->
    <item name="android:windowEnterTransition">@transition/transition_explode</item>
    <!-- 出場(Activity退出時)動畫,其實是錯誤的,下面會改正 -->
    <item name="android:windowExitTransition">@transition/transition_fade</item>
</style>

然後設定SecondActivity的主題為SecondActivityTheme。

(3)轉場
在MainActivity跳轉到SecondActivity:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    // 我們這裡沒有用ActivityCompat轉場
    startActivity(new Intent(this, SecondActivity.class), ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

因為只有5.0才支援,所以稍微做一下判斷。

通過程式碼設定
通過程式碼設定,就不一定要定義xml,因為程式碼中也有方法支援,待會再說。

(1)定義轉場和過渡動畫的xml(不一定需要,你可以定義)

在res資料夾下新建transition資料夾,然後在res/transition資料夾下新建xml檔案:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
    <fade android:duration="1000" android:interpolator="@android:interpolator/accelerate_decelerate" />
</transitionSet>

(2)在程式碼中設定

如果我們定義了轉場動畫xml檔案,可以在SecondActivity的onCreate方法這麼設定:

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.transition_explode);
            getWindow().setEnterTransition(transition);
        }
    }
}

可以利用TransitionInflater獲取到我們定義的Transition動畫。

如果沒有定義,可以這麼設定:

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            getWindow().setEnterTransition(new Explode());
        }
    }
}

new Explode()為Android提供的方法,還有其他的,自己慢慢體會。

遇到的坑
這裡我有個疑問,無論是xml方式設定,還是通過程式碼設定,我只設定了進場動畫,為什麼出場動畫也一樣?而且如果我設定了出場動畫,好像也沒有效果??

原來是我設定的不對!!!

我天真的以為設定android:windowExitTransition屬性是SecondActivity退出來的動畫!!!

我檢視到有部落格解釋了進出場相關資料,才明白:

// setEnterTransition() 
// android:windowEnterTransition  A start B時,使B中的View進入場景的transitionB中設定

// setExitTransition()  
// android:windowExitTransition  A start B時,使A中的View退出場景的transitionA中設定

// setReturnTransition() 
// android:windowReturnTransition B 返回 A時,使B中的View退出場景的transitionB中設定

// setReenterTransition() 
// android:windowReenterTransitionB 返回 A時,使A中的View進入場景的transitionA中設定

知道這個,我們改正之前錯誤的寫法:

<

style name="SecondActivityTheme" parent="AppTheme">
    <!-- 進場(Activity進入時)動畫 -->
    <item name="android:windowEnterTransition">@transition/transition_explode</item>
    <!-- 出場(Activity退出時)動畫,這是正確的 -->
    <item name="android:windowReturnTransition">@transition/transition_fade</item>
</style>

程式碼設定也一樣哦!!

更多的轉場動畫組合得靠自己慢慢摸索,多點實踐就行!!

參考資料
Android關於setExitTransition() 沒有效果的問題
Android 5.0學習之Activity過渡動畫