Android shared element transition

Shared Element

什麼是共享元素?從單一意義上講,共享元素是在兩個不同 Activity 或 Fragment 中存在的一對 View。這些 View 顯示相同的信息,例如文字或圖像,但它們在螢幕上的大小或位置可能不同。為了在切換畫面期間有保留視圖的錯覺,可以使用過渡來移動和重塑第一個 View,使其“成為”第二個 View。由於共享元素實際上包含兩個不同的視圖,實際上來講,並不是真正的共享它們。


現在,可以使用共享元素轉換來實現;但是這裡有個限制。此過渡效果僅在運行在Lollipop(Android 5.0 – API級別21)及更高版本上的設備上可用。

Android 5.0中引入了共享元素過渡,讓跨螢幕的 View 過渡更加無縫且易於實現。使用此過渡,“Activity”或“Fragment”之間的切換似乎更加自然且不受約束。

現在,讓我們來看看如何實現共享元素過渡

Step 1:Enable Window Content Transitions in styles.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- enable window content transitions -->
    <item name="android:windowContentTransitions">true</item>
</style>

Step 2: Set a Common Transition Name for Respective Views on Both Screens

在兩個佈局中的共享元素設定一個共同的 transitionName,View不必具有相同的 id,只需要transitionName 相同

在activity_home.xml中:
<LinearLayout ...>

    <ImageView
        android:id="@+id/iv_list_object_image"
        android:layout_width="75dp"
        android:layout_height="75dp"
        android:layout_margin="5dp"
        android:transitionName="object_image" />

        ...
</LinearLayout>

In the activity_object_detail.xml:
<RelativeLayout ...>

    <ImageView
        android:id="@+id/iv_detail_object_image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_margin="10dp"
        android:transitionName="object_image" />

        ...
</RelativeLayout>

Step 3: Open Activity with Element Transition

Case I:您只希望實現一個共享元素轉換:
Intent intent = new Intent(HomeActivity.this, ObjectDetailActivity.class);
intent.putExtra(ObjectDetailActivity.EXTRA_OBJECT, object);
ActivityOptionsCompat options = ActivityOptionsCompat.
		makeSceneTransitionAnimation(this, mObjectIV, "object_image");
startActivity(intent, options.toBundle());

Case II:您要實現多個共享元素轉換:
Intent intent = new Intent(HomeActivity.this, ObjectDetailActivity.class);
intent.putExtra(ObjectDetailActivity.EXTRA_OBJECT, object);
Pair<View, String> p1 = Pair.create((View)mObjectIV, "object_image");
Pair<View, String> p2 = Pair.create((View)mObjectNameTV, "object_name");
ActivityOptionsCompat options = ActivityOptionsCompat.
		makeSceneTransitionAnimation(this, p1, p2);
startActivity(intent, options.toBundle());
指定多個共享元素過渡時,請確保導入android.support.v4.util.Pair。請確保您不要過度過渡,因為這會分散用戶注意力並降低用戶體驗。

Step 4: Close Activity with Reverse Element Transition

為了在完成第二個 Activity 時獲得反​​向元素過渡效果,您需要調用Activity.supportFinishAfterTransition()方法而不是Activity.finish()方法。另外,您需要確保在活動的所有位置都覆蓋“活動完成”行為,例如,如果工具欄中有後退按鈕,或者用戶按下設備的後退按鈕。
@Override
public void onBackPressed() {
    //To support reverse transitions when user clicks the device back button
    supportFinishAfterTransition();
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        //To support reverse transition when user clicks the action bar's Up/Home button
        case android.R.id.home:
            supportFinishAfterTransition();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

Shared Elements Transitions with Fragments

我們也可以使用 Fragment 實現共享元素轉換

Step 1:Set a Common Transition Name for Respective Views on Both Screens

In the fragment_home.xml:
<LinearLayout ...>

    <ImageView
        android:id="@+id/iv_list_object_image"
        android:layout_width="75dp"
        android:layout_height="75dp"
        android:layout_margin="5dp"
        android:transitionName="object_image" />

        ...
</LinearLayout>

In the fragment_object_detail.xml:
<RelativeLayout ...>

    <ImageView
        android:id="@+id/iv_detail_object_image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_margin="10dp"
        android:transitionName="object_image" />

        ...
</RelativeLayout>

Step 2:Define a Custom Transition

change_bounds.xml in res/transition:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeBounds />
</transitionSet>

change_image_transform.xml in res/transition:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeImageTransform />
</transitionSet>

Step 3:Specify the Shared Elements Transition in FragmentTransaction

Transition changeImageTransform = TransitionInflater.from(this).
        inflateTransition(R.transition.change_image_transform);
Transition changeBoundsTransform = TransitionInflater.from(this).
        inflateTransition(R.transition.change_bounds);

// First Fragment
HomeFragment homeFragment = new HomeFragment();
//Second Fragment
ObjectDetailFragment objectDetailFragment = new ObjectDetailFragment();

// Setup exit transition on HomeFragment
homeFragment.setSharedElementReturnTransition(changeImageTransform);
homeFragment.setExitTransition(changeBoundsTransform);

// Setup enter transition on ObjectDetailFragment
objectDetailFragment.setSharedElementEnterTransition(changeImageTransform);
objectDetailFragment.setEnterTransition(changeBoundsTransform);

// Find the shared element (in MainFragment)
ImageView objectIV = (ImageView) findViewById(R.id.iv_list_object_image);

// Add ObjectDetailFragment by replacing the HomeFragment
FragmentTransaction ft = getSupportFragmentManager().beginTransaction()
        .replace(R.id.container, objectDetailFragment)
        .addToBackStack("transaction_tag")
        .addSharedElement(objectIV, "object_image");

// Apply the transaction
ft.commit();

Custom Shared Elements Transitions:

在Android Lollipop(Android 5.0)中,默認共享元素動畫是4個動畫的組合:
  1. ChangeBounds - View的位置創建移動和縮放的動畫
  2. ChangeTransform - View的比例和旋轉創建縮放和旋轉動畫
  3. ChangeClipBounds ChangeBounds針對的是視圖而ChangeClipBounds針對的是View的裁剪區域(setClipBound(Rect rect)中的矩形)。如果沒有設置則沒有動畫效果
  4. ChangeImageTransform ImageView(這裡是專指ImageView)的尺寸,位置以及ScaleType,並創建相應的動畫。

在大多數情況下,默認動畫就足夠了。但是,在某些情況下,您可能需要自定義默認行為並定義自己的自定義動畫。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- enable window content transitions -->
    <item name="android:windowContentTransitions">true</item>

    <!-- specify window enter and exit transitions -->
    <item name="android:windowEnterTransition">@transition/custom_window_transition</item>
    <item name="android:windowExitTransition">@transition/custom_window_transition</item>

    <!-- specify shared element enter and exit transitions -->
    <item name="android:windowSharedElementEnterTransition">@transition/custom_shared_element_transition</item>
    <item name="android:windowSharedElementExitTransition">@transition/custom_shared_element_transition</item>
</style>

custom_window_transition.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeBounds />
</transitionSet>

custom_shared_element_transition.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeImageTransform />
</transitionSet>

Exclude Elements from Window Content Transitions

您可以通過從動畫中排除這些元素來實現。這可以通過添加<target>標記並指定要排除的元素的ID來完成。

custom_transition.xml:
<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:slideEdge="left"
    android:duration="1500">

    <targets>
        <!-- Specify the status bar ID if it needs to be excluded -->
        <target android:excludeId="@android:id/statusBarBackground"/>
        <!-- Specify the navigation bar ID if it needs to be excluded -->
        <target android:excludeId="@android:id/navigationBarBackground"/>
        <!-- Specify the AppBarLayout ID if custom toolbar is used that needs to be excluded -->
        <target android:excludeId="@id/app_bar_layout" />
    </targets>

</slide>

Shared Element Transitions - Fixing Flashing/Blinking

onCreate in MainActivity.kt:
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setTitle("Activity 1")

        val fade = Fade()

        fade.excludeTarget(R.id.action_bar_container, true)
        fade.excludeTarget(android.R.id.statusBarBackground, true)
        fade.excludeTarget(android.R.id.navigationBarBackground, true)

        window.enterTransition = fade
        window.exitTransition = fade

    }

運用淡入淡出的動畫效果,解決切換畫面閃爍問題

留言

這個網誌中的熱門文章

Android - 使用 adb 安装apk

Android TextView autosizing 自動調整大小

Kotlin - 實現Android中的Parcelable