การเปลี่ยนองค์ประกอบแบบใช้ร่วมกันในเครื่องมือเขียน

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

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

รูปที่ 1 เดโมองค์ประกอบที่ใช้ร่วมกันของ Jetsnack

ใน Compose มี API ระดับสูงบางรายการที่ช่วยคุณสร้างองค์ประกอบที่ใช้ร่วมกันได้ ดังนี้

  • SharedTransitionLayout: เลย์เอาต์นอกสุดที่จำเป็นสำหรับการใช้การเปลี่ยนภาพองค์ประกอบที่ใช้ร่วมกัน ซึ่งจะให้SharedTransitionScope Composable ต้องอยู่ใน SharedTransitionScope เพื่อใช้ตัวปรับแต่งองค์ประกอบที่แชร์
  • Modifier.sharedElement(): ตัวปรับแต่งที่แจ้งให้ SharedTransitionScope ทราบถึง Composable ที่ควรจับคู่กับ Composable อื่น
  • Modifier.sharedBounds(): ตัวแก้ไขที่แจ้ง SharedTransitionScope ว่าควรใช้ขอบเขตของ Composable นี้เป็นขอบเขตคอนเทนเนอร์สำหรับตำแหน่งที่ควรเกิดการเปลี่ยนภาพ sharedBounds() ออกแบบมาสำหรับเนื้อหาที่แตกต่างกันในเชิงภาพ ซึ่งตรงกันข้ามกับ sharedElement()

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

การใช้งานพื้นฐาน

ระบบจะสร้างการเปลี่ยนภาพต่อไปนี้ในส่วนนี้ โดยเปลี่ยนจากรายการ "ลิสต์" ขนาดเล็กเป็นรายการรายละเอียดขนาดใหญ่

การเปลี่ยนองค์ประกอบที่ใช้ร่วมกันขั้นพื้นฐานระหว่าง Composable 2 รายการใน Jetsnack
รูปที่ 2 ตัวอย่างพื้นฐานของการเปลี่ยนองค์ประกอบที่ใช้ร่วมกันระหว่าง Composable 2 รายการ

วิธีที่ดีที่สุดในการใช้ Modifier.sharedElement() คือใช้ร่วมกับ AnimatedContent, AnimatedVisibility หรือ NavHost เนื่องจากจะจัดการการเปลี่ยนระหว่าง Composable ให้คุณโดยอัตโนมัติ

จุดเริ่มต้นคือ AnimatedContent พื้นฐานที่มีอยู่ซึ่งมี MainContent และ DetailsContent Composable ก่อนที่จะเพิ่มองค์ประกอบที่แชร์

ภาพเคลื่อนไหวที่แสดงการเปลี่ยนภาพแบบครอสเฟดและสไลด์เริ่มต้นโดยไม่มีองค์ประกอบที่ใช้ร่วมกัน
รูปที่ 3 กำลังเริ่ม AnimatedContent โดยไม่มีองค์ประกอบแบบใช้ร่วมกันในการเปลี่ยนภาพ

  1. หากต้องการให้องค์ประกอบที่แชร์เคลื่อนไหวระหว่างเลย์เอาต์ทั้ง 2 แบบ ให้ล้อมรอบคอมโพสิเบิล AnimatedContent ด้วย SharedTransitionLayout ขอบเขตจาก SharedTransitionLayout และ AnimatedContent จะส่งไปยัง MainContent และ DetailsContent ดังนี้

    var showDetails by remember {
        mutableStateOf(false)
    }
    SharedTransitionLayout {
        AnimatedContent(
            showDetails,
            label = "basic_transition"
        ) { targetState ->
            if (!targetState) {
                MainContent(
                    onShowDetails = {
                        showDetails = true
                    },
                    animatedVisibilityScope = this@AnimatedContent,
                    sharedTransitionScope = this@SharedTransitionLayout
                )
            } else {
                DetailsContent(
                    onBack = {
                        showDetails = false
                    },
                    animatedVisibilityScope = this@AnimatedContent,
                    sharedTransitionScope = this@SharedTransitionLayout
                )
            }
        }
    }

  2. เพิ่ม Modifier.sharedElement() ลงในเชนตัวแก้ไขที่ประกอบกันได้ใน Composable 2 รายการที่ตรงกัน สร้างออบเจ็กต์ SharedContentState และจดจำด้วย rememberSharedContentState() ออบเจ็กต์ SharedContentState จะจัดเก็บคีย์ที่ไม่ซ้ำกันซึ่งกำหนดองค์ประกอบที่แชร์ ระบุคีย์ที่ไม่ซ้ำกันเพื่อระบุเนื้อหา และใช้ rememberSharedContentState() สำหรับรายการที่ต้องการให้จดจำ ระบบจะส่ง AnimatedContentScope ไปยังตัวแก้ไข ซึ่งใช้เพื่อประสานงานภาพเคลื่อนไหว

    @Composable
    private fun MainContent(
        onShowDetails: () -> Unit,
        modifier: Modifier = Modifier,
        sharedTransitionScope: SharedTransitionScope,
        animatedVisibilityScope: AnimatedVisibilityScope
    ) {
        Row(
            // ...
        ) {
            with(sharedTransitionScope) {
                Image(
                    painter = painterResource(id = R.drawable.cupcake),
                    contentDescription = "Cupcake",
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(key = "image"),
                            animatedVisibilityScope = animatedVisibilityScope
                        )
                        .size(100.dp)
                        .clip(CircleShape),
                    contentScale = ContentScale.Crop
                )
                // ...
            }
        }
    }
    
    @Composable
    private fun DetailsContent(
        modifier: Modifier = Modifier,
        onBack: () -> Unit,
        sharedTransitionScope: SharedTransitionScope,
        animatedVisibilityScope: AnimatedVisibilityScope
    ) {
        Column(
            // ...
        ) {
            with(sharedTransitionScope) {
                Image(
                    painter = painterResource(id = R.drawable.cupcake),
                    contentDescription = "Cupcake",
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(key = "image"),
                            animatedVisibilityScope = animatedVisibilityScope
                        )
                        .size(200.dp)
                        .clip(CircleShape),
                    contentScale = ContentScale.Crop
                )
                // ...
            }
        }
    }

หากต้องการดูข้อมูลว่ามีการจับคู่องค์ประกอบที่แชร์เกิดขึ้นหรือไม่ ให้ดึงข้อมูล rememberSharedContentState() ลงในตัวแปร แล้วค้นหา isMatchFound

ซึ่งจะส่งผลให้เกิดภาพเคลื่อนไหวอัตโนมัติต่อไปนี้

ภาพเคลื่อนไหวที่แสดงรูปภาพและข้อความขยายอย่างราบรื่นทั่วทั้งหน้าจอ
รูปที่ 4 ตัวอย่างพื้นฐานของการเปลี่ยนองค์ประกอบที่ใช้ร่วมกันระหว่าง Composable 2 รายการ

คุณอาจสังเกตเห็นว่าสีพื้นหลังและขนาดของคอนเทนเนอร์ทั้งหมดยังคงใช้การตั้งค่าเริ่มต้นของ AnimatedContent

ขอบเขตที่ใช้ร่วมกันเทียบกับองค์ประกอบที่ใช้ร่วมกัน

Modifier.sharedBounds() คล้ายกับ Modifier.sharedElement() อย่างไรก็ตาม ตัวแก้ไขมีความแตกต่างกันดังนี้

  • sharedBounds() มีไว้สำหรับเนื้อหาที่แตกต่างกันในเชิงภาพ แต่ควรแชร์พื้นที่เดียวกันระหว่างสถานะ ในขณะที่ sharedElement() คาดหวังว่าเนื้อหาจะเหมือนกัน
  • เมื่อใช้ sharedBounds() เนื้อหาที่เข้าและออกจากหน้าจอจะมองเห็นได้ในระหว่างการเปลี่ยนสถานะระหว่าง 2 สถานะ ส่วนเมื่อใช้ sharedElement() ระบบจะแสดงผลเฉพาะเนื้อหาเป้าหมายในขอบเขตการเปลี่ยนรูปแบบ Modifier.sharedBounds() มีพารามิเตอร์ enter และ exit สำหรับระบุวิธีที่เนื้อหาควรเปลี่ยนผ่าน ซึ่งคล้ายกับวิธีการทำงานของ AnimatedContent
  • กรณีการใช้งานที่พบบ่อยที่สุดสำหรับ sharedBounds() คือรูปแบบการเปลี่ยนรูปแบบคอนเทนเนอร์ ส่วนกรณีการใช้งานตัวอย่างสำหรับ sharedElement() คือการเปลี่ยนภาพฮีโร่
  • เมื่อใช้ Composable Text เราขอแนะนำให้ใช้ sharedBounds() เพื่อรองรับการเปลี่ยนแปลงแบบอักษร เช่น การเปลี่ยนระหว่างตัวเอียงและตัวหนา หรือการเปลี่ยนสี

จากตัวอย่างก่อนหน้านี้ การเพิ่ม Modifier.sharedBounds() ลงใน Row และ Column ใน 2 สถานการณ์ที่แตกต่างกันจะช่วยให้เราแชร์ขอบเขตของทั้ง 2 สถานการณ์และแสดงภาพเคลื่อนไหวของการเปลี่ยนผ่านได้ ซึ่งจะช่วยให้ทั้ง 2 สถานการณ์ขยายขนาดระหว่างกันได้ ดังนี้

@Composable
private fun MainContent(
    onShowDetails: () -> Unit,
    modifier: Modifier = Modifier,
    sharedTransitionScope: SharedTransitionScope,
    animatedVisibilityScope: AnimatedVisibilityScope
) {
    with(sharedTransitionScope) {
        Row(
            modifier = Modifier
                .padding(8.dp)
                .sharedBounds(
                    rememberSharedContentState(key = "bounds"),
                    animatedVisibilityScope = animatedVisibilityScope,
                    enter = fadeIn(),
                    exit = fadeOut(),
                    resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
                )
                // ...
        ) {
            // ...
        }
    }
}

@Composable
private fun DetailsContent(
    modifier: Modifier = Modifier,
    onBack: () -> Unit,
    sharedTransitionScope: SharedTransitionScope,
    animatedVisibilityScope: AnimatedVisibilityScope
) {
    with(sharedTransitionScope) {
        Column(
            modifier = Modifier
                .padding(top = 200.dp, start = 16.dp, end = 16.dp)
                .sharedBounds(
                    rememberSharedContentState(key = "bounds"),
                    animatedVisibilityScope = animatedVisibilityScope,
                    enter = fadeIn(),
                    exit = fadeOut(),
                    resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
                )
                // ...

        ) {
            // ...
        }
    }
}

รูปที่ 5 ขอบเขตที่ใช้ร่วมกันระหว่าง Composable 2 รายการ

ทำความเข้าใจขอบเขต

หากต้องการใช้ Modifier.sharedElement() คอมโพสิเบิลต้องอยู่ใน SharedTransitionScope SharedTransitionLayout Composable จะให้ SharedTransitionScope ตรวจสอบว่าได้วางไว้ที่จุดระดับบนสุดเดียวกันในลำดับชั้น UI ที่มีองค์ประกอบที่คุณต้องการแชร์

โดยทั่วไปแล้ว คุณควรวาง Composable ไว้ใน AnimatedVisibilityScope ด้วย โดยทั่วไปแล้วจะใช้ AnimatedContent เพื่อสลับระหว่าง Composable หรือเมื่อใช้ AnimatedVisibility โดยตรง หรือใช้ฟังก์ชันที่ประกอบกันได้ NavHost เว้นแต่คุณจะจัดการระดับการมองเห็นด้วยตนเอง หากต้องการใช้หลายขอบเขต ให้บันทึกขอบเขตที่จำเป็นใน CompositionLocal, ใช้พารามิเตอร์บริบทใน Kotlin หรือส่งขอบเขตเป็นพารามิเตอร์ไปยังฟังก์ชัน

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

val LocalNavAnimatedVisibilityScope = compositionLocalOf<AnimatedVisibilityScope?> { null }
val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null }

@Composable
private fun SharedElementScope_CompositionLocal() {
    // An example of how to use composition locals to pass around the shared transition scope, far down your UI tree.
    // ...
    SharedTransitionLayout {
        CompositionLocalProvider(
            LocalSharedTransitionScope provides this
        ) {
            // This could also be your top-level NavHost as this provides an AnimatedContentScope
            AnimatedContent(state, label = "Top level AnimatedContent") { targetState ->
                CompositionLocalProvider(LocalNavAnimatedVisibilityScope provides this) {
                    // Now we can access the scopes in any nested composables as follows:
                    val sharedTransitionScope = LocalSharedTransitionScope.current
                        ?: throw IllegalStateException("No SharedElementScope found")
                    val animatedVisibilityScope = LocalNavAnimatedVisibilityScope.current
                        ?: throw IllegalStateException("No AnimatedVisibility found")
                }
                // ...
            }
        }
    }
}

หรือหากลำดับชั้นของคุณไม่ได้ซ้อนกันหลายชั้น คุณสามารถส่งขอบเขตลงไปเป็นพารามิเตอร์ได้ ดังนี้

@Composable
fun MainContent(
    animatedVisibilityScope: AnimatedVisibilityScope,
    sharedTransitionScope: SharedTransitionScope
) {
}

@Composable
fun Details(
    animatedVisibilityScope: AnimatedVisibilityScope,
    sharedTransitionScope: SharedTransitionScope
) {
}

องค์ประกอบที่แชร์กับ AnimatedVisibility

ตัวอย่างก่อนหน้านี้แสดงวิธีใช้องค์ประกอบที่ใช้ร่วมกันกับ AnimatedContent แต่องค์ประกอบที่ใช้ร่วมกันก็ใช้กับ AnimatedVisibility ได้เช่นกัน

เช่น ในตัวอย่าง LazyColumn นี้ แต่ละองค์ประกอบจะรวมอยู่ใน AnimatedVisibility เมื่อคลิกรายการ เนื้อหาจะมีเอฟเฟกต์ภาพที่ดึงออกจาก UI ไปยังคอมโพเนนต์ที่คล้ายกับกล่องโต้ตอบ

var selectedSnack by remember { mutableStateOf<Snack?>(null) }

SharedTransitionLayout(modifier = Modifier.fillMaxSize()) {
    LazyColumn(
        // ...
    ) {
        items(listSnacks) { snack ->
            AnimatedVisibility(
                visible = snack != selectedSnack,
                enter = fadeIn() + scaleIn(),
                exit = fadeOut() + scaleOut(),
                modifier = Modifier.animateItem()
            ) {
                Box(
                    modifier = Modifier
                        .sharedBounds(
                            sharedContentState = rememberSharedContentState(key = "${snack.name}-bounds"),
                            // Using the scope provided by AnimatedVisibility
                            animatedVisibilityScope = this,
                            clipInOverlayDuringTransition = OverlayClip(shapeForSharedElement)
                        )
                        .background(Color.White, shapeForSharedElement)
                        .clip(shapeForSharedElement)
                ) {
                    SnackContents(
                        snack = snack,
                        modifier = Modifier.sharedElement(
                            sharedContentState = rememberSharedContentState(key = snack.name),
                            animatedVisibilityScope = this@AnimatedVisibility
                        ),
                        onClick = {
                            selectedSnack = snack
                        }
                    )
                }
            }
        }
    }
    // Contains matching AnimatedContent with sharedBounds modifiers.
    SnackEditDetails(
        snack = selectedSnack,
        onConfirmClick = {
            selectedSnack = null
        }
    )
}

รูปที่ 6 องค์ประกอบที่แชร์กับ AnimatedVisibility

การสั่งซื้อตัวขยาย

เมื่อใช้ Modifier.sharedElement() และ Modifier.sharedBounds() ลำดับของเชนตัวแก้ไขจะมีความสำคัญ เช่นเดียวกับส่วนที่เหลือของ Compose การวางตัวแก้ไขที่ส่งผลต่อขนาดอย่างไม่ถูกต้องอาจทำให้เกิดการข้ามภาพที่ไม่คาดคิดระหว่างการจับคู่องค์ประกอบที่แชร์

ตัวอย่างเช่น หากคุณวางตัวแก้ไขระยะห่างจากขอบในตำแหน่งที่ต่างกันในองค์ประกอบที่แชร์ 2 รายการ ภาพเคลื่อนไหวจะมีความแตกต่างกันในด้านภาพ

var selectFirst by remember { mutableStateOf(true) }
val key = remember { Any() }
SharedTransitionLayout(
    Modifier
        .fillMaxSize()
        .padding(10.dp)
        .clickable {
            selectFirst = !selectFirst
        }
) {
    AnimatedContent(targetState = selectFirst, label = "AnimatedContent") { targetState ->
        if (targetState) {
            Box(
                Modifier
                    .padding(12.dp)
                    .sharedBounds(
                        rememberSharedContentState(key = key),
                        animatedVisibilityScope = this@AnimatedContent
                    )
                    .border(2.dp, Color.Red)
            ) {
                Text(
                    "Hello",
                    fontSize = 20.sp
                )
            }
        } else {
            Box(
                Modifier
                    .offset(180.dp, 180.dp)
                    .sharedBounds(
                        rememberSharedContentState(
                            key = key,
                        ),
                        animatedVisibilityScope = this@AnimatedContent
                    )
                    .border(2.dp, Color.Red)
                    // This padding is placed after sharedBounds, but it doesn't match the
                    // other shared elements modifier order, resulting in visual jumps
                    .padding(12.dp)

            ) {
                Text(
                    "Hello",
                    fontSize = 36.sp
                )
            }
        }
    }
}

ขอบเขตที่ตรงกัน

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

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

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

ยกเว้นในกรณีที่คุณใช้ resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds() สำหรับภาพเคลื่อนไหว หรือ Modifier.skipToLookaheadSize() ใน Composable ในกรณีนี้ Compose จะจัดวางองค์ประกอบย่อยโดยใช้ข้อจำกัดเป้าหมาย และใช้ค่าตัวคูณมาตราส่วนเพื่อแสดงภาพเคลื่อนไหวแทนการเปลี่ยนขนาดเลย์เอาต์เอง

คีย์ที่ไม่ซ้ำกัน

เมื่อทำงานกับองค์ประกอบที่ใช้ร่วมกันที่ซับซ้อน แนวทางปฏิบัติแนะนำคือการสร้างคีย์ที่ไม่ใช่สตริง เนื่องจากสตริงอาจทำให้เกิดข้อผิดพลาดในการจับคู่ได้ คีย์แต่ละรายการต้องไม่ซ้ำกันเพื่อให้เกิดการจับคู่ ตัวอย่างเช่น ใน Jetsnack เรามีองค์ประกอบที่ใช้ร่วมกันต่อไปนี้

อินเทอร์เฟซ Jetsnack ที่มีคำอธิบายประกอบซึ่งระบุองค์ประกอบ UI ที่ใช้ร่วมกัน
รูปที่ 7 รูปภาพแสดง Jetsnack พร้อมคำอธิบายประกอบสำหรับแต่ละส่วนของ UI

คุณสามารถสร้าง enum เพื่อแสดงประเภทองค์ประกอบที่แชร์ได้ ในตัวอย่างนี้ การ์ดขนมขบเคี้ยวทั้งการ์ดยังปรากฏในตำแหน่งต่างๆ บนหน้าจอหลักได้ด้วย เช่น ในส่วน "ยอดนิยม" และ "แนะนำ" คุณสามารถสร้างคีย์ที่มี snackId, origin ("ยอดนิยม" / "แนะนำ") และ type ขององค์ประกอบที่แชร์ซึ่งจะแชร์ได้ดังนี้

data class SnackSharedElementKey(
    val snackId: Long,
    val origin: String,
    val type: SnackSharedElementType
)

enum class SnackSharedElementType {
    Bounds,
    Image,
    Title,
    Tagline,
    Background
}

@Composable
fun SharedElementUniqueKey() {
    // ...
            Box(
                modifier = Modifier
                    .sharedElement(
                        rememberSharedContentState(
                            key = SnackSharedElementKey(
                                snackId = 1,
                                origin = "latest",
                                type = SnackSharedElementType.Image
                            )
                        ),
                        animatedVisibilityScope = this@AnimatedVisibility
                    )
            )
            // ...
}

เราขอแนะนำให้ใช้คลาสข้อมูลสำหรับคีย์ เนื่องจากคลาสข้อมูลจะใช้ hashCode() และ equals()

จัดการระดับการมองเห็นองค์ประกอบที่แชร์ด้วยตนเอง

ในกรณีที่คุณอาจไม่ได้ใช้ AnimatedVisibility หรือ AnimatedContent คุณสามารถจัดการระดับการเข้าถึงองค์ประกอบที่แชร์ได้ด้วยตนเอง ใช้ Modifier.sharedElementWithCallerManagedVisibility() และระบุเงื่อนไขของคุณเองซึ่งกำหนดว่าเมื่อใดที่รายการควรปรากฏหรือไม่ปรากฏ ดังนี้

var selectFirst by remember { mutableStateOf(true) }
val key = remember { Any() }
SharedTransitionLayout(
    Modifier
        .fillMaxSize()
        .padding(10.dp)
        .clickable {
            selectFirst = !selectFirst
        }
) {
    Box(
        Modifier
            .sharedElementWithCallerManagedVisibility(
                rememberSharedContentState(key = key),
                !selectFirst
            )
            .background(Color.Red)
            .size(100.dp)
    ) {
        Text(if (!selectFirst) "false" else "true", color = Color.White)
    }
    Box(
        Modifier
            .offset(180.dp, 180.dp)
            .sharedElementWithCallerManagedVisibility(
                rememberSharedContentState(
                    key = key,
                ),
                selectFirst
            )
            .alpha(0.5f)
            .background(Color.Blue)
            .size(180.dp)
    ) {
        Text(if (selectFirst) "false" else "true", color = Color.White)
    }
}

ข้อจำกัดในปัจจุบัน

API เหล่านี้มีข้อจำกัดบางประการ โดยเฉพาะอย่างยิ่งในเรื่องต่อไปนี้

  • ไม่รองรับความสามารถในการทำงานร่วมกันระหว่าง Views และ Compose ซึ่งรวมถึง Composable ที่รวม AndroidView เช่น Dialog หรือ ModalBottomSheet
  • ระบบไม่รองรับภาพเคลื่อนไหวอัตโนมัติสำหรับรายการต่อไปนี้
    • คอมโพสิเบิลรูปภาพที่แชร์:
      • ContentScale จะไม่เคลื่อนไหวโดยค่าเริ่มต้น โดยจะจัดชิดกับจุดสิ้นสุดที่ตั้งไว้ ContentScale
    • การครอบตัดรูปร่าง - ไม่มีการรองรับในตัวสำหรับการเคลื่อนไหวอัตโนมัติระหว่างรูปร่าง เช่น การเคลื่อนไหวจากสี่เหลี่ยมจัตุรัสเป็นวงกลมเมื่อรายการเปลี่ยนสถานะ
    • สำหรับกรณีที่ไม่รองรับ ให้ใช้ Modifier.sharedBounds() แทน sharedElement() และเพิ่ม Modifier.animateEnterExit() ลงในรายการ