การใช้ View ใน Compose

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

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

@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 ให้ใช้ API AndroidViewBinding ซึ่งมีให้โดยไลบรารี androidx.compose.ui:ui-viewbinding หากต้องการทำเช่นนี้ โปรเจ็กต์ของคุณต้องเปิดใช้การเชื่อมโยงมุมมอง

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

AndroidView ใน Lazy List

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

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

  • onReset - การเรียกกลับที่เรียกใช้เพื่อส่งสัญญาณว่ากำลังจะนำ View กลับมาใช้ซ้ำ ต้องไม่เป็น Null เพื่อเปิดใช้การใช้ View ซ้ำ
  • onRelease (ไม่บังคับ) - การเรียกกลับที่เรียกใช้เพื่อส่งสัญญาณว่า 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 ให้ใช้ Composable AndroidFragment คุณส่งFragmentคลาสไปยังAndroidFragment ซึ่งจะเพิ่ม อินสแตนซ์ของคลาสนั้นลงในการคอมโพสิชันโดยตรง AndroidFragment ยังมีออบเจ็กต์ fragmentState เพื่อสร้าง AndroidFragment ด้วยสถานะที่กำหนด arguments เพื่อส่งไปยัง Fragment ใหม่ และการเรียกกลับ onUpdate ที่ให้ Fragment จากการจัดองค์ประกอบ เช่นเดียวกับ Composable บิวท์อินอื่นๆ อีกมากมาย AndroidFragment รับพารามิเตอร์ Modifier ที่คุณใช้ได้ เช่น เพื่อกำหนดตำแหน่งใน Composable ระดับบน

เรียกใช้ AndroidFragment ในฟีเจอร์เขียนอีเมลดังนี้

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

สถานะวงจรของ Fragment สูงสุด

ใน Fragment 1.9.0 ขึ้นไป คุณสามารถใช้พารามิเตอร์ maxLifecycle เพื่อจำกัดสถานะวงจรของ Fragment ที่ฝังไว้ได้

เช่น ใน HorizontalPager คุณสามารถตั้งค่า maxLifecycle เป็น RESUMED แบบไดนามิกได้เมื่อหน้าเว็บที่เกี่ยวข้องใช้งานอยู่เท่านั้น โดยจำกัดหน้าเว็บที่อยู่นอกหน้าจอไว้ที่ STARTED เมื่อผู้ใช้ไปยังหน้าอื่น ขีดจำกัดของส่วนย่อยของ หน้าก่อนหน้าจะกลายเป็น STARTED ซึ่งจะหยุดชั่วคราวหรือยกเลิกภาระงานที่กำหนดขอบเขตไว้ที่ RESUMED (เช่น โครูทีนที่ทำงานใน repeatOnLifecycle(RESUMED)) เพื่อป้องกันไม่ให้หน้าจอที่อยู่นอกหน้าจอทำงานเฉพาะเบื้องหน้า ขณะเดียวกันก็ยังคงเริ่มต้น UI ไว้ที่ STARTED

HorizontalPager(state = pagerState) { page ->
    // Dynamically cap the lifecycle state based on whether the page is selected
    val maxLifecycle = if (pagerState.settledPage == page) {
        Lifecycle.State.RESUMED
    } else {
        Lifecycle.State.STARTED
    }

    when (page) {
        0 -> AndroidFragment<HomeFragment>(maxLifecycle = maxLifecycle)
        1 -> AndroidFragment<LibraryFragment>(maxLifecycle = maxLifecycle)
        /* Other pages and corresponding fragments */
    }
}

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

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

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

การเรียบเรียงเพลงในภาษาท้องถิ่น

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

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 นี้ จะเปิดกิจกรรมอื่น

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