การใช้ Compose ใน View

คุณสามารถเพิ่ม 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 เท่านั้น

ViewCompositionStrategy เป็นเวลา ComposeView

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

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

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

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

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

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

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

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

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

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

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

* ComposeView ในมุมมองของ Fragment
* ComposeView ในข้อมูลพร็อพเพอร์ตี้ที่ยังไม่ทราบวงจร

ComposeView ใน Fragment

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

คุณสามารถวาง 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() เพื่อใช้การเขียน

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

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

คุณยังรวม 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:

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

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

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

สมมติว่าคุณต้องการแสดง Composable ต่อไปนี้ในเครื่องมือแก้ไขการออกแบบ หมายเหตุ ที่ 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>

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

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

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