Sử dụng Compose trong Khung hiển thị

Bạn có thể thêm giao diện người dùng dựa trên Compose vào một ứng dụng hiện có sử dụng thiết kế dựa trên Khung hiển thị.

Để tạo một màn hình mới hoàn toàn dựa trên Compose, hãy gọi phương thức setContent() và truyền bất kỳ hàm có khả năng kết hợp nào mà bạn muốn.

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!")
}

Đoạn mã trên được viết giống hệt những gì bạn sẽ thấy trong bất kỳ ứng dụng thuần Compose nào

ViewCompositionStrategy cho ComposeView

Theo mặc định, Compose sẽ huỷ bỏ Composition (Thành phần kết hợp) bất cứ khi nào khung hiển thị bị tách khỏi một cửa sổ. Các loại View giao diện người dùng của Compose như lớp ComposeView và lớp AbstractComposeView sử dụng lớp giao tiếp ViewCompositionStrategy để thực hiện tác vụ này.

Theo mặc định, Compose sẽ sử dụng lớp giao tiếp DisposeOnDetachedFromWindowOrReleasedFromPool. Tuy nhiên, giá trị mặc định này có thể gây phiền phức trong một số trường hợp khi sử dụng các loại View Giao diện người dùng của Compose trong:

  • Mảnh. Cấu trúc sử dụng phải tuân thủ theo vòng đời của thành phần hiển thị mảnh dành cho View Giao diện người dùng của Compose để lưu lại trạng thái.

  • Chuyển đổi. Bất cứ khi nào View trên giao diện người dùng Compose được sử dụng trong quá trình chuyển đổi, nó sẽ tách ra khỏi cửa sổ ngay khi quá trình chuyển đổi bắt đầu (chứ không phải khi quá trình chuyển đổi kết thúc). Việc này khiến thành phần kết hợp huỷ bỏ trạng thái của nó trong khi vẫn hiện trên màn hình.

  • View tuỳ chỉnh của riêng bạn do vòng đời quản lý.

Trong một số trường hợp như vậy, ứng dụng cũng có thể dần bị rò rỉ bộ nhớ từ các thực thể Cấu trúc (Composition), trừ phi bạn gọi phương thức AbstractComposeView.disposeComposition theo cách thủ công.

Để tự động huỷ bỏ Cấu trúc không cần dùng đến, hãy đặt một chiến lược khác hoặc tự tạo chiến lược riêng bằng cách gọi phương thức setViewCompositionStrategy. Ví dụ: chiến lược DisposeOnLifecycleDestroyed sẽ huỷ bỏ Các thành phần khi lifecycle bị huỷ. Chiến lược này phù hợp với các loại View trên giao diện người dùng Compose có chung mối liên kết 1:1 với LifecycleOwner đã biết. Khi LifecycleOwner là không xác định, bạn có thể sử dụng DisposeOnViewTreeLifecycleDestroyed.

Xem API này trong thực tế tại ComposeView trong Fragment (Mảnh).

ComposeView trong Fragment (Mảnh)

Nếu bạn muốn kết hợp nội dung trên giao diện người dùng Compose trong một mảnh hoặc bố cục Khung hiển thị hiện có, hãy sử dụng lớp ComposeView và gọi phương thức setContent(). Lớp ComposeView là một lớp cơ sở View của Android.

Bạn có thể đặt lớp ComposeView vào bố cục XML giống như đối với bất kỳ lớp cơ sở View nào khác:

<?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>

Trong mã nguồn Kotlin, hãy tăng cường sử dụng bố cục trong tài nguyên bố cục được định nghĩa trong XML. Tiếp theo, tạo lớp ComposeView bằng mã XML, đặt chiến lược Bố cục phù hợp nhất với máy chủ View và gọi phương thức setContent() để sử dụng tính năng từ 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
    }
}

Hai thành phần văn bản văn có chút khác biệt, một thành phần ở trên thành phần khác

Hình 1. Hình trên thể hiện kết quả chạy của đoạn mã trên sau khi thêm các phần tử Compose vào một hệ phân cấp Giao diện người dùng. Dòng chữ "Hello Android!" ("Xin chào Android!") hiển thị nhờ tiện ích TextView. Dòng chữ "Hello Compose!" ("Xin chào Compose!") hiển thị nhờ phần tử Compose.

Bạn cũng có thể trực tiếp đưa lớp ComposeView vào một mảnh nếu chế độ toàn màn hình của bạn được tạo bằng Compose. Điều này giúp bạn tránh hoàn toàn việc sử dụng tệp bố cục 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!")
                }
            }
        }
    }
}

Nhiều thực thể ComposeView trong cùng một bố cục

Nếu có nhiều phần tử ComposeView trong cùng một bố cục, thì mỗi phần tử phải có một mã nhận dạng duy nhất để savedInstanceState hoạt động.

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

Mã nhận dạng ComposeView được xác định trong tệp res/values/ids.xml:

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

Các bước tiếp theo

Giờ đây, bạn đã biết các API có khả năng tương tác để sử dụng Compose trong Khung hiển thị, hãy tìm hiểu cách sử dụng Khung hiển thị trong Compose.