iT邦幫忙

2022 iThome 鐵人賽

DAY 29
1

繼前一章節 Day28 使用M3的Motion - Transition Patterns四種模式,其中的Container transform patterns (1)

Shared axis

此模式用於具有空間或導航關係的UI元素之間的過渡,該模式使用x、y或z軸上的共享變換來加強元素之間的關係。
MaterialSharedAxis使用向前或向後移動的概念,以下是MaterialSharedAxis向前和向後移動的軸線。

Shared axis 方向

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 查看效果

實作上探討

Transition between Activities examples

Activity 和 Window過渡需要使用com.google.android.material.transition.platform提供的Android框架Transition,並且只能在API級別21及更高版本上可用。

準備兩個Class,和兩個 layout

  1. 設定Enable Activity Transitions,可以在主題xml、Activity的onCreate設定

    • 主題xml,res/values/themes.xml
    <style  name="MyTheme" parent="Theme.Material3.DayNight.NoActionBar">
      ...
      <item  name="android:windowActivityTransitions">true</item>
    </style>
    
    
    • Activity的onCreate
    override fun onCreate(savedInstanceState: Bundle?) {
      window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
      ...
    }
    
  2. 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"/>
    
  3. Activity B 設定 MaterialSharedAxis 進入動畫效果

    ActivityB’s Class 的onCreate設定:

    • 跟剛剛一樣,設定啟動Activity B動畫效果 MaterialSharedAxis(MaterialSharedAxis.X, true)
    • 指定動畫效果的layout id 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>
    
  4. Activity A 開啟 Activity B頁面

    1. 透過ActivityOptions傳遞Bundle,開啟Activity B頁面
    val bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
    startActivity(Intent(this, ActivityB::class.java), bundle)
    

Transition between Fragments examples

接下來範例在Fragment A 和 Fragment B 之間使用Z軸的動畫效果

準備兩個Class,和兩個 layout

  1. Fragment A 設定開啟和關閉的過渡動畫
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

		// FragmentA關閉準備開啟FragmentB頁面時動畫
	  exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true)
	  // FragmentB頁面返回FragmentA時動畫
    reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false)
}
  1. Fragment B 設定開啟和關閉的過渡動畫
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

   enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true)
   returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false)
}
  1. Fragment A 開啟到 FragmentB
supportFragmentManager
  .beginTransaction()
  .replace(R.id.fragment_container, FragmentB())
  .commit()

Customization

Shared axis attributes

https://ithelp.ithome.com.tw/upload/images/20221013/20144469wPb35qLrrf.png

Shared axis fade variant

MaterialSharedAxisMaterialVisibility擴充套件,
MaterialVisibility 是一個Visibility過渡,由較小atomicVisibilityAnimatorProviders組成,能夠根據目標是否出現還是消失來構建動畫。

預設情況下,MaterialVisibility實現具有主要和次要VisibilityAnimatorProvider,主要提供程式可以修改,而次要提供程式可以修改、替換或刪除。 這允許自定義動畫,同時仍然堅持圖案的基礎,被稱為variant

Shared axis 組成項目
https://ithelp.ithome.com.tw/upload/images/20221013/20144469c6RbmUAeLZ.png

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)
  ...
}

感謝耐心看到這邊~~/images/emoticon/emoticon06.gif
今日只講了Motion - Shared axis,明日繼續其他。

參考資料:Material Design Motion


上一篇
Day28 使用M3的Motion - Transition Patterns四種模式,其中的Container transform patterns (1)
下一篇
Day30 Motion - Fade through 、Fade(3)
系列文
Kotlin 實踐 Material Design 懶人包30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言