การใช้ View ใน Compose

คุณสามารถใส่ลําดับชั้นของมุมมอง Android ใน UI ของ Compose ได้ แนวทางนี้จะมีประโยชน์อย่างยิ่งหากคุณต้องการใช้องค์ประกอบ UI ที่ยังไม่มีให้ใช้งานในเครื่องมือเขียน เช่น AdView วิธีนี้ยังช่วยให้คุณนํามุมมองที่กําหนดเองซึ่งอาจออกแบบไว้มาใช้ซ้ำได้ด้วย

หากต้องการรวมองค์ประกอบมุมมองหรือลําดับชั้น ให้ใช้ AndroidView composable AndroidView ได้รับค่า Lambda ที่แสดงผล View AndroidView ยังมี update การเรียกกลับที่จะเรียกใช้เมื่อมีการขยายมุมมอง AndroidView จะจัดเรียงใหม่ทุกครั้งที่มีการเปลี่ยนแปลงStateที่อ่านภายในการเรียกกลับ AndroidView เช่นเดียวกับคอมโพสิเบิลในตัวอื่นๆ อีกหลายรายการ จะใช้พารามิเตอร์ Modifier ที่สามารถใช้เพื่อตั้งค่าตำแหน่งในคอมโพสิเบิลหลักได้

@Composable
fun CustomView() {
    var selectedItem by remember { mutableStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 1
                }
            }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary

            // As selectedItem is read here, AndroidView will recompose
            // whenever the state changes
            // Example of Compose -> View communication
            view.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

AndroidView ที่มี View Binding

หากต้องการฝังเลย์เอาต์ XML ให้ใช้ AndroidViewBinding API ซึ่งมาจากไลบรารี androidx.compose.ui:ui-viewbinding โดยโปรเจ็กต์ต้องเปิดใช้การเชื่อมโยงมุมมอง

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

AndroidView ในรายการแบบเลื่อน

หากคุณใช้ AndroidView ในรายการแบบ Lazy (LazyColumn, LazyRow, Pager ฯลฯ) ให้ลองใช้การโอเวอร์โหลด AndroidView ที่เปิดตัวในเวอร์ชัน 1.4.0-rc01 การโอเวอร์โหลดนี้ช่วยให้ Compose ใช้อินสแตนซ์ View ที่อยู่เบื้องหลังซ้ำได้เมื่อใช้การคอมโพสิชันที่มีซ้ำตามที่เป็นอยู่สำหรับรายการแบบ Lazy

การโอเวอร์โหลด AndroidView นี้จะเพิ่มพารามิเตอร์อีก 2 รายการ ได้แก่

  • onReset - เรียกใช้การเรียกกลับเพื่อส่งสัญญาณว่าViewกำลังจะนํามาใช้ซ้ำ ค่านี้ต้องไม่เท่ากับ Null เพื่อเปิดใช้การนํามุมมองมาใช้ซ้ำ
  • onRelease (ไม่บังคับ) - การเรียกกลับที่เรียกให้แสดงสัญญาณว่า View ออกจากการเรียบเรียงแล้วและจะไม่นํากลับมาใช้อีก

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

ข้อมูลโค้ดใน Compose

ใช้คอมโพสิเบิล AndroidViewBinding เพื่อเพิ่ม Fragment ในคอมโพสิชัน AndroidViewBinding มีการจัดการเฉพาะสำหรับแฟรกเมนต์ เช่น การนำแฟรกเมนต์ออกเมื่อคอมโพสิเบิลออกจากการคอมโพสิชัน

โดยให้ทำดังนี้ ขยาย XML ที่มี FragmentContainerView เป็นตัวยึดสำหรับ Fragment

ตัวอย่างเช่น หากได้กําหนด my_fragment_layout.xml แล้ว คุณจะใช้โค้ดแบบนี้ได้ขณะแทนที่แอตทริบิวต์ XML android:name ด้วยชื่อคลาสของ Fragment

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.compose.snippets.interop.MyFragment" />

ขยายข้อมูลโค้ดนี้ใน "เขียน" ดังนี้

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

หากต้องใช้หลายข้อมูลโค้ดในเลย์เอาต์เดียวกัน โปรดตรวจสอบว่าคุณได้กำหนดรหัสที่ไม่ซ้ำกันสำหรับ FragmentContainerView แต่ละรายการแล้ว

การเรียกใช้เฟรมเวิร์ก Android จาก Compose

Compose ทำงานภายในคลาสเฟรมเวิร์ก Android เช่น โฮสต์ในคลาส Android View เช่น Activity หรือ Fragment และอาจใช้คลาสเฟรมเวิร์ก Android เช่น Context, ทรัพยากรของระบบ, Service หรือ BroadcastReceiver

ดูข้อมูลเพิ่มเติมเกี่ยวกับทรัพยากรของระบบได้ที่ทรัพยากรในคอมโพซ

องค์ประกอบในท้องถิ่น

คลาส CompositionLocal ช่วยให้ส่งข้อมูลได้โดยไม่ระบุผ่านฟังก์ชันคอมโพสิเบิล โดยปกติแล้วจะมีค่าในโหนดหนึ่งของต้นไม้ UI ค่าดังกล่าวสามารถใช้โดยองค์ประกอบย่อยแบบคอมโพสิเบิลได้โดยไม่ต้องประกาศ CompositionLocal ว่าเป็นพารามิเตอร์ในฟังก์ชันแบบคอมโพสิเบิล

CompositionLocal ใช้เพื่อเผยแพร่ค่าสำหรับประเภทเฟรมเวิร์ก Android ใน Compose เช่น Context, Configuration หรือ View ที่โฮสต์โค้ด Compose ไว้กับ LocalContext, LocalConfiguration หรือ LocalView ที่เกี่ยวข้อง โปรดทราบว่าคลาส CompositionLocal จะมี Local นำหน้าเพื่อให้ค้นพบได้ง่ายขึ้นด้วยการเติมข้อความอัตโนมัติใน IDE

เข้าถึงค่าปัจจุบันของ CompositionLocal โดยใช้พร็อพเพอร์ตี้ current ตัวอย่างเช่น โค้ดด้านล่างแสดงข้อความโทสต์โดยระบุ LocalContext.current ลงในเมธอด Toast.makeToast

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

ดูตัวอย่างที่สมบูรณ์ยิ่งขึ้นได้ในส่วนกรณีศึกษา: BroadcastReceiver ที่ท้ายเอกสารนี้

การโต้ตอบอื่นๆ

หากไม่มีการกำหนดยูทิลิตีสำหรับการโต้ตอบที่ต้องการ แนวทางปฏิบัติแนะนำคือการปฏิบัติตามหลักเกณฑ์ทั่วไปของ Compose ซึ่งก็คือข้อมูลไหลลง เหตุการณ์ไหลขึ้น (ดูรายละเอียดเพิ่มเติมในการคิดใน Compose) เช่น คอมโพสิเบิลนี้จะเปิดใช้งานกิจกรรมอื่น

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}

กรณีศึกษา: ตัวรับสัญญาณการออกอากาศ

ตัวอย่างที่ใกล้เคียงกับการใช้งานจริงมากขึ้นของฟีเจอร์ที่คุณอาจต้องการย้ายข้อมูลหรือติดตั้งใช้งานใน Compose และเพื่อแสดง CompositionLocal และผลข้างเคียง สมมติว่าต้องลงทะเบียน BroadcastReceiver จากฟังก์ชันคอมโพสิเบิล

โซลูชันนี้ใช้ประโยชน์จาก LocalContext เพื่อใช้บริบทปัจจุบัน และ rememberUpdatedState และผลข้างเคียงของ DisposableEffect

@Composable
fun SystemBroadcastReceiver(
    systemAction: String,
    onSystemEvent: (intent: Intent?) -> Unit
) {
    // Grab the current context in this part of the UI tree
    val context = LocalContext.current

    // Safely use the latest onSystemEvent lambda passed to the function
    val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)

    // If either context or systemAction changes, unregister and register again
    DisposableEffect(context, systemAction) {
        val intentFilter = IntentFilter(systemAction)
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                currentOnSystemEvent(intent)
            }
        }

        context.registerReceiver(broadcast, intentFilter)

        // When the effect leaves the Composition, remove the callback
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}

@Composable
fun HomeScreen() {

    SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
        val isCharging = /* Get from batteryStatus ... */ true
        /* Do something if the device is charging */
    }

    /* Rest of the HomeScreen */
}

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

ตอนนี้คุณทราบเกี่ยวกับ API การทํางานร่วมกันเมื่อใช้การเขียนในมุมมองและในทางกลับกันแล้ว โปรดดูข้อมูลเพิ่มเติมในหน้าข้อควรพิจารณาอื่นๆ