หากต้องการปรับแต่งวิธีการทำงานของภาพเคลื่อนไหวการเปลี่ยนภาพองค์ประกอบแบบใช้ร่วมกัน คุณสามารถใช้พารามิเตอร์บางอย่างเพื่อเปลี่ยนวิธีการเปลี่ยนภาพองค์ประกอบแบบใช้ร่วมกันได้
ข้อกำหนดของภาพเคลื่อนไหว
หากต้องการเปลี่ยนข้อกำหนดของภาพเคลื่อนไหวที่ใช้สำหรับการเคลื่อนไหวของขนาดและตำแหน่ง คุณสามารถระบุพารามิเตอร์ 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
boundsTransformพารามิเตอร์โหมดปรับขนาด
เมื่อสร้างภาพเคลื่อนไหวระหว่างขอบเขตที่แชร์ 2 ขอบเขต คุณสามารถตั้งค่าพารามิเตอร์ resizeMode เป็น RemeasureToBounds หรือ ScaleToBounds ก็ได้ พารามิเตอร์นี้จะกำหนดวิธีที่องค์ประกอบที่ใช้ร่วมกันเปลี่ยนสถานะระหว่าง 2 สถานะ ScaleToBounds จะวัดเลย์เอาต์ย่อยด้วยข้อจำกัด Lookahead (หรือเป้าหมาย) ก่อน จากนั้นเลย์เอาต์ที่เสถียรขององค์ประกอบย่อยจะปรับขนาดให้พอดีกับขอบเขตที่ใช้ร่วมกัน
ScaleToBounds ถือได้ว่าเป็น "มาตราส่วนแบบกราฟิก" ระหว่างสถานะต่างๆ
ในทางตรงกันข้าม RemeasureToBounds จะวัดและจัดเลย์เอาต์ย่อยของ sharedBounds อีกครั้งด้วยข้อจำกัดคงที่แบบเคลื่อนไหวตามขนาดเป้าหมาย การวัดอีกครั้งจะทริกเกอร์โดยการเปลี่ยนแปลงขนาดขอบเขต ซึ่งอาจเกิดขึ้นได้ทุกเฟรม
สำหรับคอมโพสิเบิล Text เราขอแนะนำให้ใช้ ScaleToBounds เนื่องจากจะช่วยหลีกเลี่ยงการจัดเลย์เอาต์ใหม่และการจัดเรียงข้อความใหม่ในบรรทัดต่างๆ ขอแนะนำให้ใช้ RemeasureToBounds สำหรับขอบเขตที่มีสัดส่วนภาพต่างกัน และหากต้องการให้องค์ประกอบที่แชร์ทั้ง 2 รายการมีความต่อเนื่องที่ลื่นไหล
ความแตกต่างระหว่างโหมดปรับขนาดทั้ง 2 โหมดจะเห็นได้จากตัวอย่างต่อไปนี้
|
|
|---|---|
เปิดและปิดใช้องค์ประกอบที่แชร์แบบไดนามิก
โดยค่าเริ่มต้น sharedElement() และ sharedBounds() จะได้รับการกำหนดค่าให้แสดงภาพเคลื่อนไหวของการเปลี่ยนแปลงเลย์เอาต์เมื่อใดก็ตามที่พบคีย์ที่ตรงกันในสถานะเป้าหมาย อย่างไรก็ตาม คุณอาจต้องการปิดใช้ภาพเคลื่อนไหวนี้แบบไดนามิกตามเงื่อนไขที่เฉพาะเจาะจง เช่น ทิศทางการนำทางหรือสถานะ UI ปัจจุบัน
หากต้องการควบคุมว่าจะให้เกิดการเปลี่ยนองค์ประกอบที่แชร์หรือไม่ คุณสามารถปรับแต่ง
SharedContentConfig ที่ส่งไปยัง rememberSharedContentState() ได้ พร็อพเพอร์ตี้ isEnabled จะกำหนดว่าองค์ประกอบที่แชร์ทำงานอยู่หรือไม่
ตัวอย่างต่อไปนี้แสดงวิธีกำหนดการกำหนดค่าที่เปิดใช้เฉพาะการเปลี่ยนที่แชร์เมื่อไปยังส่วนต่างๆ ระหว่างหน้าจอที่เฉพาะเจาะจง (เช่น จาก A ไป B เท่านั้น) ขณะที่ปิดใช้การเปลี่ยนที่แชร์สำหรับหน้าจออื่นๆ
SharedTransitionLayout { val transition = updateTransition(currentState) transition.AnimatedContent { targetState -> // Create the configuration that depends on state changing. fun animationConfig() : SharedTransitionScope.SharedContentConfig { return object : SharedTransitionScope.SharedContentConfig { override val SharedTransitionScope.SharedContentState.isEnabled: Boolean // For this example, we only enable the transition in one direction // from A -> B and not the other way around. get() = transition.currentState == "A" && transition.targetState == "B" } } when (targetState) { "A" -> Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } "B" -> { Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } } } } }
โดยค่าเริ่มต้น หากองค์ประกอบที่แชร์ถูกปิดใช้ระหว่างภาพเคลื่อนไหวที่กำลังดำเนินอยู่ องค์ประกอบนั้นจะยังคงเล่นภาพเคลื่อนไหวที่กำลังดำเนินอยู่จนจบเพื่อป้องกันไม่ให้นำภาพเคลื่อนไหวที่กำลังเล่นอยู่ออกโดยไม่ตั้งใจ หากต้องการนำองค์ประกอบออกขณะที่ภาพเคลื่อนไหวกำลังทำงาน คุณสามารถลบล้าง shouldKeepEnabledForOngoingAnimation ในอินเทอร์เฟซ SharedContentConfig เพื่อแสดงผลเป็นเท็จได้
ข้ามไปยังเลย์เอาต์สุดท้าย
โดยค่าเริ่มต้น เมื่อเปลี่ยนระหว่าง 2 เลย์เอาต์ ขนาดเลย์เอาต์จะเคลื่อนไหวระหว่างสถานะเริ่มต้นและสถานะสุดท้าย ซึ่งอาจเป็นลักษณะการทำงานที่ไม่พึงประสงค์เมื่อสร้างภาพเคลื่อนไหวให้กับเนื้อหา เช่น ข้อความ
ตัวอย่างต่อไปนี้แสดงข้อความอธิบาย "Lorem Ipsum" ที่เข้าสู่หน้าจอใน 2 วิธีที่แตกต่างกัน ในตัวอย่างแรก ข้อความจะจัดเรียงใหม่เมื่อเข้ามาในขณะที่ที่เก็บข้อความขยายขนาดใหญ่ขึ้น ในตัวอย่างที่ 2 ข้อความจะไม่จัดเรียงใหม่เมื่อขยายขนาด การเพิ่ม Modifier.skipToLookaheadSize() จะป้องกันไม่ให้เกิดการจัดเรียงใหม่เมื่อมีขนาดใหญ่ขึ้น
ไม่ |
|
|---|---|
คลิปและการวางซ้อน
เพื่อให้องค์ประกอบที่ใช้ร่วมกันสามารถแชร์ระหว่าง 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 อยู่ในระดับที่สูงขึ้น
ไม่มี |
ด้วย |
|---|---|
คุณอาจต้องการให้ Composable ที่ไม่ได้แชร์เคลื่อนไหวออกไป รวมถึงอยู่ด้านบนของ Composable อื่นๆ ก่อนการเปลี่ยนผ่าน ในกรณีเช่นนี้ ให้ใช้ renderInSharedTransitionScopeOverlay().animateEnterExit() เพื่อทำให้ Composable เคลื่อนไหวออกเมื่อการเปลี่ยนองค์ประกอบที่ใช้ร่วมกันทำงาน
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
ในบางกรณีซึ่งเกิดขึ้นไม่บ่อยนักที่คุณไม่ต้องการให้องค์ประกอบที่แชร์แสดงผลในภาพซ้อนทับ คุณสามารถตั้งค่า renderInOverlayDuringTransition ใน sharedElement() เป็นเท็จได้
แจ้งเตือนเลย์เอาต์ข้างเคียงเกี่ยวกับการเปลี่ยนแปลงขนาดองค์ประกอบที่แชร์
โดยค่าเริ่มต้น sharedBounds() และ sharedElement() จะไม่แจ้งให้คอนเทนเนอร์หลักทราบถึงการเปลี่ยนแปลงขนาดเมื่อเลย์เอาต์เปลี่ยนไป
หากต้องการเผยแพร่การเปลี่ยนแปลงขนาดไปยังคอนเทนเนอร์หลักเมื่อมีการเปลี่ยนผ่าน ให้เปลี่ยนพารามิเตอร์ placeholderSize เป็น PlaceholderSize.AnimatedSize การดำเนินการดังกล่าวจะทำให้รายการขยายหรือหดตัว รายการอื่นๆ ทั้งหมดในเลย์เอาต์จะตอบสนองต่อการเปลี่ยนแปลง
|
(สังเกตว่ารายการอื่นๆ ในลิสต์เลื่อนลงเพื่อตอบสนองต่อรายการที่ขยายขนาดขึ้น) |
|---|---|