การใช้ View ใน Compose

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

หากต้องการรวมองค์ประกอบหรือลำดับชั้นของมุมมอง ให้ใช้ Composable AndroidView AndroidView จะส่งผ่าน Lambda ที่แสดงผล View นอกจากนี้ AndroidView ยังมี Callback update ที่จะเรียกใช้เมื่อขยายมุมมอง AndroidView จะจัดองค์ประกอบใหม่เมื่อใดก็ตามที่มีการเปลี่ยนแปลง State ที่อ่านภายใน Callback AndroidView เช่นเดียวกับ Composable ในตัวอื่นๆ จะใช้พารามิเตอร์ Modifier ที่สามารถใช้เพื่อกำหนดตำแหน่งใน 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 พร้อมการเชื่อมโยงมุมมอง

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

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

AndroidView ใน Lazy List

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

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

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

@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 ออกจาก Composition

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

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

@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

คลาส 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)
    }
}