Android轉場動畫深入分析探究

早期的轉場

最初,兩個Activity之間的切換的過度動畫,都是用overridePendingTransition。它隻支持平移、縮放、透明度、旋轉四種動畫效果。

比如我們寫個平移跳轉動畫,實現是這樣的。首先,我們在資源文件anim下新建兩個動畫資源文件 enter_anim.xml 和 quit_anim.xml,分別表示進入和退出的動畫。

enter_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">
    <translate
        android:fromXDelta="100%p"
        android:toXDelta="0" />
</set>

quit_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">
    <translate
        android:fromXDelta="0"
        android:toXDelta="-100%p" />
</set>

然後在界面跳轉的時候,調用overridePendingTransition就行啦

            startActivity(Intent(this, TestActivity::class.java))
            overridePendingTransition(R.anim.enter_anim, R.anim.quit_anim)

Material Design 轉場動畫

Android 5.0之後使用,具有三種轉場動畫效果:

  • Explode:爆炸式,將視圖移入場景中心或從中移出
  • Fade:淡入淡出式,通過更改透明度來添加和移出視圖
  • Slide:滑動式,從場景的一個邊緣移入或移出視圖

Materia轉場有兩種啟用方式,一種是在theme中設置 windowActivityTransitions

    <style name="Theme.AndroidApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <item name="android:windowActivityTransitions">true</item>
    </style>

一種是通過代碼開啟

window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)

下面,通過一個簡單的示例,來瞧瞧具體是怎麼去使用的

在MainActivity中設置退出動畫,然後通過點擊事件去進行跳轉到TestActivity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        window.apply {
            requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
            exitTransition = Fade() //退出動畫
        }
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.go.setOnClickListener {
            materialGo()
        }
    }
    private fun materialGo() {
        val intent = Intent(this, TestActivity::class.java)
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
    }
}

在TestActivity中設置進入動畫,這樣淡入淡出式就完成瞭

class TestActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        window.run {
            requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
            enterTransition = Fade() //進入動畫
        }
        super.onCreate(savedInstanceState)
        val binding = ActivityTestBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

共享元素

如下所示,共享元素的效果很有趣,看著就像是圖片從一個頁面放大到另一個頁面

共享元素過渡方式有四種

  • changeBounds:為目標視圖佈局邊界的變化添加動畫效果
  • changeClipBounds:為目標視圖裁剪邊界的變化添加動畫效果
  • changeTransform:為目標視圖縮放和旋轉方面的變化添加動畫效果
  • changeImageTransform:為目標圖片尺寸和縮放方面的變化添加動畫效果

首先需要設置transitionName,告訴系統,哪個View需要做動畫,然後進行Activity的跳轉

        binding.image.setOnClickListener {
            binding.image.transitionName = "shared_elements"
            val options =
                ActivityOptions.makeSceneTransitionAnimation(this, binding.image, "shared_elements")
            val intent = Intent(this, TestActivity::class.java)
            startActivity(intent, options.toBundle())
        }

在目標Activity中給View設置transitionName,也可添加一系列的過渡效果

class TestActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val transitionSet = TransitionSet().apply {
            addTransition(ChangeBounds())
            addTransition(ChangeClipBounds())
            addTransition(ChangeTransform())
            addTransition(ChangeImageTransform())
        }
        with(window) {
            requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
            sharedElementEnterTransition = transitionSet
            sharedElementExitTransition = transitionSet
        }
        super.onCreate(savedInstanceState)
        val binding = ActivityTestBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.picture.transitionName = "shared_elements"
    }
}

如果要使用多個共享元素,可使用Pair,例如

            val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                this, Pair(binding.imageView, "imageView"),
                Pair(binding.signature, "signature")
            )

Material Motion 動畫

它提供瞭四種模式,可以根據需求靈活選用,分別是:

MaterialContainerTransform

用於包含容器的界面元素之間的過渡,通過將一個UI元素無縫轉換為另一個UI元素,在兩個不同的界面元素之間創造可視化的連接,跟之前共享元素動畫最大的不同點在於它可以是一個 ViewGroup,也可以是一個 View。

下面,我們通過一個簡單的例子來感受一下效果,item是個LinearLayout,用於點擊跳轉。

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        //1.啟用轉場動畫
        window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
        setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback())
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initView()
    }
    private fun initView() {
        //2.設置transitionName
        binding.item.transitionName = "share"
        binding.item.setOnClickListener {
            //3.進行頁面跳轉
            startActivity(
                Intent(this, TestActivity::class.java),
                ActivityOptions.makeSceneTransitionAnimation(this, it, "share").toBundle()
            )
        }
    }
}

在目標Activity中,隻顯示瞭幾行文本,整體用LinearLayout做容器,id為display。

這裡為瞭更加清楚的看到動畫的轉換過程,我將動畫的執行時間duration設置為2秒,實際開發中,不能搞這麼久哦。

class TestActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        //1.啟用轉場動畫
        window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
        setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback())
        super.onCreate(savedInstanceState)
        val binding = ActivityTestBinding.inflate(layoutInflater)
        setContentView(binding.root)
        //2.設置transitionName
        binding.display.transitionName = "share"
        //3.設置具體的動畫
        window.sharedElementEnterTransition = MaterialContainerTransform().apply {
            addTarget(binding.display)
            scrimColor = Color.TRANSPARENT
            setAllContainerColors(Color.WHITE)
            duration = 2000L
        }
        window.sharedElementExitTransition = MaterialContainerTransform().apply {
            addTarget(binding.display)
            scrimColor = Color.TRANSPARENT
            setAllContainerColors(Color.WHITE)
            duration = 2000L
        }
    }
}

MaterialContainerTransform有兩個屬性需要註意下:

  • scrimColor:用於控制在動畫容器後面繪制的半透明陰影的顏色。默認情況下,該元素會設為 32% 黑色。這裡將其設為透明,這意味著不會繪制任何紗罩。
  • setAllContainerColors:在兩個視圖之間添加動畫時,它會在畫佈上繪制 3 個容器: 後臺容器 ,起始視圖的容器和結束視圖的容器。對於這 3 個容器,我們都可以為其填充顏色,並將其默認設為透明。如果您的起始視圖或結束視圖本身沒有繪制背景,導致在動畫播放期間其他元素顯示在其下層,那麼設置這些背景填充顏色可能會很有用。我們可以使用 setAllContainerColors 來統一顏色,確保我們不會遇到任何視覺問題。

Shared axis

用於具有空間或導航關系的界面元素之間的過渡,讓元素在轉換時共用 x 軸、y 軸或 z 軸,用以強調元素間的關系。

在MainActivity中設置退出動畫

        //啟用轉場動畫
        window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
        window.exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true).apply {
            //指定轉換視圖
            addTarget(R.id.list)
            //轉換不包含導航和狀態欄
            excludeTarget(android.R.id.statusBarBackground, true)
            excludeTarget(android.R.id.navigationBarBackground, true)
        }

進行跳轉

            startActivity(
                Intent(this, TestActivity::class.java),
                ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
            )

在TestActivity中設置進入動畫

        window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
        window.enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true).apply {
            addTarget(R.id.display)
            excludeTarget(android.R.id.statusBarBackground, true)
            excludeTarget(android.R.id.navigationBarBackground, true)
        }

然後,我們來看一下轉場效果

Fade Through

用於彼此之間沒有密切關系的界面元素之間的過渡,使用依序淡出和淡入的效果,並會對轉入的元素進行縮放,用法跟MaterialSharedAxis差不多。

        window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
        window.enterTransition = MaterialFadeThrough().apply {
            addTarget(R.id.display)
            excludeTarget(android.R.id.statusBarBackground, true)
            excludeTarget(android.R.id.navigationBarBackground, true)
        }

Fade

用於進入或退出屏幕畫面范圍的界面元素。似乎效果上Fade 和上面的 Fade Through 差不多,其實確實都是透明度+縮放動畫,但是官方建議,如果發生在同一個界面,例如在屏幕中心淡出的對話框。

這裡為瞭更清楚的看清轉場過程,將動畫的執行時間duration設置為1000,實際開發中應設置小點,或者不用去設置

            val materialFade = MaterialFade().apply {
                duration = 1000L
            }
            TransitionManager.beginDelayedTransition(binding.list, materialFade)
            binding.detail.visibility = View.VISIBLE

總結

在Android 轉場動畫的發展中,早期的轉場支持平移、縮放、透明度、旋轉四種基礎動畫效果,隨後,出現瞭Material Design 轉場動畫,給我們帶來瞭共享元素動畫效果,最後Material Motion 動畫封裝瞭四種動畫,使得轉場效果的實現更加容易。我覺得,頁面之間的轉場效果,可以賦予應用活力,豐富用戶的使用體驗,升華應用交互的靈魂,常言道,好看的皮囊千篇一律,有趣的靈魂萬裡挑一。

到此這篇關於Android轉場動畫深入分析探究的文章就介紹到這瞭,更多相關Android轉場動畫內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: