การใช้ View ใน Compose

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

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

@Composable
fun CustomView() {
    var selectedItem by remember { mutableIntStateOf(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

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

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

AndroidView ในรายการ Lazy

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

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

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

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

Fragment ใน Compose (ขั้นตอนการเปลี่ยนผ่าน)

ใช้ Composable AndroidFragment เพื่อเพิ่ม Fragment ใน Compose AndroidFragment มีการจัดการเฉพาะ Fragment เช่น การนำ Fragment ออกเมื่อ Composable ออกจากการเรียบเรียง

หากต้องการรวม Fragment ให้ใช้ AndroidFragment Composable คุณส่งคลาส Fragment ไปยัง AndroidFragment ซึ่งจะเพิ่ม อินสแตนซ์ของคลาสดังกล่าวลงในการเรียบเรียงโดยตรง นอกจากนี้ AndroidFragment ยังมีออบเจ็กต์ fragmentState เพื่อสร้าง AndroidFragment ที่มีสถานะที่กำหนด arguments เพื่อส่งไปยัง Fragment ใหม่ และ Callback onUpdate ที่ให้ Fragment จากการเรียบเรียง AndroidFragment ยอมรับพารามิเตอร์ Modifier ซึ่งคุณสามารถใช้เพื่อกำหนดตำแหน่งใน Composable หลักได้ เช่นเดียวกับ Composable บิวท์อินอื่นๆ อีกมากมาย

เรียก AndroidFragment ใน Compose ดังนี้

@Composable
fun FragmentInComposeExample() {
    AndroidFragment<MyFragment>()
}

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

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

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

Composition Local

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

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

เข้าถึงค่าปัจจุบันของ CompositionLocal โดยใช้พร็อพเพอร์ตี้ current ตัวอย่างเช่น โค้ดด้านล่างจะแสดงข้อความ Toast โดยระบุ 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")
    }
}

Broadcast Receiver

หากต้องการแสดง 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 */
}

การโต้ตอบรูปแบบอื่น

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

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