ปรับแต่งการเปลี่ยนองค์ประกอบที่แชร์

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

ข้อกำหนดของภาพเคลื่อนไหว

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

เช่น หากต้องการให้ข้อความในตัวอย่างก่อนหน้าเคลื่อนที่ตามส่วนโค้ง ให้ระบุพารามิเตอร์ boundsTransform เพื่อใช้ข้อกำหนด keyframes ดังนี้

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

คุณใช้ AnimationSpec ใดก็ได้ ตัวอย่างนี้ใช้ข้อกำหนด keyframes

รูปที่ 1 ตัวอย่างที่แสดงboundsTransformพารามิเตอร์
ต่างๆ

โหมดปรับขนาด

เมื่อเคลื่อนไหวระหว่างขอบเขตที่แชร์ 2 รายการ คุณสามารถตั้งค่าพารามิเตอร์ resizeMode เป็น RemeasureToBounds หรือ ScaleToBounds ได้ พารามิเตอร์นี้จะกำหนดวิธี การเปลี่ยนองค์ประกอบที่แชร์ระหว่าง 2 สถานะ ScaleToBounds จะวัดเลย์เอาต์ย่อยด้วยข้อจำกัดแบบมองล่วงหน้า (หรือเป้าหมาย) ก่อน จากนั้นระบบจะปรับขนาดเลย์เอาต์ที่เสถียรของ บุตรหลานให้พอดีกับขอบเขตที่แชร์ ScaleToBounds สามารถมองได้ว่าเป็น "มาตราส่วนกราฟิก" ระหว่างสถานะ

ในทางตรงกันข้าม RemeasureToBounds จะวัดและจัดเลย์เอาต์ใหม่สำหรับเลย์เอาต์ย่อยของ sharedBounds โดยมีข้อจำกัดแบบคงที่ที่เคลื่อนไหวได้ตามขนาดเป้าหมาย การวัดซ้ำจะทริกเกอร์โดยการเปลี่ยนแปลงขนาดขอบเขต ซึ่งอาจเกิดขึ้น ทุกเฟรม

สำหรับ Composable ของ Text เราขอแนะนำให้ใช้ ScaleToBounds เนื่องจากจะช่วยหลีกเลี่ยงการจัดวางใหม่ และการปรับข้อความให้พอดีกับบรรทัดต่างๆ RemeasureToBounds ขอแนะนำ สำหรับขอบเขตที่มีสัดส่วนภาพต่างกัน และหากต้องการให้องค์ประกอบที่แชร์ 2 รายการมีความต่อเนื่องที่ราบรื่น

ความแตกต่างระหว่างโหมดเปลี่ยนขนาดทั้ง 2 โหมดจะเห็นได้ในตัวอย่างต่อไปนี้

ScaleToBounds

RemeasureToBounds

ข้ามไปยังเลย์เอาต์สุดท้าย

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

ตัวอย่างต่อไปนี้แสดงข้อความคำอธิบาย "Lorem Ipsum" ที่ปรากฏบนหน้าจอใน 2 วิธีที่แตกต่างกัน ในตัวอย่างแรก ข้อความจะปรับให้พอดีกับคอนเทนเนอร์เมื่อคอนเทนเนอร์มีขนาดใหญ่ขึ้น ในตัวอย่างที่ 2 ข้อความจะไม่ จัดข้อความใหม่เมื่อขยาย การเพิ่ม Modifier.skipToLookaheadSize() จะป้องกันการปรับข้อความใหม่ เมื่อข้อความยาวขึ้น

ไม่มี Modifier.skipToLookahead() - สังเกตข้อความ "Lorem Ipsum" ที่มีการปรับข้อความใหม่

Modifier.skipToLookahead() - สังเกตว่าข้อความ "Lorem Ipsum" ยังคงมีสถานะสุดท้ายที่จุดเริ่มต้นของภาพเคลื่อนไหว

คลิปและการซ้อนทับ

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

โดยจะแสดงทับองค์ประกอบ UI อื่นๆ ที่ไม่ได้แชร์ เมื่อการเปลี่ยนผ่าน เสร็จสมบูรณ์แล้ว ระบบจะนำองค์ประกอบออกจากภาพซ้อนทับไปยังDrawScopeขององค์ประกอบนั้นเอง

หากต้องการตัดองค์ประกอบที่ใช้ร่วมกันให้เป็นรูปร่าง ให้ใช้ฟังก์ชัน Modifier.clip() มาตรฐาน วางไว้หลัง sharedElement() ดังนี้

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

หากต้องการให้มั่นใจว่าองค์ประกอบที่แชร์จะไม่แสดงผลนอกคอนเทนเนอร์หลัก คุณสามารถตั้งค่า clipInOverlayDuringTransition ใน sharedElement() ได้ โดยค่าเริ่มต้น สำหรับขอบเขตที่แชร์ที่ซ้อนกัน clipInOverlayDuringTransition จะใช้เส้นทางคลิปจาก sharedBounds() ระดับบน

หากต้องการรองรับการคงองค์ประกอบ UI บางอย่าง เช่น แถบด้านล่างหรือปุ่มการดำเนินการลอย ไว้ด้านบนเสมอในระหว่างการเปลี่ยนองค์ประกอบที่แชร์ ให้ใช้ Modifier.renderInSharedTransitionScopeOverlay() โดยค่าเริ่มต้น ตัวแก้ไขนี้จะเก็บเนื้อหาไว้ในภาพซ้อนทับในช่วงเวลาที่ การเปลี่ยนฉากที่แชร์ทำงานอยู่

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

ไม่มีModifier.renderInSharedTransitionScopeOverlay()

ด้วย Modifier.renderInSharedTransitionScopeOverlay()

คุณอาจต้องการให้ Composable ที่ไม่ได้แชร์เคลื่อนไหวออกไปและ ยังคงอยู่เหนือ Composable อื่นๆ ก่อนการเปลี่ยน ในกรณีเช่นนี้ ให้ใช้ renderInSharedTransitionScopeOverlay().animateEnterExit() เพื่อเคลื่อนไหว Composables ออกขณะที่การเปลี่ยนภาพองค์ประกอบที่ใช้ร่วมกันทำงาน

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

รูปที่ 2 แถบแอปด้านล่างเลื่อนเข้าและออกเมื่อภาพเคลื่อนไหวเปลี่ยน

ในกรณีที่ไม่ได้เกิดขึ้นบ่อยนัก หากคุณไม่ต้องการให้องค์ประกอบที่แชร์แสดงใน ภาพซ้อนทับ คุณสามารถตั้งค่า renderInOverlayDuringTransition ใน sharedElement() เป็น false ได้

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

โดยค่าเริ่มต้น sharedBounds() และ sharedElement() จะไม่แจ้งให้คอนเทนเนอร์ระดับบนสุดทราบถึงการเปลี่ยนแปลงขนาดใดๆ เมื่อเลย์เอาต์เปลี่ยน

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

PlaceholderSize.contentSize (ค่าเริ่มต้น)

PlaceholderSize.animatedSize

(สังเกตว่ารายการอื่นๆ ในรายการจะเลื่อนลงเมื่อมีรายการหนึ่งเพิ่มขึ้น)