Android shared element transition
Shared Element
什麼是共享元素?從單一意義上講,共享元素是在兩個不同 Activity 或 Fragment 中存在的一對 View。這些 View 顯示相同的信息,例如文字或圖像,但它們在螢幕上的大小或位置可能不同。為了在切換畫面期間有保留視圖的錯覺,可以使用過渡來移動和重塑第一個 View,使其“成為”第二個 View。由於共享元素實際上包含兩個不同的視圖,實際上來講,並不是真正的共享它們。
現在,可以使用共享元素轉換來實現;但是這裡有個限制。此過渡效果僅在運行在Lollipop(Android 5.0 – API級別21)及更高版本上的設備上可用。
Android 5.0中引入了共享元素過渡,讓跨螢幕的 View 過渡更加無縫且易於實現。使用此過渡,“Activity”或“Fragment”之間的切換似乎更加自然且不受約束。
現在,可以使用共享元素轉換來實現;但是這裡有個限制。此過渡效果僅在運行在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 相同
<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());
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個動畫的組合:- ChangeBounds - View的位置創建移動和縮放的動畫
- ChangeTransform - View的比例和旋轉創建縮放和旋轉動畫
- ChangeClipBounds - ChangeBounds針對的是視圖而ChangeClipBounds針對的是View的裁剪區域(
setClipBound(Rect rect)
中的矩形)。如果沒有設置則沒有動畫效果 - 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
}
運用淡入淡出的動畫效果,解決切換畫面閃爍問題
留言
張貼留言