การใช้ Compose ใน View

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

หากต้องการสร้างหน้าจอใหม่ที่ใช้ 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!")
}

โค้ดนี้มีลักษณะเหมือนกับที่คุณเห็นในแอปเขียนอีเมลเท่านั้น

ViewCompositionStrategy เป็นเวลา ComposeView

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

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

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

ViewCompositionStrategy คำอธิบายและสถานการณ์การทํางานร่วมกัน
DisposeOnDetachedFromWindow ระบบจะทิ้งองค์ประกอบเมื่อมีการแยก ComposeView ที่อยู่เบื้องหลังออกจากหน้าต่าง แทนที่ด้วย DisposeOnDetachedFromWindowOrReleasedFromPool.

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

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

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

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

สถานการณ์การผสานรวม

* ComposeView ในมุมมองของ Fregment
DisposeOnViewTreeLifecycleDestroyed ระบบจะทิ้งคอมโพสิชันเมื่อมีการทำลาย Lifecycle ที่เป็นของ LifecycleOwner ที่ ViewTreeLifecycleOwner.get ของหน้าต่างถัดไปที่ View แนบอยู่แสดงผล

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

* ComposeView ในมุมมองของ Fregment
* ComposeView ในมุมมองที่ยังไม่ทราบว่าวงจรของลูกค้าเป็นอย่างไร

ComposeView ในรายการ "ส่วนย่อย"

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

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

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

        android:id="@+id/text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />

        android:id="@+id/compose_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

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

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

หรือจะใช้การเชื่อมโยงมุมมองเพื่อรับการอ้างอิงComposeViewก็ได้โดยอ้างอิงคลาสการเชื่อมโยงที่สร้างขึ้นสำหรับไฟล์เลย์เอาต์ 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 การแสดงผลนี้แสดงเอาต์พุตของโค้ดที่เพิ่มองค์ประกอบการเขียนในลําดับชั้น UI ของมุมมอง ข้อความ "สวัสดี Android" จะแสดงโดยวิดเจ็ต TextView ข้อความ "สวัสดี Compose" แสดงโดยองค์ประกอบข้อความ Compose

นอกจากนี้ คุณยังใส่ ComposeView ลงในข้อมูลโค้ดโดยตรงได้หากสร้างหน้าจอแบบเต็มด้วย 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


   name="compose_view_x" type="id" />
   name="compose_view_y" type="id" />

แสดงตัวอย่างคอมโพเนนต์ที่เขียนด้วย Compose ได้ในเครื่องมือสร้างเลย์เอาต์

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

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

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

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

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

        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"/>

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

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

เมื่อทราบ API การทำงานร่วมกันเพื่อใช้ Compose ใน View แล้ว โปรดดูวิธีใช้ View ใน Compose