繼前一章節 Day28 使用M3的Motion - Transition Patterns四種模式,其中的Container transform patterns (1)
此模式用於具有空間或導航關係的UI元素之間的過渡,該模式使用x、y或z軸上的共享變換來加強元素之間的關係。
MaterialSharedAxis使用向前或向後移動的概念,以下是MaterialSharedAxis向前和向後移動的軸線。
| Axis | Forward | Backward | 動畫效果 | 
|---|---|---|---|
| X | Left on x-axis | Right on x-axis | 水平滑動和淡入淡出 | 
| Y | Up on y-axis | Down on y-axis | 垂直滑動和淡入淡出 | 
| Z | Forward on z-axis | Backward on z-axis | 使用縮放和淡入淡出 | 
x-axis 查看效果
y-axis 查看效果
z-axis 查看效果

Activity 和 Window過渡需要使用com.google.android.material.transition.platform提供的Android框架Transition,並且只能在API級別21及更高版本上可用。
準備兩個Class,和兩個 layout
設定Enable Activity Transitions,可以在主題xml、Activity的onCreate設定
<style  name="MyTheme" parent="Theme.Material3.DayNight.NoActionBar">
  ...
  <item  name="android:windowActivityTransitions">true</item>
</style>
override fun onCreate(savedInstanceState: Bundle?) {
  window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
  ...
}
Activity A 設定 MaterialSharedAxis,退出的動畫效果
Activity A’s Class 的onCreate設定:
設定Activity A關閉的設定axis、動畫開始效果 MaterialSharedAxis(MaterialSharedAxis.X, true)
查看源碼:
/**
	* Params:axis設定X-axis、Y-axis、Z-axis
	* Params:Boolean forward 設定動畫方向,**True**從左邊水平滑動,**False**從右邊水平滑動
	**/
public MaterialSharedAxis(@Axis int axis, boolean forward) {
    super(createPrimaryAnimatorProvider(axis, forward), createSecondaryAnimatorProvider());
    this.axis = axis;
    this.forward = forward;
 }
指定動畫效果的layout id addTarget(android.R.id.content)
class SharedAxisActivityA: AppCompatActivity() {
    private lateinit var binding: ActivitySharedAxisABinding
    override fun onCreate(savedInstanceState: Bundle?) {
        // 設定退出的動畫效果, MaterialSharedAxis,設定axis、動畫開始方向
        window.exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true).apply {
            // 指定動畫效果的layout id
            addTarget(R.id.btn_axis_x)
        }
        super.onCreate(savedInstanceState)
        binding = ActivitySharedAxisABinding.inflate(layoutInflater)
        setContentView(binding.root)
				// X 軸頁面切換動畫效果 
        binding.btnAxisX.setOnClickListener {
            val bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
            startActivity(Intent(this, SharedAxisActivityB::class.java), bundle)
        }
    }
}
Activity A’s layout:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <Button
        android:id="@+id/btn_axis_x"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="40dp"
        android:text="X-Axis動畫效果"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>
    <Button
        android:id="@+id/btn_axis_y"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="40dp"
        android:text="Y-Axis動畫效果"
        app:layout_constraintTop_toBottomOf="@id/btn_axis_x"
        app:layout_constraintStart_toStartOf="parent"/>
    <Button
        android:id="@+id/btn_axis_z"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="40dp"
        android:text="Z-Axis動畫效果"
        app:layout_constraintTop_toBottomOf="@id/btn_axis_y"
        app:layout_constraintStart_toStartOf="parent"/>
Activity B 設定 MaterialSharedAxis 進入動畫效果
ActivityB’s Class 的onCreate設定:
MaterialSharedAxis(MaterialSharedAxis.X, true)
addTarget(android.R.id.content)
class SharedAxisActivityB: AppCompatActivity() {
    private lateinit var binding: ActivitySharedAxisBBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        // 設定進入動畫效果,MaterialSharedAxis,設定axis、動畫開始方向
        window.enterTransition  = MaterialSharedAxis(MaterialSharedAxis.X, true).apply {
            // 指定動畫效果的layout id
            addTarget(R.id.b_container)
        }
        // ActivityA 和 ActivityB 的動畫效果重疊,預設為True
        // 當為 true 時,ActivityA轉換完成後再開始ActivityB
        // 當為 false 時,轉換將不等到ActivityA退出轉換完成後再開始ActivityB
        window.allowEnterTransitionOverlap = true
        super.onCreate(savedInstanceState)
        binding = ActivitySharedAxisBBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}
Activity B’s layout:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/b_container"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Activity B"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Activity A 開啟 Activity B頁面
ActivityOptions傳遞Bundle,開啟Activity B頁面val bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
startActivity(Intent(this, ActivityB::class.java), bundle)
接下來範例在Fragment A 和 Fragment B 之間使用Z軸的動畫效果
準備兩個Class,和兩個 layout
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
		// FragmentA關閉準備開啟FragmentB頁面時動畫
	  exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true)
	  // FragmentB頁面返回FragmentA時動畫
    reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false)
}
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
   enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true)
   returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false)
}
supportFragmentManager
  .beginTransaction()
  .replace(R.id.fragment_container, FragmentB())
  .commit()

MaterialSharedAxis 是MaterialVisibility擴充套件,MaterialVisibility 是一個Visibility過渡,由較小atomic的VisibilityAnimatorProviders組成,能夠根據目標是否出現還是消失來構建動畫。
預設情況下,MaterialVisibility實現具有主要和次要VisibilityAnimatorProvider,主要提供程式可以修改,而次要提供程式可以修改、替換或刪除。 這允許自定義動畫,同時仍然堅持圖案的基礎,被稱為variant
Shared axis 組成項目
Examples: 設定ActivityA使用 MaterialSharedAxis Z軸,預設會有輔助動畫效果,讓換頁時動畫會在ActivityA中和ActivityA上淡化效果,同時使ActivityA的alpha保持不變。
可查看源碼:createSecondaryAnimatorProvider()方法開啟創建輔助動畫效果
  public MaterialSharedAxis(@Axis int axis, boolean forward) {
    super(createPrimaryAnimatorProvider(axis, forward), createSecondaryAnimatorProvider());
    this.axis = axis;
    this.forward = forward;
  }
// 提供淡出或在視圖中的 Animator 
private static VisibilityAnimatorProvider createSecondaryAnimatorProvider() {
    return new FadeThroughProvider();
  }
所以在客製化時,想移除預設會有輔助動畫效果,可以設定 secondaryAnimatorProvider = null
ActivityA‘s class:
override fun onCreate(savedInstanceState: Bundle?) {
  val exit = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
    secondaryAnimatorProvider = null
    addTarget(R.id.main_container)
  }
  window.exitTransition = exit
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)
  ...
}
感謝耐心看到這邊~~![]()
今日只講了Motion - Shared axis,明日繼續其他。