在 Views 中使用 Compose

您可以在採用基於 View 設計的現有應用程式中,新增以 Compose 為基礎的 UI。

如要建立以 Compose 為基礎的新畫面,請讓活動呼叫 setContent() 方法,然後視需要傳遞任何可組合項。

class ExampleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

這段程式碼看起來就像 Compose-only App中會出現的寫法。

ComposeViewViewCompositionStrategy

當檢視畫面從視窗卸離時,Compose 預設會處置 Composition。Compose UI View 類型 (例如 ComposeViewAbstractComposeView) 是使用ViewCompositionStrategy來定義此行為。

Compose 在預設情況下是使用 DisposeOnDetachedFromWindowOrReleasedFromPool 策略。但在某些使用 Compose UI View 類型的情況下,這個預設值是不適合的

  • Fragment。Composition 必須遵循 Fragment 的檢視畫面生命週期,Compose UI View 類型才能儲存狀態。

  • 轉換。只要在轉換中使用 Compose UI View,View 就會在轉換開始 (而非轉換結束) 時從視窗卸離,導致可組合項仍顯示在畫面上時就處置自身狀態。

  • 您自己的生命週期管理自訂 View

在上述某些情況下,除非您手動呼叫 AbstractComposeView.disposeComposition,否則應用程式將從 Composition 執行個體緩慢地流失記憶體。

請設定其他策略、或透過呼叫 setViewCompositionStrategy 方法,建立自己的策略,自動地處置不再需要的 Composition。例如,lifecycle 遭刪除時,DisposeOnLifecycleDestroyed 策略會處置Composition。此策略適用於與已知 LifecycleOwner 同樣具有 1 對 1 關係的 Compose UI View 類型。如果 LifecycleOwner 為未知,您可以使用 DisposeOnViewTreeLifecycleDestroyed

如要查看這個 API 的實際應用情形,請參閱 Fragment 中的 ComposeView

Fragment 中的 ComposeView

如要在片段或現有的 View 版面配置中加入 Compose UI 內容,請使用 ComposeView 並呼叫其 setContent() 方法。ComposeView 是 Android View

您可將 ComposeView 和其他的 View 一樣地放在 XML 版面配置中:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/hello_world"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello Android!" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

在 Kotlin 原始碼中,從 XML 定義的版面配置資源加載版面配置。然後使用 XML ID 取得 ComposeView,並設定最適合代管 View 的 Composition 策略,並呼叫 setContent() 以使用 Compose。

class ExampleFragment : Fragment() {

    private var _binding: FragmentExampleBinding? = null

    // This property is only valid between onCreateView and onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        val view = binding.root
        binding.composeView.apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

兩個細微不同的文字元素,一個在另一個的上面

圖 1. 這個程式碼會顯示在 View UI 階層中新增 Compose 元素的程式碼輸出內容。「Hello Android!」文字會以 TextView 小工具顯示。「您好 Compose!」文字會顯示Compose 文字元素。

假如您使用 Compose 建構全螢幕畫面,可以直接在片段中加入 ComposeView,避免單獨使用 XML 版面配置檔案。

class ExampleFragmentNoXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                MaterialTheme {
                    // In Compose world
                    Text("Hello Compose!")
                }
            }
        }
    }
}

在同一個版面配置中有多個 ComposeView 執行個體

如果在同一個版面配置中有多個 ComposeView 元素,則每個元素都必須有專屬 ID,savedInstanceState 才能正常運作。

class ExampleFragmentMultipleComposeView : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = LinearLayout(requireContext()).apply {
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_x
                // ...
            }
        )
        addView(TextView(requireContext()))
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_y
                // ...
            }
        )
    }
}

res/values/ids.xml 檔案已定義 ComposeView ID:

<resources>
    <item name="compose_view_x" type="id" />
    <item name="compose_view_y" type="id" />
</resources>

後續步驟

現在您已瞭解互通性 API 的相關資訊,能夠在 View 中使用 Compose,接著請瞭解如何在 Compose 中使用 View