शेयर किए गए एलिमेंट ट्रांज़िशन की मदद से, कंपोज़ेबल के बीच आसानी से ट्रांज़िशन किया जा सकता है जिनमें एक जैसा कॉन्टेंट हो. अक्सर इनका इस्तेमाल इन चीज़ों के लिए किया जाता है की मदद से, एक उपयोगकर्ता के तौर पर अलग-अलग स्क्रीन को विज़ुअल तौर पर कनेक्ट किया जा सकता है नेविगेट करता है.
उदाहरण के लिए, नीचे दिए गए वीडियो में, स्नैक को लिस्टिंग पेज से लेकर ज़्यादा जानकारी वाले पेज तक शेयर किया जाता है.
Compose में कुछ हाई लेवल एपीआई हैं जिनकी मदद से, शेयर किया जा सकता है एलिमेंट:
SharedTransitionLayout
: शेयर किए गए एक्सटेंशन को लागू करने के लिए, सबसे बाहरी लेआउट ज़रूरी है एलिमेंट ट्रांज़िशन के बारे में ज़्यादा जानें. इससेSharedTransitionScope
मिलता है. कंपोज़ेबल की ज़रूरत शेयर किए गए एलिमेंट मॉडिफ़ायर का इस्तेमाल करने के लिए,SharedTransitionScope
में होना ज़रूरी है.Modifier.sharedElement()
: वह मॉडिफ़ायर जोSharedTransitionScope
कंपोज़ेबल जो किसी अन्य कंपोज़ेबल से मेल खाना चाहिए कंपोज़ेबल.Modifier.sharedBounds()
: वह मॉडिफ़ायर जोSharedTransitionScope
कि इस कंपोज़ेबल के बाउंड का इस्तेमाल, कंटेनर बाउंड है, जहां ट्रांज़िशन होने चाहिए. इसके उलटsharedElement()
,sharedBounds()
को अलग-अलग विज़ुअल के लिए डिज़ाइन किया गया है कॉन्टेंट.
Compose में शेयर किए गए एलिमेंट बनाते समय एक अहम बात यह है कि वे कैसे काम करते हैं वीडियो क्लिप बनाने की सुविधा जोड़ी गई है. इन क्लिप पर एक नज़र डालें और ओवरले सेक्शन देखें.
बुनियादी इस्तेमाल
इस सेक्शन में, नीचे दिया गया ट्रांज़िशन बनाया जाएगा. यह ट्रांज़िशन छोटी "सूची" आइटम, बड़े विवरण वाले आइटम में:
![](https://developer.android.google.cn/static/develop/ui/compose/images/animations/shared-element/basic_shared_element_jetsnack.gif?hl=hi)
Modifier.sharedElement()
का इस्तेमाल करने का सबसे अच्छा तरीका यह है कि
AnimatedContent
, AnimatedVisibility
या NavHost
, क्योंकि यह मैनेज करता है
आपके लिए, कंपोज़ेबल के बीच अपने-आप ट्रांज़िशन हो.
शुरुआत की जगह एक मौजूदा बेसिक AnimatedContent
है, जिसमें
शेयर किए गए एलिमेंट जोड़ने से पहले MainContent
और DetailsContent
कंपोज़ेबल:
![](https://developer.android.google.cn/static/develop/ui/compose/images/animations/shared-element/basic_no_animation_jetsnack.gif?hl=hi)
AnimatedContent
शुरू हो रहा है.शेयर किए गए एलिमेंट को दो लेआउट के बीच ऐनिमेट करने के लिए,
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 ) } } }
Modifier.sharedElement()
को मेल खाने वाले दो कंपोज़ेबल. कोई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
के लिए क्वेरी करें.
जिसके कारण नीचे दिए गए अपने आप चलने वाले ऐनिमेशन होते हैं:
![](https://developer.android.google.cn/static/develop/ui/compose/images/animations/shared-element/basic_shared_element_jetsnack.gif?hl=hi)
शायद आपको दिखे कि पूरे कंटेनर के बैकग्राउंड का रंग और साइज़ अब भी दिख रहा है
AnimatedContent
की डिफ़ॉल्ट सेटिंग का इस्तेमाल करता है.
शेयर की गई सीमाएं बनाम शेयर किए गए एलिमेंट
Modifier.sharedBounds()
, Modifier.sharedElement()
से मिलता-जुलता है.
हालांकि, मॉडिफ़ायर इस तरह से अलग-अलग होते हैं:
sharedBounds()
ऐसे कॉन्टेंट के लिए है जो दिखने में अलग है, लेकिन शेयर होना चाहिए राज्यों के बीच समान क्षेत्र है, जबकिsharedElement()
के हिसाब से कॉन्टेंट में एक जैसा रहेगा.sharedBounds()
इस्तेमाल करने पर, स्क्रीन पर दिखने वाला कॉन्टेंट किस तरह दिखेगा दो स्थितियों के बीच संक्रमण के दौरान दृश्यमान होता है, जबकि बदलाव के दौरान सिर्फ़sharedElement()
टारगेट कॉन्टेंट रेंडर किया गया है सीमाओं.Modifier.sharedBounds()
में इसके लिएenter
औरexit
पैरामीटर हैं कॉन्टेंट को ट्रांसफ़र करने का तरीका तय करते हुए,AnimatedContent
काम करता है.sharedBounds()
के लिए इस्तेमाल किया जाने वाला सबसे आम उदाहरण कंटेनर ट्रांसफ़ॉर्म है पैटर्न, जबकिsharedElement()
के लिए उदाहरण उपयोग का उदाहरण हीरो ट्रांज़िशन है.Text
कंपोज़ेबल का इस्तेमाल करते समय, फ़ॉन्ट के साथ काम करने के लिएsharedBounds()
को प्राथमिकता दी जाती है इटैलिक और बोल्ड या रंग के बीच ट्रांज़िशन जैसे बदलाव.
पिछले उदाहरण से, Row
में Modifier.sharedBounds()
जोड़कर और
दो अलग-अलग स्थितियों में Column
हमें उन सीमाओं को शेयर करने की अनुमति देगा
दूसरे चरण और ट्रांज़िशन ऐनिमेशन का इस्तेमाल करके, उन्हें
एक-दूसरे के बीच में:
@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() ) // ... ) { // ... } } }
दायरों को समझें
Modifier.sharedElement()
का इस्तेमाल करने के लिए, कंपोज़ेबल को
SharedTransitionScope
. SharedTransitionLayout
कंपोज़ेबल,
SharedTransitionScope
. अपने
यूज़र इंटरफ़ेस (यूआई) हैरारकी, जिसमें ऐसे एलिमेंट शामिल हैं जिन्हें आपको शेयर करना है.
आम तौर पर, कंपोज़ेबल को किसी
AnimatedVisibilityScope
. आम तौर पर, यह जानकारी AnimatedContent
का इस्तेमाल करके दी जाती है
कंपोज़ेबल के बीच स्विच करने के लिए या AnimatedVisibility
का इस्तेमाल करते समय
NavHost
कंपोज़ेबल फ़ंक्शन, जब तक कि प्रॉडक्ट के दिखने की सेटिंग को मैनेज नहीं किया जाता
मैन्युअल तरीके से. ऑर्डर में शामिल है
कई स्कोप का इस्तेमाल करने के लिए, अपने ज़रूरी दायरे को
ComposeLocal, 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
के साथ भी काम करते हैं.
उदाहरण के लिए, इस लेज़ी ग्रिड उदाहरण में हर एलिमेंट को रैप किया गया है
AnimatedVisibility
. जब किसी आइटम पर क्लिक किया जाता है - तो कॉन्टेंट में
यूज़र इंटरफ़ेस (यूआई) से बाहर निकाले गए, एक डायलॉग जैसे कॉम्पोनेंट में दिखने का विज़ुअल इफ़ेक्ट.
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( state = rememberSharedContentState(key = snack.name), animatedVisibilityScope = this@AnimatedVisibility ), onClick = { selectedSnack = snack } ) } } } } // Contains matching AnimatedContent with sharedBounds modifiers. SnackEditDetails( snack = selectedSnack, onConfirmClick = { selectedSnack = null } ) }
AnimatedVisibility
के साथ शेयर किए गए एलिमेंट.संशोधक ऑर्डरिंग
Modifier.sharedElement()
और Modifier.sharedBounds()
के साथ आपके
मॉडिफ़ायर चेन मायने रखती है,
यह Compose की बाकी सुविधाओं की तरह है. साइज़ पर असर डालने वाले मॉडिफ़ायर का गलत प्लेसमेंट
शेयर किए गए एलिमेंट मैचिंग के दौरान, अनचाहे विज़ुअल जंपिंग की वजह बन सकती है.
उदाहरण के लिए, अगर पैडिंग मॉडिफ़ायर को दो हिस्सों में किसी अलग पोज़िशन पर रखा जाता है शेयर किए जाने वाले एलिमेंट के साथ, ऐनिमेशन में एक विज़ुअल अंतर है.
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 = ScaleToBounds()
एनिमेशन या कंपोज़ेबल में Modifier.skipToLookaheadSize()
. इसमें
केस, कंपोज़, टारगेट कंस्ट्रेंट का इस्तेमाल करके बच्चे के लिए लेआउट बनाता है. इसके बजाय,
लेआउट का साइज़ बदलने के बजाय ऐनिमेशन करने के लिए एक स्केल फ़ैक्टर
वह भी ऐसा कर सकता है.
खास कुंजियां
जटिल शेयर किए गए एलिमेंट के साथ काम करते समय, कुंजी बनाना अच्छा होता है यह एक स्ट्रिंग नहीं है, क्योंकि स्ट्रिंग के मेल खाने में गड़बड़ी हो सकती है. हर कुंजी के लिए ज़रूरी है कि मिलान होने के लिए अद्वितीय होना चाहिए. उदाहरण के लिए, JetSnap में हमारे पास ये चीज़ें हैं शेयर किए गए एलिमेंट:
![](https://developer.android.google.cn/static/develop/ui/compose/images/animations/shared-element/unique_keys_shared_elements.jpeg?hl=hi)
शेयर किए गए एलिमेंट टाइप को दिखाने के लिए, एक 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()
और
isEquals()
.
शेयर किए गए एलिमेंट को दिखाने की सुविधा को मैन्युअल तरीके से मैनेज करें
उन मामलों में जहां हो सकता है कि आप 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) } }
मौजूदा सीमाएं
इन एपीआई की कुछ सीमाएं होती हैं. सबसे ज़्यादा ध्यान देने वाली बातें:
- व्यू और कंपोज़ की सुविधा के बीच इंटरऑपरेबिलिटी (दूसरे सिस्टम के साथ काम करने की सुविधा) काम नहीं करती. इसमें ये शामिल हैं
कोई भी कंपोज़ेबल जिसमें
AndroidView
शामिल हो, जैसे किDialog
. - इनके लिए कोई स्वचालित ऐनिमेशन समर्थन नहीं है:
- शेयर की गई इमेज कंपोज़ेबल:
ContentScale
डिफ़ॉल्ट रूप से ऐनिमेट नहीं होता है. यह तय किए गए समय पर सही तरह से काम करता हैContentScale
.
- आकार की क्लिपिंग - ऑटोमैटिक करने की सुविधा पहले से मौजूद नहीं है आकृतियों के बीच ऐनिमेशन - उदाहरण के लिए, स्क्वेयर से गोले को आइटम ट्रांज़िशन के तौर पर लिखें.
- काम न करने वाले मामलों में, इसके बजाय
Modifier.sharedBounds()
का इस्तेमाल करेंsharedElement()
और आइटम मेंModifier.animateEnterExit()
जोड़ें.
- शेयर की गई इमेज कंपोज़ेबल: