ข้อควรพิจารณาอื่นๆ

แม้ว่าการย้ายข้อมูลจากมุมมองไปยัง Compose จะเกี่ยวข้องกับ UI เท่านั้น แต่ก็มีหลายๆ สิ่งที่ต้องพิจารณาเพื่อให้การย้ายข้อมูลมีความปลอดภัยและเพิ่มขึ้นอย่างต่อเนื่อง หน้านี้มีสิ่งที่ควรพิจารณาขณะย้ายข้อมูลแอปที่อิงตาม View ไปยัง Compose

การย้ายข้อมูลธีมของแอป

การออกแบบ Material Design เป็นระบบการออกแบบที่แนะนำสำหรับธีมแอป Android

สำหรับแอปที่อิงตามการดู จะมี Material ให้ดาวน์โหลด 3 เวอร์ชันดังนี้

  • Material Design 1 โดยใช้ไลบรารี AppCompat (เช่น Theme.AppCompat.*)
  • Material Design 2 โดยใช้ไลบรารี MDC-Android (เช่น Theme.MaterialComponents.*)
  • ดีไซน์ Material 3 โดยใช้ไลบรารี MDC-Android (เช่น Theme.Material3.*)

สำหรับแอป Compose วัสดุมี 2 เวอร์ชัน ได้แก่

  • ดีไซน์ Material 2 โดยใช้ไลบรารี Compose Material (เช่น androidx.compose.material.MaterialTheme)
  • ดีไซน์ Material 3 โดยใช้ไลบรารี Compose Material 3 (เช่น androidx.compose.material3.MaterialTheme)

เราขอแนะนำให้ใช้เวอร์ชันล่าสุด (Material 3) หากระบบการออกแบบของแอปรองรับการใช้งานเวอร์ชันดังกล่าว เรามีคำแนะนำในการย้ายข้อมูลสำหรับทั้ง Views และ Compose ดังนี้

เมื่อสร้างหน้าจอใหม่ใน Compose ไม่ว่าคุณจะใช้ Material Design เวอร์ชันใดก็ตาม โปรดตรวจสอบว่าคุณใช้ MaterialTheme ก่อนคอมโพสิเบิลที่แสดง UI จากคลัง Material ของ Compose คอมโพเนนต์ของวัสดุ (Button, Text ฯลฯ) ขึ้นอยู่กับว่ามี MaterialTheme อยู่หรือไม่ และลักษณะการทํางานจะไม่มีการกําหนดหากไม่มี MaterialTheme

ตัวอย่าง Jetpack Compose ทั้งหมดใช้ธีม Compose ที่กําหนดเองซึ่งสร้างขึ้นจาก MaterialTheme

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

หากคุณใช้คอมโพเนนต์การนำทางในแอป โปรดดูข้อมูลเพิ่มเติมในการไปยังส่วนต่างๆ ด้วย Compose - การทํางานร่วมกัน และย้ายข้อมูลการนำทางของ Jetpack ไปยัง Navigation Compose

ทดสอบ UI ของ Compose/Views แบบผสม

หลังจากย้ายข้อมูลบางส่วนของแอปไปยัง Compose แล้ว การทดสอบเป็นขั้นตอนสําคัญเพื่อให้แน่ใจว่าคุณไม่ได้ทําให้แอปเสียหาย

เมื่อกิจกรรมหรือแฟรกเมนต์ใช้ 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()
    }
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบได้ที่การทดสอบเลย์เอาต์การเขียน หากต้องการทำงานร่วมกับเฟรมเวิร์กการทดสอบ UI โปรดดูความสามารถในการทำงานร่วมกับ Espresso และความสามารถในการทำงานร่วมกับ UiAutomator

การผสานรวม Compose เข้ากับสถาปัตยกรรมแอปที่มีอยู่

รูปแบบสถาปัตยกรรม Unidirectional Data Flow (UDF) ทำงานร่วมกับ Compose ได้อย่างราบรื่น หากแอปใช้รูปแบบสถาปัตยกรรมประเภทอื่นๆ แทน เช่น Model View Presenter (MVP) เราขอแนะนำให้คุณย้ายข้อมูล UI ในส่วนนั้นไปยัง UDF ก่อนหรือขณะที่ใช้งาน Compose

การใช้ ViewModel ในเครื่องมือเขียน

หากใช้ไลบรารี Architecture Components ViewModel คุณจะเข้าถึง ViewModel จากคอมโพสิเบิลใดก็ได้โดยเรียกใช้ฟังก์ชัน viewModel() ตามที่อธิบายไว้ในCompose และไลบรารีอื่นๆ

เมื่อใช้ Compose ให้ระมัดระวังในการใช้ ViewModel ประเภทเดียวกันในคอมโพสิเบิลต่างๆ เนื่องจากองค์ประกอบ ViewModel จะเป็นไปตามขอบเขตวงจรชีวิตของ View ขอบเขตจะเป็นกิจกรรมของโฮสต์ เศษ หรือกราฟการนําทาง หากใช้คลังการนําทาง

เช่น หากคอมโพสิเบิลโฮสต์อยู่ในกิจกรรม viewModel() จะแสดงอินสแตนซ์เดียวกันเสมอ ซึ่งระบบจะล้างออกก็ต่อเมื่อกิจกรรมเสร็จสิ้น ในตัวอย่างต่อไปนี้ ระบบจะทักทายผู้ใช้รายเดียวกัน ("user1") 2 ครั้ง เนื่องจากมีการใช้อินสแตนซ์ GreetingViewModel เดียวกันซ้ำในคอมโพสิเบิลทั้งหมดภายใต้กิจกรรมของโฮสต์ อินสแตนซ์ ViewModel แรกที่สร้างจะนำมาใช้ซ้ำใน Composable อื่นๆ

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 ด้วย คอมโพสิเบิลที่เป็นปลายทางในกราฟการนำทางจึงมีอินสแตนซ์ ViewModel อื่น ในกรณีนี้ ViewModel จะกำหนดขอบเขตตามวงจรชีวิตของปลายทาง และระบบจะล้าง ViewModel เมื่อนำปลายทางออกจากกองซ้อนด้านหลัง ในตัวอย่างต่อไปนี้ เมื่อผู้ใช้ไปยังหน้าจอโปรไฟล์ ระบบจะสร้างอินสแตนซ์ใหม่ของ GreetingViewModel

@Composable
fun MyApp() {
    NavHost(rememberNavController(), startDestination = "profile/{userId}") {
        /* ... */
        composable("profile/{userId}") { backStackEntry ->
            GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "")
        }
    }
}

แหล่งข้อมูลที่เป็นความจริงของรัฐ

เมื่อคุณใช้งาน Compose ในส่วนหนึ่งของ UI อาจเป็นไปได้ที่การเขียนและโค้ดของระบบ View ต้องแชร์ข้อมูลกัน เราขอแนะนําให้คุณรวมสถานะที่แชร์นั้นไว้ในคลาสอื่นที่เป็นไปตามแนวทางปฏิบัติแนะนําของ UDF ซึ่งทั้ง 2 แพลตฟอร์มใช้ เช่น ใน ViewModel ที่แสดงสตรีมข้อมูลที่แชร์เพื่อส่งการอัปเดตข้อมูล

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

เขียนเป็นแหล่งข้อมูลที่เชื่อถือได้

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

ตัวอย่างเช่น ไลบรารีข้อมูลวิเคราะห์อาจช่วยให้คุณแบ่งกลุ่มประชากรผู้ใช้ได้โดยแนบข้อมูลเมตาที่กําหนดเอง (พร็อพเพอร์ตี้ผู้ใช้ในตัวอย่างนี้) ไปกับเหตุการณ์การวิเคราะห์ที่ตามมาทั้งหมด หากต้องการสื่อสารประเภทผู้ใช้ของผู้ใช้ปัจจุบันกับคลังข้อมูลวิเคราะห์ ให้ใช้ 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

ดูระบบเป็นแหล่งข้อมูลที่ถูกต้อง

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

ในตัวอย่างต่อไปนี้ CustomViewGroup มี TextView และ ComposeView ที่มี 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 ที่กําหนดเอง คุณอาจต้องใช้คอมโพเนนต์นั้นทั้งในหน้าจอแบบคอมโพสิทและหน้าจอแบบ View

ใน Compose องค์ประกอบ UI ที่แชร์จะกลายเป็นคอมโพสิเบิลที่นํากลับมาใช้ใหม่ได้ทั่วทั้งแอป ไม่ว่าองค์ประกอบนั้นจะจัดสไตล์โดยใช้ XML หรือเป็นมุมมองที่กําหนดเองก็ตาม เช่น คุณอาจสร้างคอมโพสิเบิล CallToActionButton สําหรับคอมโพเนนต์คำกระตุ้นให้ดำเนินการ Button ที่กําหนดเอง

หากต้องการใช้คอมโพสิเบิลในหน้าจอแบบ View ให้สร้าง Wrapper มุมมองที่กําหนดเองซึ่งขยายจาก AbstractComposeView ใน Composable Content ที่ลบล้าง ให้วาง Composable ที่คุณสร้างขึ้นซึ่งรวมอยู่ในธีม 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)
        }
    }
}

โปรดทราบว่าพารามิเตอร์แบบคอมโพสิเบิลจะกลายเป็นตัวแปรที่เปลี่ยนแปลงได้ภายในมุมมองที่กําหนดเอง วิธีนี้จะทำให้มุมมอง CallToActionViewButton ที่กําหนดเองขยายและใช้งานได้ เช่นเดียวกับมุมมองแบบดั้งเดิม ดูตัวอย่างการใช้การเชื่อมโยงมุมมองด้านล่าง

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 ช่วยให้การแสดงคอมโพสิเบิลที่แตกต่างกันโดยสิ้นเชิงเป็นเรื่องง่ายโดยใช้ตรรกะแบบมีเงื่อนไขใน Kotlin ดังนี้

@Composable
fun MyComposable(showCautionIcon: Boolean) {
    if (showCautionIcon) {
        CautionIcon(/* ... */)
    }
}

CautionIcon ไม่จำเป็นต้องรู้หรือสนใจว่าเหตุใดจึงมีการแสดง CautionIcon ขึ้น และไม่มีแนวคิดของ visibility นั่นคือ CautionIcon อยู่ในองค์ประกอบหรือไม่อยู่ในองค์ประกอบ

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

โปรโมตชิ้นส่วนที่ห่อหุ้มไว้และนำมาใช้ซ้ำได้

องค์ประกอบ View มักจะทราบตำแหน่งที่ตัวเองอยู่ เช่น ภายใน Activity, Dialog, Fragment หรือภายในลําดับชั้น View อื่น เนื่องจากไฟล์เหล่านี้มักสร้างขึ้นจากไฟล์เลย์เอาต์แบบคงที่ โครงสร้างโดยรวมของ View จึงมีแนวโน้มที่จะเป็นแบบแผนมาก ซึ่งส่งผลให้มีการเชื่อมต่อที่เหนียวแน่นขึ้นและทำให้เปลี่ยนหรือนำ View มาใช้ซ้ำได้ยากขึ้น

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

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

@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 อาจไม่มีเลย อย่างน้อย 1 วิธี และ ControlPanelWithToggle ไม่จำเป็นต้องเปลี่ยนแปลง

  • สำหรับระดับบนสุด ไม่ว่า ImageWithEnabledOverlay หรือ ControlPanelWithToggle จะฝังลึกแค่ไหนก็ตาม เด็กเหล่านั้นอาจกำลังแสดงภาพเคลื่อนไหวการเปลี่ยนแปลง เปลี่ยนเนื้อหา หรือส่งต่อเนื้อหาให้เด็กคนอื่นๆ

รูปแบบนี้เรียกว่าการเปลี่ยนการควบคุม ซึ่งคุณสามารถอ่านข้อมูลเพิ่มเติมได้ในเอกสารประกอบของ CompositionLocal

การจัดการกับการเปลี่ยนแปลงขนาดหน้าจอ

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

นอกจากนี้ โปรดดูหัวข้อรองรับหน้าจอขนาดต่างๆเพื่อดูข้อมูลเกี่ยวกับเทคนิคที่ Compose มีให้เพื่อสร้าง UI แบบปรับเปลี่ยนได้

การเลื่อนที่ฝังไว้ด้วย Views

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีเปิดใช้การทำงานร่วมกันในการเลื่อนแบบซ้อนกันระหว่างองค์ประกอบ View ที่เลื่อนได้และคอมโพสิชันที่เลื่อนได้ ซึ่งซ้อนกันทั้ง 2 ทิศทางได้ในส่วนการทำงานร่วมกันในการเลื่อนแบบซ้อนกัน

เขียนใน RecyclerView

Composable ใน RecyclerView มีประสิทธิภาพตั้งแต่ RecyclerView เวอร์ชัน 1.3.0-alpha02 ตรวจสอบว่าคุณใช้ RecyclerView เวอร์ชัน 1.3.0-alpha02 เวอร์ชัน 1.3.0-alpha02 เป็นอย่างน้อยเพื่อดูสิทธิประโยชน์เหล่านั้น

WindowInsets ทำงานร่วมกับข้อมูลพร็อพเพอร์ตี้

คุณอาจต้องลบล้างส่วนเริ่มต้นเมื่อหน้าจอมีทั้งมุมมองและโค้ด Compose ในลำดับชั้นเดียวกัน ในกรณีนี้ คุณต้องระบุอย่างชัดเจนว่าชิ้นงานใดควรใช้ส่วนแทรก และชิ้นงานใดควรละเว้น

ตัวอย่างเช่น หากเลย์เอาต์ด้านนอกสุดเป็นเลย์เอาต์ของ Android View คุณควรใช้งานส่วนนั้นในระบบมุมมองและไม่ต้องสนใจ Compose หรือหากเลย์เอาต์ด้านนอกสุดเป็นคอมโพสิเบิล คุณควรใช้ส่วนเยื้องใน Compose และเพิ่มระยะห่างให้กับคอมโพสิเบิล AndroidView ตามนั้น

โดยค่าเริ่มต้น ComposeView แต่ละรายการจะใช้อินเซ็ตทั้งหมดในระดับการบริโภค WindowInsetsCompat หากต้องการเปลี่ยนลักษณะการทำงานเริ่มต้นนี้ ให้ตั้งค่า ComposeView.consumeWindowInsets เป็น false

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