แม้ว่าการย้ายข้อมูลจาก View ไปยัง Compose จะเกี่ยวข้องกับ UI โดยเฉพาะ แต่ก็มีหลายสิ่งที่ต้องคำนึงถึงเพื่อให้การย้ายข้อมูลเป็นไปอย่างปลอดภัยและค่อยๆ เป็นค่อยๆ ไป หน้านี้มีข้อควรพิจารณาบางประการขณะย้ายข้อมูลแอปแบบ View-based ไปยัง Compose
การย้ายข้อมูลธีมของแอป
Material Design เป็นระบบการออกแบบที่แนะนำสำหรับการกำหนดธีมแอป Android
สำหรับแอปแบบ View-based จะมี Material 3 เวอร์ชันให้เลือกใช้ ดังนี้
- Material Design 1 โดยใช้ไลบรารี
AppCompat (เช่น
Theme.AppCompat.*) - Material Design 2 โดยใช้
MDC-Android
ไลบรารี (เช่น
Theme.MaterialComponents.*) - Material Design 3 โดยใช้
ไลบรารี MDC-Android (เช่น
Theme.Material3.*)
สำหรับแอป Compose จะมี Material 2 เวอร์ชันให้เลือกใช้ ดังนี้
- Material Design 2 โดยใช้
ไลบรารี Compose Material
(เช่น
androidx.compose.material.MaterialTheme) - Material Design 3 โดยใช้ไลบรารี
Compose Material 3
(เช่น
androidx.compose.material3.MaterialTheme)
เราขอแนะนำให้ใช้เวอร์ชันล่าสุด (Material 3) หากระบบการออกแบบของแอปพร้อมที่จะทำเช่นนั้น โดยมีคู่มือการย้ายข้อมูลสำหรับทั้ง View และ Compose ดังนี้
- Material 1 ไปเป็น Material 2 ใน View
- Material 2 ไปเป็น Material 3 ใน View
- Material 2 ไปเป็น Material 3 ใน Compose
เมื่อสร้างหน้าจอใหม่ใน Compose ไม่ว่าคุณจะใช้ Material Design เวอร์ชันใดก็ตาม ให้ตรวจสอบว่าได้ใช้ MaterialTheme ก่อนที่จะใช้ Composables ใดๆ ที่แสดงผล UI จากไลบรารี Compose Material คอมโพเนนต์ Material (Button, Text ฯลฯ) ต้องใช้ MaterialTheme และลักษณะการทำงานของคอมโพเนนต์จะไม่ได้กำหนดไว้หากไม่มี MaterialTheme
ตัวอย่าง Jetpack Compose ทั้งหมด
ใช้ธีม Compose ที่กำหนดเองซึ่งสร้างขึ้นจาก MaterialTheme
ดูข้อมูลเพิ่มเติมได้ที่ ระบบการออกแบบใน Compose และ การย้ายข้อมูลธีม XML ไปยัง Compose
การนำทาง
หากคุณใช้คอมโพเนนต์การนำทางในแอป โปรดดู ข้อมูลเพิ่มเติมที่การนำทางด้วย Compose - ความสามารถในการทำงานร่วมกันและ การย้ายข้อมูล Jetpack Navigation ไปยัง Navigation Compose
ทดสอบ UI แบบผสม Compose/View
หลังจากย้ายข้อมูลบางส่วนของแอปไปยัง Compose แล้ว การทดสอบเป็นสิ่งสำคัญเพื่อให้แน่ใจว่าคุณไม่ได้ทำให้เกิดข้อผิดพลาด
เมื่อกิจกรรมหรือ Fragment ใช้ Compose คุณต้องใช้
createAndroidComposeRule
แทนการใช้ ActivityScenarioRule createAndroidComposeRule ผสานรวม ActivityScenarioRule กับ ComposeTestRule ซึ่งช่วยให้คุณทดสอบโค้ด Compose และ View ได้พร้อมกัน
class MyActivityTest { @Rule @JvmField val composeTestRule = createAndroidComposeRule<MyActivity>() @Test fun testGreeting() { val greeting = InstrumentationRegistry.getInstrumentation() .targetContext.resources.getString(R.string.greeting) composeTestRule.onNodeWithText(greeting).assertIsDisplayed() } }
ดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบได้ที่การทดสอบเลย์เอาต์ Compose หากต้องการความสามารถในการทำงานร่วมกันกับเฟรมเวิร์กการทดสอบ UI โปรดดู ความสามารถในการทำงานร่วมกันกับ Espresso และ ความสามารถในการทำงานร่วมกันกับ UiAutomator
การผสานรวม Compose กับสถาปัตยกรรมแอปที่มีอยู่
รูปแบบสถาปัตยกรรมโฟลว์ข้อมูลแบบทิศทางเดียว (UDF) ทำงานร่วมกับ Compose ได้อย่างราบรื่น หากแอปใช้รูปแบบสถาปัตยกรรมประเภทอื่นๆ เช่น Model View Presenter (MVP) เราขอแนะนำให้คุณย้ายข้อมูล UI ส่วนนั้นไปยัง UDF ก่อนหรือขณะใช้ Compose
การใช้ ViewModel ใน Compose
หากคุณใช้ไลบรารี Architecture Components
ViewModel คุณจะเข้าถึง
ViewModel จาก Composables ใดก็ได้โดย
เรียกใช้ฟังก์ชัน
viewModel()
ตามที่อธิบายไว้ใน Compose และไลบรารีอื่นๆ
ViewModel ไปยัง Composables อื่นๆ แต่ให้ส่งเฉพาะข้อมูลที่ Composables อื่นๆ ต้องการและฟังก์ชันที่ดำเนินการตรรกะที่จำเป็นเป็นพารามิเตอร์
เมื่อใช้ Compose โปรดระมัดระวังในการใช้ ViewModel ประเภทเดียวกันใน Composables ต่างๆ เนื่องจากองค์ประกอบ ViewModel เป็นไปตามขอบเขตวงจรการทำงานของ View ขอบเขตจะเป็นกิจกรรมโฮสต์, Fragment หรือกราฟการนำทางหากใช้ไลบรารีการนำทาง
ตัวอย่างเช่น หาก Composables โฮสต์อยู่ในกิจกรรม viewModel() จะแสดงผลอินสแตนซ์เดียวกันเสมอ ซึ่งจะล้างข้อมูลเมื่อกิจกรรมสิ้นสุดลงเท่านั้น
ในตัวอย่างต่อไปนี้ ผู้ใช้คนเดียวกัน ("user1") ได้รับการทักทาย 2 ครั้งเนื่องจากมีการใช้ GreetingViewModel อินสแตนซ์เดียวกันซ้ำใน Composables ทั้งหมดภายใต้กิจกรรมโฮสต์ ระบบจะใช้อินสแตนซ์ ViewModel แรกที่สร้างขึ้นซ้ำใน Composables อื่นๆ
class GreetingActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Column { GreetingScreen("user1") GreetingScreen("user2") } } } } } @Composable fun GreetingScreen( userId: String, viewModel: GreetingViewModel = viewModel( factory = GreetingViewModelFactory(userId) ) ) { val messageUser by viewModel.message.observeAsState("") Text(messageUser) } class GreetingViewModel(private val userId: String) : ViewModel() { private val _message = MutableLiveData("Hi $userId") val message: LiveData<String> = _message } class GreetingViewModelFactory(private val userId: String) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return GreetingViewModel(userId) as T } }
เนื่องจากกราฟการนำทางยังกำหนดขอบเขตองค์ประกอบ ViewModel ด้วย Composables ที่เป็นปลายทางในกราฟการนำทางจึงมีอินสแตนซ์ ViewModel ที่แตกต่างกัน
ในกรณีนี้ ViewModel จะกำหนดขอบเขตไว้ที่วงจรการทำงานของปลายทาง และจะล้างข้อมูลเมื่อนำปลายทางออกจาก Backstack ในตัวอย่างต่อไปนี้ เมื่อผู้ใช้นำทางไปยังหน้าจอ โปรไฟล์ ระบบจะสร้างอินสแตนซ์ GreetingViewModel ใหม่
@Composable fun MyApp() { NavHost(rememberNavController(), startDestination = "profile/{userId}") { /* ... */ composable("profile/{userId}") { backStackEntry -> GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "") } } }
แหล่งข้อมูลที่เชื่อถือได้ของสถานะ
เมื่อคุณใช้ Compose ใน UI ส่วนหนึ่ง เป็นไปได้ที่โค้ด Compose และ
โค้ดระบบ View จะต้องแชร์ข้อมูล เราขอแนะนำให้คุณห่อหุ้มสถานะที่แชร์นั้นไว้ในคลาสอื่นที่ปฏิบัติตามแนวทางปฏิบัติแนะนำของ UDF ที่ทั้ง 2 แพลตฟอร์มใช้ เช่น ใน ViewModel ที่แสดงสตรีมข้อมูลที่แชร์เพื่อแสดงการอัปเดตข้อมูล
อย่างไรก็ตาม คุณอาจทำเช่นนั้นไม่ได้เสมอไปหากข้อมูลที่จะแชร์มีการเปลี่ยนแปลงได้หรือผูกกับองค์ประกอบ UI อย่างแน่นหนา ในกรณีนี้ ระบบหนึ่งต้องเป็นแหล่งข้อมูลที่เชื่อถือได้ และระบบนั้นต้องแชร์การอัปเดตข้อมูลกับอีกระบบหนึ่ง โดยทั่วไปแล้ว แหล่งข้อมูลที่เชื่อถือได้ควรเป็นขององค์ประกอบที่อยู่ใกล้กับรูทของลำดับชั้น UI มากที่สุด
Compose เป็นแหล่งข้อมูลที่เชื่อถือได้
ใช้ Composables
SideEffect
เพื่อเผยแพร่สถานะ Compose ไปยังโค้ดที่ไม่ใช่ Compose ในกรณีนี้ แหล่งข้อมูลที่เชื่อถือได้จะเก็บไว้ใน Composables ซึ่งจะส่งการอัปเดตสถานะ
ตัวอย่างเช่น ไลบรารีการวิเคราะห์อาจอนุญาตให้คุณแบ่งกลุ่มฐานผู้ใช้โดยแนบข้อมูลเมตาที่กำหนดเอง (พร็อพเพอร์ตี้ผู้ใช้ ในตัวอย่างนี้) กับเหตุการณ์การวิเคราะห์ทั้งหมดที่ตามมา หากต้องการสื่อสารประเภทผู้ใช้ของผู้ใช้ปัจจุบันกับไลบรารีข้อมูลวิเคราะห์ ให้ใช้ SideEffect เพื่ออัปเดตค่า
@Composable fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics { val analytics: FirebaseAnalytics = remember { FirebaseAnalytics() } // On every successful composition, update FirebaseAnalytics with // the userType from the current User, ensuring that future analytics // events have this metadata attached SideEffect { analytics.setUserProperty("userType", user.userType) } return analytics }
ดูข้อมูลเพิ่มเติมได้ที่ผลข้างเคียงใน Compose
ระบบ View เป็นแหล่งข้อมูลที่เชื่อถือได้
หากระบบ View เป็นเจ้าของสถานะและแชร์สถานะกับ Compose เราขอแนะนำให้คุณห่อหุ้มสถานะไว้ในออบเจ็กต์ mutableStateOf เพื่อให้ Compose ใช้ได้อย่างปลอดภัย หากใช้วิธีนี้ ฟังก์ชัน Composables จะง่ายขึ้นเนื่องจากไม่มีแหล่งข้อมูลที่เชื่อถือได้อีกต่อไป แต่ระบบ View ต้องอัปเดตสถานะที่เปลี่ยนแปลงได้และ View ที่ใช้สถานะนั้น
ในตัวอย่างต่อไปนี้ CustomViewGroup มี TextView และ ComposeView ที่มี Composables TextField อยู่ภายใน TextView ต้องแสดงเนื้อหาที่ผู้ใช้พิมพ์ใน TextField
class CustomViewGroup @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : LinearLayout(context, attrs, defStyle) { // Source of truth in the View system as mutableStateOf // to make it thread-safe for Compose private var text by mutableStateOf("") private val textView: TextView init { orientation = VERTICAL textView = TextView(context) val composeView = ComposeView(context).apply { setContent { MaterialTheme { TextField(value = text, onValueChange = { updateState(it) }) } } } addView(textView) addView(composeView) } // Update both the source of truth and the TextView private fun updateState(newValue: String) { text = newValue textView.text = newValue } }
การย้ายข้อมูล UI ที่แชร์
หากคุณกำลังย้ายข้อมูลไปยัง Compose ทีละน้อย คุณอาจต้องใช้องค์ประกอบ UI ที่แชร์ทั้งใน Compose และระบบ View ตัวอย่างเช่น หากแอปมีคอมโพเนนต์ CallToActionButton ที่กำหนดเอง คุณอาจต้องใช้คอมโพเนนต์นี้ทั้งในหน้าจอ Compose และหน้าจอแบบ View-based
ใน Compose องค์ประกอบ UI ที่แชร์จะกลายเป็น Composables ที่นำกลับมาใช้ใหม่ได้ทั่วทั้งแอป ไม่ว่าองค์ประกอบนั้นจะจัดรูปแบบโดยใช้ XML หรือเป็น View ที่กำหนดเอง ตัวอย่างเช่น คุณจะสร้าง Composables CallToActionButton สำหรับคอมโพเนนต์การกระตุ้นให้ดำเนินการที่กำหนดเอง Button
หากต้องการใช้ Composables ในหน้าจอแบบ View-based ให้สร้าง Wrapper View ที่กำหนดเองซึ่งขยายจาก AbstractComposeView ใน Composables Content ที่ลบล้าง ให้วาง Composables ที่คุณสร้างขึ้นซึ่งห่อหุ้มไว้ในธีม Compose ดังที่แสดงในตัวอย่างด้านล่าง
@Composable fun CallToActionButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Button( colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary ), onClick = onClick, modifier = modifier, ) { Text(text) } } class CallToActionViewButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : AbstractComposeView(context, attrs, defStyle) { var text by mutableStateOf("") var onClick by mutableStateOf({}) @Composable override fun Content() { YourAppTheme { CallToActionButton(text, onClick) } } }
โปรดทราบว่าพารามิเตอร์ Composables จะกลายเป็นตัวแปรที่เปลี่ยนแปลงได้ภายใน View ที่กำหนดเอง ซึ่งจะทำให้ View CallToActionViewButton ที่กำหนดเองสามารถขยายและใช้งานได้เหมือนกับ View แบบเดิม ดูตัวอย่างการผูก View Binding
กับข้อมูลด้านล่าง:
class ViewBindingActivity : ComponentActivity() { private lateinit var binding: ActivityExampleBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) binding.callToAction.apply { text = getString(R.string.greeting) onClick = { /* Do something */ } } } }
หากคอมโพเนนต์ที่กำหนดเองมีสถานะที่เปลี่ยนแปลงได้ โปรดดู แหล่งข้อมูลที่เชื่อถือได้ของ สถานะ
ให้ความสำคัญกับการแยกสถานะออกจากงานนำเสนอ
โดยทั่วไปแล้ว View จะมีสถานะ View จะจัดการฟิลด์ที่อธิบาย สิ่งที่ จะแสดง นอกเหนือจาก วิธี แสดง เมื่อคุณ แปลง View เป็น Compose ให้แยกข้อมูลที่จะแสดงผลเพื่อให้ ได้โฟลว์ข้อมูลแบบทิศทางเดียวตามที่อธิบายไว้เพิ่มเติมใน การย้ายสถานะ
ตัวอย่างเช่น View มีพร็อพเพอร์ตี้ visibility ที่อธิบายว่ามองเห็นได้ มองไม่เห็น หรือหายไป ซึ่งเป็นพร็อพเพอร์ตี้โดยธรรมชาติของ View แม้ว่าโค้ดส่วนอื่นๆ อาจเปลี่ยนการมองเห็นของ View แต่มีเพียง View เองเท่านั้นที่ทราบว่าการมองเห็นปัจจุบันเป็นอย่างไร ตรรกะในการตรวจสอบว่า View มองเห็นได้หรือไม่นั้นอาจเกิดข้อผิดพลาดได้ง่าย และมักจะผูกกับ View เอง
ในทางตรงกันข้าม Compose ช่วยให้แสดง Composables ที่แตกต่างกันโดยสิ้นเชิงได้ง่ายๆ โดยใช้ตรรกะแบบมีเงื่อนไขใน Kotlin ดังนี้
@Composable fun MyComposable(showCautionIcon: Boolean) { if (showCautionIcon) { CautionIcon(/* ... */) } }
CautionIcon ไม่จำเป็นต้องทราบหรือสนใจว่าเหตุใดจึงมีการแสดงผล และไม่มีแนวคิดเรื่อง visibility โดยไอคอนจะอยู่ใน Composition หรือไม่เท่านั้น
การแยกการจัดการสถานะและตรรกะการนำเสนออย่างชัดเจนช่วยให้คุณเปลี่ยนวิธีแสดงเนื้อหาได้อย่างอิสระมากขึ้นเมื่อแปลงสถานะเป็น UI นอกจากนี้ การยกสถานะเมื่อจำเป็นยังทำให้ Composables นำกลับมาใช้ใหม่ได้มากขึ้น เนื่องจากความเป็นเจ้าของสถานะมีความยืดหยุ่นมากขึ้น
ส่งเสริมคอมโพเนนต์ที่ห่อหุ้มและนำกลับมาใช้ใหม่ได้
องค์ประกอบ View มักจะทราบว่าองค์ประกอบนั้นอยู่ตรงไหน เช่น ภายใน Activity, Dialog, Fragment หรือที่ใดที่หนึ่งภายในลำดับชั้น View อื่น เนื่องจากมักจะขยายจากไฟล์เลย์เอาต์แบบคงที่ โครงสร้างโดยรวมของ View จึงมีแนวโน้มที่จะแข็งทื่อมาก ซึ่งส่งผลให้เกิดการผูกที่แน่นแฟ้นยิ่งขึ้น และทำให้การเปลี่ยนแปลงหรือการนำ View กลับมาใช้ใหม่ทำได้ยากขึ้น
ตัวอย่างเช่น View ที่กำหนดเองอาจสันนิษฐานว่ามี View ย่อยประเภทหนึ่งที่มีรหัสหนึ่ง และเปลี่ยนพร็อพเพอร์ตี้โดยตรงเพื่อตอบสนองต่อการดำเนินการบางอย่าง ซึ่งจะผูกองค์ประกอบ View เหล่านั้นเข้าด้วยกันอย่างแน่นหนา View ที่กำหนดเองอาจหยุดทำงานหรือเกิดข้อผิดพลาดหากไม่พบ View ย่อย และ View ย่อยอาจนำกลับมาใช้ใหม่ไม่ได้หากไม่มี View หลักที่กำหนดเอง
ปัญหาดังกล่าวเกิดขึ้นน้อยลงใน Compose ที่มี Composables ที่นำกลับมาใช้ใหม่ได้ องค์ประกอบหลักสามารถระบุสถานะและการเรียกกลับได้อย่างง่ายดาย คุณจึงเขียน Composables ที่นำกลับมาใช้ใหม่ได้โดยไม่ต้องทราบตำแหน่งที่แน่นอนที่จะใช้ Composables เหล่านั้น
@Composable fun AScreen() { var isEnabled by rememberSaveable { mutableStateOf(false) } Column { ImageWithEnabledOverlay(isEnabled) ControlPanelWithToggle( isEnabled = isEnabled, onEnabledChanged = { isEnabled = it } ) } }
ในตัวอย่างด้านบน ทั้ง 3 ส่วนได้รับการห่อหุ้มมากขึ้นและผูกกันน้อยลง ดังนี้
ImageWithEnabledOverlayจำเป็นต้องทราบเฉพาะสถานะisEnabledปัจจุบันเท่านั้น ไม่จำเป็นต้องทราบว่าControlPanelWithToggleมีอยู่ หรือแม้แต่จะควบคุมได้อย่างไรControlPanelWithToggleไม่ทราบว่าImageWithEnabledOverlayมีอยู่ อาจมีวิธีแสดงisEnabledเป็น 0, 1 หรือมากกว่านั้น และControlPanelWithToggleไม่จำเป็นต้องเปลี่ยนแปลงสำหรับองค์ประกอบหลัก ไม่สำคัญว่า
ImageWithEnabledOverlayหรือControlPanelWithToggleจะซ้อนกันลึกเพียงใด องค์ประกอบย่อยเหล่านั้นอาจแสดงการเปลี่ยนแปลงแบบเคลื่อนไหว สลับเนื้อหา หรือส่งเนื้อหาไปยังองค์ประกอบย่อยอื่นๆ
รูปแบบนี้เรียกว่า การผกผันของการควบคุม ซึ่งคุณสามารถอ่านเพิ่มเติม
ได้ในเอกสารประกอบ CompositionLocal
การจัดการการเปลี่ยนแปลงขนาดหน้าจอ
การมีทรัพยากรที่แตกต่างกันสำหรับขนาดหน้าต่างที่แตกต่างกันเป็นวิธีหลักวิธีหนึ่งในการสร้างเลย์เอาต์ View ที่ปรับเปลี่ยนตามอุปกรณ์ แม้ว่าทรัพยากรที่มีคุณสมบัติยังคงเป็นตัวเลือกสำหรับการตัดสินใจเกี่ยวกับเลย์เอาต์ระดับหน้าจอ แต่ Compose ช่วยให้การเปลี่ยนเลย์เอาต์ทั้งหมดในโค้ดด้วยตรรกะแบบมีเงื่อนไขปกติทำได้ง่ายขึ้นมาก ดูข้อมูลเพิ่มเติมได้ที่ใช้คลาสขนาดหน้าต่าง
นอกจากนี้ โปรดดูหัวข้อ รองรับขนาดการแสดงผลต่างๆ เพื่อดูข้อมูลเกี่ยวกับเทคนิคที่ Compose มีให้เพื่อสร้าง UI แบบปรับอัตโนมัติ
การเลื่อนที่ฝังไว้ด้วย View
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีเปิดใช้การทำงานร่วมกันของการเลื่อนที่ฝังไว้ระหว่าง องค์ประกอบ View ที่เลื่อนได้กับ Composables ที่เลื่อนได้ ซึ่งฝังไว้ทั้ง 2 ทิศทาง ได้ที่ การทำงานร่วมกันของการเลื่อนที่ฝังไว้
Compose ใน RecyclerView
Composables ใน RecyclerView มีประสิทธิภาพสูงนับตั้งแต่ RecyclerView เวอร์ชัน 1.3.0-alpha02 โปรดตรวจสอบว่าคุณใช้ RecyclerView เวอร์ชัน 1.3.0-alpha02 ขึ้นไปเพื่อดูข้อดีเหล่านั้น
WindowInsets การทำงานร่วมกันกับ View
คุณอาจต้องลบล้างระยะขอบเริ่มต้นเมื่อหน้าจอมีทั้งโค้ด View และโค้ด Compose อยู่ในลำดับชั้นเดียวกัน ในกรณีนี้ คุณต้องระบุอย่างชัดเจนว่าองค์ประกอบใดควรใช้ระยะขอบ และองค์ประกอบใดควรละเว้นระยะขอบ
ตัวอย่างเช่น หากเลย์เอาต์ชั้นนอกสุดเป็นเลย์เอาต์ Android View คุณควรใช้ระยะขอบในระบบ View และละเว้นระยะขอบสำหรับ Compose
หรือหากเลย์เอาต์ชั้นนอกสุดเป็น Composables คุณควรใช้ระยะขอบใน Compose และเพิ่มระยะห่างจากขอบให้กับ Composables AndroidView ตามความเหมาะสม
โดยค่าเริ่มต้น ComposeView แต่ละรายการจะใช้ระยะขอบทั้งหมดที่ระดับการใช้ WindowInsetsCompat หากต้องการเปลี่ยนลักษณะการทำงานเริ่มต้นนี้ ให้ตั้งค่า
ComposeView.consumeWindowInsets
เป็น false
ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบ WindowInsets ใน Compose
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- แสดงอีโมจิ
- Material Design 2 ใน Compose
- ระยะขอบหน้าต่างใน Compose