Navigation 3, सीन के ज़रिए आपके ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) फ़्लो को मैनेज करने के लिए, एक दमदार और फ़्लेक्सिबल सिस्टम उपलब्ध कराता है. सीन की मदद से, पूरी तरह से पसंद के मुताबिक बनाए गए लेआउट बनाए जा सकते हैं. साथ ही, अलग-अलग स्क्रीन साइज़ के हिसाब से लेआउट को अडजस्ट किया जा सकता है. इसके अलावा, एक साथ कई पैनल वाले जटिल अनुभव को आसानी से मैनेज किया जा सकता है.
सीन के बारे में जानकारी
Navigation 3 में, एक Scene एक बुनियादी यूनिट है. यह एक या उससे ज़्यादा
NavEntry इंस्टेंस रेंडर करती है. Scene को अपने यूज़र इंटरफ़ेस (यूआई) की एक अलग विज़ुअल स्थिति या सेक्शन के तौर पर समझें. इसमें पिछली गतिविधियों से कॉन्टेंट के डिसप्ले को शामिल और मैनेज किया जा सकता है.
हर Scene इंस्टेंस की पहचान, उसके key और
Scene की क्लास से होती है. यह यूनीक आइडेंटिफ़ायर ज़रूरी है, क्योंकि यह
टॉप-लेवल ऐनिमेशन को चलाता है, जब Scene में बदलाव होता है.
Scene इंटरफ़ेस में ये प्रॉपर्टी होती हैं:
key: Any: यहSceneके इस खास इंस्टेंस के लिए यूनीक आइडेंटिफ़ायर है. इस कुंजी कोSceneकी क्लास के साथ मिलाकर, अलग-अलग सीन के बीच अंतर किया जाता है. ऐसा मुख्य तौर पर ऐनिमेशन के लिए किया जाता है.entries: List<NavEntry<T>>: यहNavEntryऑब्जेक्ट की सूची है. इसे दिखाने की ज़िम्मेदारीSceneकी होती है. अहम जानकारी, अगर ट्रांज़िशन के दौरान एक हीNavEntryको कईScenesमें दिखाया जाता है (उदाहरण के लिए, शेयर किए गए एलिमेंट के ट्रांज़िशन में), तो उसका कॉन्टेंट सिर्फ़ सबसे हाल ही के टारगेटSceneसे रेंडर होगा.previousEntries: List<NavEntry<T>>: यह प्रॉपर्टी उनNavEntryको तय करती है जो मौजूदाSceneसे "पीछे जाएं" ऐक्शन होने पर दिखेंगी. पीछे जाने पर झलक दिखाने वाले हाथ के जेस्चर की सही स्थिति का पता लगाने के लिए यह ज़रूरी है. इससेNavDisplayको पिछली सही स्थिति का अनुमान लगाने और उसमें ट्रांज़िशन करने में मदद मिलती है. यह स्थिति, अलग क्लास और/या कुंजी वाला सीन हो सकती है.content: @Composable () -> Unit: यह कंपोज़ेबल फ़ंक्शन है. इसमें यह तय किया जाता है किSceneअपनेentriesऔर उसSceneके लिए खास तौर पर बनाए गए आस-पास के यूज़र इंटरफ़ेस (यूआई) एलिमेंट को कैसे रेंडर करता है.metadata: Map<String, Any>: यह अन्य लाइब्रेरी कॉम्पोनेंट को सीन के बारे में खास जानकारी देता है, जैसेNavDisplay. डिफ़ॉल्ट रूप से, यहentriesमें मौजूद आखिरीNavEntryकाmetadataदिखाता है.
सीन की रणनीतियों के बारे में जानकारी
एक SceneStrategy एक ऐसा तरीका है जिससे यह तय किया जाता है कि पिछली गतिविधियों से
NavEntry की दी गई सूची को कैसे व्यवस्थित किया जाए और उसे
Scene में कैसे ट्रांज़िशन किया जाए. असल में, मौजूदा बैक स्टैक एंट्री दिखाने पर, SceneStrategy खुद से ये दो अहम सवाल पूछता है:
- क्या मैं इन एंट्री से कोई
Sceneबना सकता/सकती हूं? अगरSceneStrategyको लगता है कि वह दी गईNavEntryको हैंडल कर सकता है और काम काSceneबना सकता है (उदाहरण के लिए, कोई डायलॉग या एक साथ कई पैनल वाला लेआउट), तो वह आगे बढ़ता है. वरना, वहnullदिखाता है. इससे अन्य रणनीतियों कोSceneबनाने का मौका मिलता है. - अगर हां, तो मुझे उन एंट्री को
Scene?में कैसे व्यवस्थित करना चाहिए? जब कोईSceneStrategyएंट्री को हैंडल करने का फ़ैसला करता है, तो वह बनाने और यह तय करने की ज़िम्मेदारी लेता है कि तय की गईNavEntryको उसSceneमें कैसे दिखाया जाएगा.Scene
का मुख्य हिस्सा, उसका तरीका है:SceneStrategycalculateScene
@Composable public fun calculateScene( entries: List<NavEntry<T>>, onBack: (count: Int) -> Unit, ): Scene<T>?
यह तरीका, SceneStrategyScope पर एक एक्सटेंशन फ़ंक्शन है. यह बैक स्टैक से
मौजूदा List<NavEntry<T>> लेता है. अगर यह दी गई एंट्री से कोई Scene<T>
बना सकता है, तो उसे Scene<T>
दिखाना चाहिए. अगर यह ऐसा नहीं कर सकता, तो null दिखाना चाहिए.
SceneStrategyScope उन सभी वैकल्पिक आर्ग्युमेंट
को बनाए रखने के लिए ज़िम्मेदार है जिनकी ज़रूरत SceneStrategy को हो सकती है. जैसे, onBack कॉलबैक.
सीन और सीन की रणनीतियां एक साथ कैसे काम करती हैं
NavDisplay एक ऐसा कंपोज़ेबल है जो आपकी पिछली गतिविधियों पर नज़र रखता है. साथ ही, सही Scene का पता लगाने और उसे रेंडर करने के लिए, एक या उससे ज़्यादा SceneStrategy का इस्तेमाल करता है.
NavDisplay का sceneStrategies पैरामीटर, SceneStrategy
इंस्टेंस की सूची लेता है. ये इंस्टेंस, दिखाए जाने वाले Scene का पता लगाने के लिए ज़िम्मेदार होते हैं. अगर दी गई रणनीतियों से कोई
Scene नहीं मिलता है, तो NavDisplay डिफ़ॉल्ट रूप से
का इस्तेमाल करता है.SinglePaneSceneStrategy
यहां इंटरैक्शन की जानकारी दी गई है:
- जब आप अपनी पिछली गतिविधियों में कुंजियां जोड़ते या हटाते हैं (उदाहरण के लिए,
backStack.add()याbackStack.removeLastOrNull()का इस्तेमाल करके), तोNavDisplayइन बदलावों पर नज़र रखता है. NavDisplayमौजूदा सूची को कॉन्फ़िगर किए गएsceneStrategiesको क्रम से पास करता है. साथ ही, हर सूची परcalculateSceneको तब तक कॉल करता है, जब तक कोईSceneनहीं मिल जाता.NavEntry- जब कोई
SceneStrategy,Sceneको सफलतापूर्वक दिखाता है, तोNavDisplayफिर उसSceneकाcontentरेंडर करता है.NavDisplay,Sceneकी प्रॉपर्टी के आधार पर ऐनिमेशन और पीछे जाने पर झलक दिखाने वाला हाथ का जेस्चर को भी मैनेज करता है.
उदाहरण: एक पैनल वाला लेआउट (डिफ़ॉल्ट व्यवहार)
पसंद के मुताबिक बनाया गया सबसे आसान लेआउट, एक पैनल वाला डिसप्ले हो सकता है. अगर कोई अन्य SceneStrategy प्राथमिकता नहीं लेता है, तो यह डिफ़ॉल्ट व्यवहार होता है.
data class SinglePaneScene<T : Any>( override val key: Any, val entry: NavEntry<T>, override val previousEntries: List<NavEntry<T>>, ) : Scene<T> { override val entries: List<NavEntry<T>> = listOf(entry) override val content: @Composable () -> Unit = { entry.Content() } } /** * A [SceneStrategy] that always creates a 1-entry [Scene] simply displaying the last entry in the * list. */ public class SinglePaneSceneStrategy<T : Any> : SceneStrategy<T> { override fun SceneStrategyScope<T>.calculateScene(entries: List<NavEntry<T>>): Scene<T>? = SinglePaneScene( key = entries.last().contentKey, entry = entries.last(), previousEntries = entries.dropLast(1) ) }
उदाहरण: सूची की जानकारी वाला बुनियादी लेआउट (पसंद के मुताबिक बनाया गया सीन और रणनीति)
इस उदाहरण में, सूची की जानकारी वाला सामान्य लेआउट बनाने का तरीका बताया गया है. यह लेआउट, इन दो शर्तों के आधार पर चालू होता है:
- विंडो की चौड़ाई इतनी होनी चाहिए कि दो पैनल दिखाए जा सकें. इसका मतलब है कि यह कम से कम
WIDTH_DP_MEDIUM_LOWER_BOUNDहोनी चाहिए. - पिछली गतिविधियों में ऐसी एंट्री होनी चाहिए जिन्होंने खास मेटाडेटा का इस्तेमाल करके, सूची की जानकारी वाले लेआउट में दिखाए जाने की अनुमति दी हो.
यहां दिया गया स्निपेट, ListDetailScene.kt का सोर्स कोड है. इसमें
ListDetailScene और ListDetailSceneStrategy, दोनों शामिल हैं:
// --- ListDetailScene --- /** * A [Scene] that displays a list and a detail [NavEntry] side-by-side in a 40/60 split. * */ class ListDetailScene<T : Any>( override val key: Any, override val previousEntries: List<NavEntry<T>>, val listEntry: NavEntry<T>, val detailEntry: NavEntry<T>, ) : Scene<T> { override val entries: List<NavEntry<T>> = listOf(listEntry, detailEntry) override val content: @Composable (() -> Unit) = { Row(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.weight(0.4f)) { listEntry.Content() } Column(modifier = Modifier.weight(0.6f)) { detailEntry.Content() } } } } @Composable fun <T : Any> rememberListDetailSceneStrategy(): ListDetailSceneStrategy<T> { val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass return remember(windowSizeClass) { ListDetailSceneStrategy(windowSizeClass) } } // --- ListDetailSceneStrategy --- /** * A [SceneStrategy] that returns a [ListDetailScene] if the window is wide enough, the last item * is the backstack is a detail, and before it, at any point in the backstack is a list. */ class ListDetailSceneStrategy<T : Any>(val windowSizeClass: WindowSizeClass) : SceneStrategy<T> { override fun SceneStrategyScope<T>.calculateScene(entries: List<NavEntry<T>>): Scene<T>? { if (!windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND)) { return null } val detailEntry = entries.lastOrNull()?.takeIf { it.metadata.contains(DetailKey) } ?: return null val listEntry = entries.findLast { it.metadata.contains(ListKey) } ?: return null // We use the list's contentKey to uniquely identify the scene. // This allows the detail panes to be displayed instantly through recomposition, rather than // having NavDisplay animate the whole scene out when the selected detail item changes. val sceneKey = listEntry.contentKey return ListDetailScene( key = sceneKey, previousEntries = entries.dropLast(1), listEntry = listEntry, detailEntry = detailEntry ) } object ListKey : NavMetadataKey<Boolean> object DetailKey : NavMetadataKey<Boolean> companion object { /** * Helper function to add metadata to a [NavEntry] indicating it can be displayed * as a list in the [ListDetailScene]. */ fun listPane() = metadata { put(ListKey, true) } /** * Helper function to add metadata to a [NavEntry] indicating it can be displayed * as a list in the [ListDetailScene]. */ fun detailPane() = metadata { put(DetailKey, true) } } }
अपने NavDisplay में इस ListDetailSceneStrategy का इस्तेमाल करने के लिए, अपने entryProvider कॉल में बदलाव करें. साथ ही, जिस एंट्री को सूची लेआउट के तौर पर दिखाना है उसके लिए ListDetailScene.listPane() मेटाडेटा और जिस एंट्री को जानकारी लेआउट के तौर पर दिखाना है उसके लिए ListDetailScene.detailPane() मेटाडेटा शामिल करें. इसके बाद, ListDetailSceneStrategy() को अपनी sceneStrategy के तौर पर इस्तेमाल करें. साथ ही, एक पैनल वाले सीन के लिए डिफ़ॉल्ट फ़ॉलबैक पर भरोसा करें:
// Define your navigation keys @Serializable data object ConversationList : NavKey @Serializable data class ConversationDetail(val id: String) : NavKey @Composable fun MyAppContent() { val backStack = rememberNavBackStack(ConversationList) val listDetailStrategy = rememberListDetailSceneStrategy<NavKey>() NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, sceneStrategies = listOf(listDetailStrategy), entryProvider = entryProvider { entry<ConversationList>( metadata = ListDetailSceneStrategy.listPane() ) { Column(modifier = Modifier.fillMaxSize()) { Text(text = "I'm a Conversation List") Button(onClick = { backStack.addDetail(ConversationDetail("123")) }) { Text(text = "Open detail") } } } entry<ConversationDetail>( metadata = ListDetailSceneStrategy.detailPane() ) { Text(text = "I'm a Conversation Detail") } } ) } private fun NavBackStack<NavKey>.addDetail(detailRoute: ConversationDetail) { // Remove any existing detail routes, then add the new detail route removeIf { it is ConversationDetail } add(detailRoute) }
अगर आपको सूची की जानकारी वाला अपना सीन नहीं बनाना है, तो मटीरियल सूची की जानकारी वाला सीन इस्तेमाल किया जा सकता है. इसमें काम की जानकारी और प्लेसहोल्डर की सुविधा मिलती है. इसके बारे में अगले सेक्शन में बताया गया है.
मटीरियल अडैप्टिव सीन में सूची-जानकारी वाला कॉन्टेंट दिखाना
सूची की जानकारी वाले इस्तेमाल के उदाहरण के लिए, androidx.compose.material3.adaptive:adaptive-navigation3 आर्टफ़ैक्ट ListDetailSceneStrategy उपलब्ध कराता है. इससे सूची की जानकारी वाला Scene बनता है. यह Scene, एक साथ कई पैनल वाले जटिल सीन (सूची, जानकारी, और अतिरिक्त पैनल) को अपने-आप हैंडल करता है. साथ ही, विंडो के साइज़ और डिवाइस की स्थिति के हिसाब से उन्हें अडजस्ट करता है.
मटीरियल सूची की जानकारी वाला Scene बनाने के लिए, यह तरीका अपनाएं:
- डिपेंडेंसी जोड़ें: अपने प्रोजेक्ट की
androidx.compose.material3.adaptive:adaptive-navigation3फ़ाइल में,build.gradle.ktsशामिल करें. ListDetailSceneStrategyमेटाडेटा के साथ अपनी एंट्री तय करें:NavEntrysको सही पैनल में दिखाने के लिए,listPane(), detailPane(), औरextraPane()का इस्तेमाल करें.listPane()हेल्पर की मदद से, कोई आइटम न चुने जाने परdetailPlaceholderभी तय किया जा सकता है.- का इस्तेमाल करें
rememberListDetailSceneStrategy: यह कंपोज़ेबल फ़ंक्शन पहले से कॉन्फ़िगर किया गयाListDetailSceneStrategyउपलब्ध कराता है. इसका इस्तेमालNavDisplayकर सकता है.
यहां दिया गया स्निपेट, ListDetailSceneStrategy के इस्तेमाल को दिखाने वाली एक सैंपल Activity है:
@Serializable object ProductList : NavKey @Serializable data class ProductDetail(val id: String) : NavKey @Serializable data object Profile : NavKey class MaterialListDetailActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3AdaptiveApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Scaffold { paddingValues -> val backStack = rememberNavBackStack(ProductList) val listDetailStrategy = rememberListDetailSceneStrategy<NavKey>() NavDisplay( backStack = backStack, modifier = Modifier.padding(paddingValues), onBack = { backStack.removeLastOrNull() }, sceneStrategies = listOf(listDetailStrategy), entryProvider = entryProvider { entry<ProductList>( metadata = ListDetailSceneStrategy.listPane( detailPlaceholder = { ContentYellow("Choose a product from the list") } ) ) { ContentRed("Welcome to Nav3") { Button(onClick = { backStack.add(ProductDetail("ABC")) }) { Text("View product") } } } entry<ProductDetail>( metadata = ListDetailSceneStrategy.detailPane() ) { product -> ContentBlue("Product ${product.id} ", Modifier.background(PastelBlue)) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { backStack.add(Profile) }) { Text("View profile") } } } } entry<Profile>( metadata = ListDetailSceneStrategy.extraPane() ) { ContentGreen("Profile") } } ) } } } }