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

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

ข้อกำหนดเฉพาะของแอนิเมชัน

หากต้องการเปลี่ยนข้อกำหนดของภาพเคลื่อนไหวที่ใช้สำหรับการเคลื่อนไหวของขนาดและตำแหน่ง ให้ระบุพารามิเตอร์ 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 ก่อน วัดเลย์เอาต์ย่อยด้วยข้อจำกัด Lookahead (หรือเป้าหมาย) จากนั้นระบบจะปรับขนาดเลย์เอาต์ที่เสถียรของรายการย่อยให้พอดีกับขอบเขตที่แชร์ ScaleToBounds อาจหมายถึง "ระดับกราฟิก" ระหว่างรัฐเหล่านี้

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

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

ดูความแตกต่างระหว่างโหมดการปรับขนาด 2 โหมดได้จากตัวอย่างต่อไปนี้

ScaleToBounds

RemeasureToBounds

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

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

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

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

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

คลิปและการวางซ้อน

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

โดยจะแสดงผลทับองค์ประกอบ 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 ช่วยยกสูงขึ้น

ไม่มี Modifier.renderInSharedTransitionScopeOverlay()

ด้วย Modifier.renderInSharedTransitionScopeOverlay()

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

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

(สังเกตว่ารายการอื่นๆ ในรายการขยับลงเพื่อตอบสนองต่อรายการเดียวที่เพิ่มขึ้นอย่างไร)