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दिखाता है.
पसंद के मुताबिक बनाए गए सीन में equals और hashCode को लागू करना
पसंद के मुताबिक बनाए गए Scene को लागू करने के लिए, equals और hashCode को सही तरीके से लागू करना ज़रूरी है, ताकि ये काम किए जा सकें:
- सीन की स्थिति में ट्रांज़िशन: सीन की रणनीतियों की सूची से हर बार सीन का हिसाब लगाने पर,
NavDisplayनए सीन और पिछले सीन की समानता की जांच करता है. अगर उसे कोई बदलाव दिखता है, तो वह सीन के बीच ट्रांज़िशन के लिए इस्तेमाल किए गएSeekableTransitionको अपडेट करता है. इससे सीन की लाइफ़साइकल की स्थिति पर असर पड़ता है. - ओवरले मैनेजमेंट: डायलॉग जैसे
OverlayScenes के लिए,NavDisplayओवरले की लाइफ़साइकल और एक्ज़िट ऐनिमेशन को ट्रैक करने के लिए, सीन ऑब्जेक्ट कोkeyके तौर पर इस्तेमाल करता है.
इस बात की पुष्टि करें कि सीन के कॉन्टेंट को तय करने वाली सभी प्रॉपर्टी, जैसे कि key, entries, और previousEntries, equals और hashCode को लागू करने के दौरान शामिल की गई हों. आम तौर पर, इन तरीकों को लागू करने के दौरान, कॉलबैक (जैसे कि onBack) शामिल न करें, क्योंकि इनसे लॉजिकल सीन की स्थिति में बदलाव किए बिना, इंस्टेंस में बदलाव किया जा सकता है.
सीन की रणनीतियों के बारे में जानकारी
एक 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>
बना सकता है, तो उसे लौटाना चाहिए. अगर ऐसा नहीं हो सकता, तो null लौटाना चाहिए.
SceneStrategyScope की ज़िम्मेदारी, उन सभी ज़रूरी नहीं वाले आर्ग्युमेंट
को बनाए रखना है जिनकी ज़रूरत SceneStrategy को हो सकती है. जैसे, onBack कॉलबैक.
सीन और सीन की रणनीतियां एक साथ कैसे काम करती हैं
NavDisplay एक ऐसा कंपोज़ेबल है जो आपके बैक स्टैक को मॉनिटर करता है. साथ ही, सही Scene तय करने और उसे रेंडर करने के लिए, एक या उससे ज़्यादा SceneStrategy का इस्तेमाल करता है.
NavDisplay का sceneStrategies पैरामीटर, SceneStrategy
इंस्टेंस की एक सूची की उम्मीद करता है. इन इंस्टेंस की ज़िम्मेदारी, दिखाने के लिए Scene का हिसाब लगाना होती है. अगर दी गई रणनीतियों से कोई
Scene नहीं बनता है, तो NavDisplay डिफ़ॉल्ट रूप से SinglePaneSceneStrategy का इस्तेमाल करता है.
यहां इंटरैक्शन की जानकारी दी गई है:
- जब अपने बैक स्टैक में कुंजियां जोड़ी या हटाई जाती हैं (उदाहरण के लिए,
backStack.add()याbackStack.removeLastOrNull()का इस्तेमाल करके), तोNavDisplayइन बदलावों को मॉनिटर करता है. NavDisplayमौजूदा सूची कोNavEntrys (बैक स्टैक की कुंजियों से ली गई) को कॉन्फ़िगर किए गएsceneStrategiesको क्रम से पास करता है. साथ ही, हर सूची परcalculateSceneको तब तक कॉल करता है, जब तक कोईSceneनहीं मिल जाता.- जब कोई
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. * */ data 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") } } ) } } } }