การใช้ Compose ใน View

คุณสามารถเพิ่ม UI ที่อิงตาม Compose ลงในแอปที่มีอยู่ซึ่งใช้การออกแบบที่อิงตาม View ได้

หากต้องการสร้างหน้าจอใหม่ที่ใช้ Compose ทั้งหมด ให้กิจกรรมเรียกใช้เมธอด setContent() และส่งฟังก์ชันที่ใช้ Composable ที่ต้องการ

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 เท่านั้น

ViewCompositionStrategy เป็นเวลา ComposeView

ViewCompositionStrategy กำหนดเวลาที่ควรทิ้ง Composition ค่าเริ่มต้น ViewCompositionStrategy.Default จะทิ้ง Composition เมื่อ ComposeView พื้นฐานแยกออกจากหน้าต่าง เว้นแต่จะเป็นส่วนหนึ่งของคอนเทนเนอร์การจัดกลุ่ม เช่น RecyclerView ในแอปที่ใช้ Compose อย่างเดียวซึ่งมี Activity เดียว ลักษณะการทำงานเริ่มต้นนี้คือ สิ่งที่คุณต้องการ แต่หากคุณค่อยๆ เพิ่ม Compose ใน โค้ดเบส ลักษณะการทำงานนี้อาจทำให้สถานะสูญหายในบางสถานการณ์

หากต้องการเปลี่ยน ViewCompositionStrategy ให้เรียกใช้เมธอด setViewCompositionStrategy() แล้วระบุกลยุทธ์อื่น

ตารางด้านล่างสรุปสถานการณ์ต่างๆ ที่คุณใช้ ViewCompositionStrategy ได้

ViewCompositionStrategy คำอธิบายและสถานการณ์การทำงานร่วมกัน
DisposeOnDetachedFromWindow ระบบจะทิ้ง Composition เมื่อ ComposeView พื้นฐานแยกออกจากหน้าต่าง DisposeOnDetachedFromWindowOrReleasedFromPool

สถานการณ์การทำงานร่วมกัน:

* ComposeView ไม่ว่าจะเป็นองค์ประกอบเดียวในลำดับชั้นของ View หรือในบริบทของหน้าจอ View/Compose แบบผสม (ไม่ใช่ใน Fragment)
DisposeOnDetachedFromWindowOrReleasedFromPool (ค่าเริ่มต้น) เช่นเดียวกับ DisposeOnDetachedFromWindow เมื่อองค์ประกอบไม่ได้อยู่ในคอนเทนเนอร์การจัดกลุ่ม เช่น RecyclerView หากอยู่ในคอนเทนเนอร์การจัดกลุ่ม ระบบจะทิ้งเมื่อคอนเทนเนอร์การจัดกลุ่มเองหลุดออกจากหน้าต่าง หรือเมื่อมีการทิ้งรายการ (เช่น เมื่อพูลเต็ม)

สถานการณ์การทำงานร่วมกัน:

* ComposeView ไม่ว่าจะเป็นองค์ประกอบเดียวในลำดับชั้นของ View หรือในบริบทของหน้าจอ View/Compose แบบผสม (ไม่ได้อยู่ใน Fragment)
* ComposeView เป็นรายการในคอนเทนเนอร์การจัดกลุ่ม เช่น RecyclerView
DisposeOnLifecycleDestroyed ระบบจะทิ้ง Composition เมื่อทำลาย Lifecycle ที่ระบุ

สถานการณ์การทำงานร่วมกัน

* ComposeView ในมุมมองของ Fragment
DisposeOnViewTreeLifecycleDestroyed ระบบจะทิ้ง Composition เมื่อ Lifecycle ที่ LifecycleOwner เป็นเจ้าของซึ่ง ViewTreeLifecycleOwner.get ของหน้าต่างถัดไปที่ View เชื่อมต่ออยู่ถูกทำลาย

สถานการณ์การทำงานร่วมกัน:

* ComposeView ใน View ของ Fragment
* ComposeView ในมุมมองที่ยังไม่ทราบวงจร

ComposeView ในส่วนย่อย

หากต้องการรวมเนื้อหา UI ของ Compose ใน Fragment หรือเลย์เอาต์ View ที่มีอยู่ ให้ใช้ ComposeView และเรียกใช้เมธอด setContent() ComposeView เป็น View ของ Android

คุณใส่ ComposeView ในเลย์เอาต์ XML ได้เช่นเดียวกับ View อื่นๆ

<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/text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />

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

ในซอร์สโค้ด Kotlin ให้ขยายเลย์เอาต์จากเลย์เอาต์ ทรัพยากรที่กำหนดไว้ใน XML จากนั้นรับ ComposeView โดยใช้รหัส XML ตั้งค่ากลยุทธ์การจัดองค์ประกอบที่เหมาะกับ โฮสต์ View มากที่สุด และเรียกใช้ setContent() เพื่อใช้ Compose

class ExampleFragmentXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.fragment_example, container, false)
        val composeView = view.findViewById<ComposeView>(R.id.compose_view)
        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
    }
}

หรือคุณจะใช้ View Binding เพื่อรับการอ้างอิงไปยัง ComposeView โดยอ้างอิงคลาส Binding ที่สร้างขึ้นสำหรับไฟล์เลย์เอาต์ XML ก็ได้

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

องค์ประกอบข้อความ 2 รายการที่แตกต่างกันเล็กน้อย โดยวางซ้อนกัน

รูปที่ 1 ซึ่งแสดงเอาต์พุตของโค้ดที่เพิ่มองค์ประกอบ Compose ในลำดับชั้น UI ของ View ข้อความ "สวัสดี Android!" แสดงโดยวิดเจ็ต TextView ข้อความ "สวัสดี Compose!" แสดงโดย องค์ประกอบข้อความ Compose

นอกจากนี้ คุณยังใส่ ComposeView ใน Fragment ได้โดยตรงหากสร้างแบบเต็มหน้าจอด้วย Compose ซึ่งจะช่วยให้คุณไม่ต้องใช้ไฟล์เลย์เอาต์ 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 หลายรายการในเลย์เอาต์เดียวกัน องค์ประกอบแต่ละรายการต้องมีรหัสที่ไม่ซ้ำกันเพื่อให้ 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
                // ...
            }
        )
    }
}

ระบบกำหนดรหัส ComposeView ไว้ในไฟล์ res/values/ids.xml ดังนี้

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

แสดงตัวอย่าง Composable ในเครื่องมือสร้างเลย์เอาต์

นอกจากนี้ คุณยังดูตัวอย่าง Composable ภายในเครื่องมือแก้ไขเลย์เอาต์สำหรับเลย์เอาต์ XML ที่มี ComposeView ได้ด้วย การทำเช่นนี้จะช่วยให้คุณเห็นลักษณะของ Composable ภายในเลย์เอาต์ของ View และ Compose แบบผสม

สมมติว่าคุณต้องการแสดง Composable ต่อไปนี้ใน Layout Editor หมายเหตุ ว่า Composable ที่มีคำอธิบายประกอบด้วย @Preview เหมาะที่จะแสดงตัวอย่างใน เครื่องมือแก้ไขเลย์เอาต์

@Preview
@Composable
fun GreetingPreview() {
    Greeting(name = "Android")
}

หากต้องการแสดง Composable นี้ ให้ใช้tools:composableNameแอตทริบิวต์เครื่องมือและ ตั้งค่าเป็นชื่อที่สมบูรณ์ของ Composable เพื่อดูตัวอย่างใน เลย์เอาต์

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/my_compose_view"
      tools:composableName="com.example.compose.snippets.interop.InteroperabilityAPIsSnippetsKt.GreetingPreview"
      android:layout_height="match_parent"
      android:layout_width="match_parent"/>

</LinearLayout>

Composable ที่แสดงภายในเครื่องมือแก้ไขเลย์เอาต์

ขั้นตอนถัดไป

ตอนนี้คุณทราบ API ความสามารถในการทำงานร่วมกันเพื่อใช้ Compose ใน View แล้ว มาดูวิธีใช้ View ใน Compose กัน