स्क्रोल मॉडिफ़ायर
verticalScroll
और horizontalScroll
मॉडिफ़ायर, उपयोगकर्ता को किसी एलिमेंट को स्क्रोल करने की अनुमति देने का सबसे आसान तरीका है. ऐसा तब होता है, जब उसके कॉन्टेंट की सीमाएं, उसके साइज़ की ज़्यादा से ज़्यादा पाबंदियों से ज़्यादा होती हैं. verticalScroll
और horizontalScroll
मॉडिफ़ायर की मदद से, आपको कॉन्टेंट का अनुवाद करने या उसे ऑफ़सेट करने की ज़रूरत नहीं होती.
@Composable private fun ScrollBoxes() { Column( modifier = Modifier .background(Color.LightGray) .size(100.dp) .verticalScroll(rememberScrollState()) ) { repeat(10) { Text("Item $it", modifier = Modifier.padding(2.dp)) } } }
ScrollState
की मदद से, स्क्रोल करने की जगह बदली जा सकती है या उसकी मौजूदा स्थिति पता की जा सकती है. डिफ़ॉल्ट पैरामीटर के साथ इसे बनाने के लिए, rememberScrollState()
का इस्तेमाल करें.
@Composable private fun ScrollBoxesSmooth() { // Smoothly scroll 100px on first composition val state = rememberScrollState() LaunchedEffect(Unit) { state.animateScrollTo(100) } Column( modifier = Modifier .background(Color.LightGray) .size(100.dp) .padding(horizontal = 8.dp) .verticalScroll(state) ) { repeat(10) { Text("Item $it", modifier = Modifier.padding(2.dp)) } } }
स्क्रोल किया जा सकने वाला मॉडिफ़ायर
scrollable
मॉडिफ़ायर, स्क्रोल मॉडिफ़ायर से इस मामले में अलग है कि scrollable
स्क्रोल जेस्चर का पता लगाता है और डेल्टा कैप्चर करता है. हालांकि, यह अपने कॉन्टेंट को अपने-आप ऑफ़सेट नहीं करता है. इसके बजाय, यह ScrollableState
के ज़रिए उपयोगकर्ता को सौंपा जाता है. इस मॉडिफ़ायर के सही तरीके से काम करने के लिए, यह ज़रूरी है.
ScrollableState
बनाते समय, आपको एक consumeScrollDelta
फ़ंक्शन देना होगा. इस फ़ंक्शन को हर स्क्रोल स्टेप (जेस्चर इनपुट, स्मूथ स्क्रोलिंग या फ़्लिंग करके) पर पिक्सल में डेल्टा के साथ लागू किया जाएगा. इस फ़ंक्शन को स्क्रोल की गई दूरी की जानकारी देनी चाहिए, ताकि यह पक्का किया जा सके कि इवेंट को सही तरीके से ट्रिगर किया गया है. ऐसा उन मामलों में ज़रूरी है जहां नेस्ट किए गए ऐसे एलिमेंट मौजूद हों जिनमें scrollable
मॉडिफ़ायर मौजूद हो.
यहां दिए गए स्निपेट में, जेस्चर का पता लगाया जाता है और ऑफ़सेट के लिए संख्या वाली वैल्यू दिखाई जाती है. हालांकि, इससे किसी भी एलिमेंट को ऑफ़सेट नहीं किया जाता:
@Composable private fun ScrollableSample() { // actual composable state var offset by remember { mutableStateOf(0f) } Box( Modifier .size(150.dp) .scrollable( orientation = Orientation.Vertical, // Scrollable state: describes how to consume // scrolling delta and update offset state = rememberScrollableState { delta -> offset += delta delta } ) .background(Color.LightGray), contentAlignment = Alignment.Center ) { Text(offset.toString()) } }
नेस्ट की गई स्क्रोलिंग
नेस्टेड स्क्रोलिंग एक ऐसा सिस्टम है जिसमें एक-दूसरे के अंदर मौजूद कई स्क्रोलिंग कॉम्पोनेंट, एक साथ काम करते हैं. ये कॉम्पोनेंट, स्क्रोल करने के एक ही जेस्चर पर प्रतिक्रिया देते हैं और स्क्रोलिंग के डेल्टा (बदलाव) को कम्यूनिकेट करते हैं.
नेस्टेड स्क्रोलिंग सिस्टम, स्क्रोल किए जा सकने वाले और हैरारकी के हिसाब से लिंक किए गए कॉम्पोनेंट के बीच तालमेल बिठाने की सुविधा देता है. ये कॉम्पोनेंट, अक्सर एक ही पैरंट को शेयर करते हैं. यह सिस्टम, स्क्रोल करने वाले कंटेनर को लिंक करता है. साथ ही, स्क्रोल करने वाले उन डेल्टा के साथ इंटरैक्ट करने की अनुमति देता है जिन्हें इनके बीच में फैलाया और शेयर किया जा रहा है.
Compose, कंपोज़ेबल के बीच नेस्ट की गई स्क्रोलिंग को मैनेज करने के कई तरीके उपलब्ध कराता है. नेस्टेड स्क्रोलिंग का एक सामान्य उदाहरण, किसी सूची के अंदर दूसरी सूची है. वहीं, इसका एक ज़्यादा जटिल उदाहरण कोलैप्स होने वाला टूलबार है.
नेस्ट की गई स्क्रोलिंग की सुविधा अपने-आप चालू होना
सिंपल नेस्टेड स्क्रोलिंग के लिए, आपको कुछ नहीं करना है. स्क्रोल करने की सुविधा शुरू करने वाले जेस्चर, बच्चों से माता-पिता को अपने-आप मिल जाते हैं. ऐसा इसलिए होता है, ताकि जब बच्चा आगे स्क्रोल न कर पाए, तो जेस्चर को उसके पैरंट एलिमेंट से मैनेज किया जा सके.
ऑटोमैटिक नेस्टेड स्क्रोलिंग की सुविधा, Compose के कुछ कॉम्पोनेंट और मॉडिफ़ायर के साथ काम करती है. जैसे:
verticalScroll
,
horizontalScroll
,
scrollable
,
Lazy
एपीआई, और TextField
. इसका मतलब है कि जब उपयोगकर्ता नेस्ट किए गए कॉम्पोनेंट के किसी इनर चाइल्ड को स्क्रोल करता है, तो पिछले मॉडिफ़ायर, स्क्रोलिंग डेल्टा को उन पैरंट कॉम्पोनेंट तक पहुंचाते हैं जिनमें नेस्ट की गई स्क्रोलिंग की सुविधा होती है.
इस उदाहरण में, ऐसे एलिमेंट दिखाए गए हैं जिन पर verticalScroll
मॉडिफ़ायर लागू किया गया है. ये एलिमेंट, ऐसे कंटेनर में मौजूद हैं जिस पर verticalScroll
मॉडिफ़ायर लागू किया गया है.
@Composable private fun AutomaticNestedScroll() { val gradient = Brush.verticalGradient(0f to Color.Gray, 1000f to Color.White) Box( modifier = Modifier .background(Color.LightGray) .verticalScroll(rememberScrollState()) .padding(32.dp) ) { Column { repeat(6) { Box( modifier = Modifier .height(128.dp) .verticalScroll(rememberScrollState()) ) { Text( "Scroll here", modifier = Modifier .border(12.dp, Color.DarkGray) .background(brush = gradient) .padding(24.dp) .height(150.dp) ) } } } } }
nestedScroll
मॉडिफ़ायर का इस्तेमाल करना
अगर आपको कई एलिमेंट के बीच बेहतर तरीके से कोऑर्डिनेट की गई स्क्रोलिंग बनानी है, तो nestedScroll
मॉडिफ़ायर का इस्तेमाल करें. इससे आपको नेस्ट की गई स्क्रोलिंग की हैरारकी तय करने में ज़्यादा आसानी होती है. पिछले सेक्शन में बताया गया है कि कुछ कॉम्पोनेंट में, नेस्ट किए गए स्क्रोल करने की सुविधा पहले से मौजूद होती है. हालांकि, अपने-आप स्क्रोल न होने वाले कंपोज़ेबल के लिए, जैसे कि Box
या Column
, ऐसे कॉम्पोनेंट पर स्क्रोल डेल्टा, नेस्ट किए गए स्क्रोल सिस्टम में नहीं फैलेंगे. साथ ही, डेल्टा NestedScrollConnection
या पैरंट कॉम्पोनेंट तक नहीं पहुंचेंगे. इस समस्या को हल करने के लिए, nestedScroll
का इस्तेमाल करके, कस्टम कॉम्पोनेंट के साथ-साथ अन्य कॉम्पोनेंट को भी यह सुविधा दी जा सकती है.
नेस्ट की गई स्क्रोलिंग साइकल
नेस्टेड स्क्रोल साइकल, स्क्रोल डेल्टा का फ़्लो होता है. इन्हें नेस्टेड स्क्रोलिंग सिस्टम का हिस्सा बनने वाले सभी कॉम्पोनेंट (या नोड) के ज़रिए, हैरारकी ट्री में ऊपर और नीचे भेजा जाता है. उदाहरण के लिए, स्क्रोल किए जा सकने वाले कॉम्पोनेंट और मॉडिफ़ायर या nestedScroll
का इस्तेमाल करके.
नेस्ट की गई स्क्रोलिंग के साइकल के फ़ेज़
जब स्क्रोल किए जा सकने वाले कॉम्पोनेंट को ट्रिगर इवेंट (उदाहरण के लिए, कोई जेस्चर) का पता चलता है, तो स्क्रोल करने की कार्रवाई शुरू होने से पहले ही, जनरेट किए गए डेल्टा को नेस्ट किए गए स्क्रोल सिस्टम को भेज दिया जाता है. इसके बाद, ये तीन चरणों से गुज़रते हैं: स्क्रोल से पहले, नोड का इस्तेमाल, और स्क्रोल के बाद.
पहले, प्री-स्क्रोल फ़ेज़ में, जिस कॉम्पोनेंट को ट्रिगर इवेंट डेल्टा मिले हैं वह उन इवेंट को ऊपर की ओर भेजेगा. ऐसा, हैरारकी ट्री के ज़रिए सबसे ऊपर मौजूद पैरंट तक किया जाएगा. इसके बाद, डेल्टा इवेंट नीचे की ओर जाएंगे. इसका मतलब है कि डेल्टा, सबसे ऊपर मौजूद पैरंट से लेकर उस चाइल्ड तक भेजे जाएंगे जिसने नेस्ट किए गए स्क्रोल साइकल को शुरू किया था.
इससे नेस्ट किए गए स्क्रोल के पैरंट (nestedScroll
या स्क्रोल किए जा सकने वाले मॉडिफ़ायर का इस्तेमाल करने वाले कंपोज़ेबल) को डेल्टा के साथ कुछ करने का मौका मिलता है. ऐसा तब होता है, जब नोड खुद इसका इस्तेमाल कर सकता है.
नोड के इस्तेमाल के चरण में, नोड खुद उस डेल्टा का इस्तेमाल करेगा जिसे उसके पैरंट ने इस्तेमाल नहीं किया था. इस दौरान, स्क्रोलिंग की प्रोसेस पूरी हो जाती है और वह दिखने लगती है.
इस दौरान, बच्चा बची हुई पूरी स्क्रोल या उसके कुछ हिस्से का इस्तेमाल कर सकता है. बची हुई सभी चीज़ों को वापस भेज दिया जाएगा, ताकि वे स्क्रोल करने के बाद दिखने वाले विज्ञापन के चरण से गुज़र सकें.
आखिर में, स्क्रोल करने के बाद वाले फ़ेज़ में, नोड ने जिस भी कॉन्टेंट का इस्तेमाल नहीं किया है उसे उसके पूर्वजों को भेज दिया जाएगा, ताकि वे उसका इस्तेमाल कर सकें.
पोस्ट-स्क्रोल फ़ेज़, प्री-स्क्रोल फ़ेज़ की तरह ही काम करता है. इसमें कोई भी पैरंट, कॉन्टेंट को देख सकता है या नहीं भी देख सकता है.
स्क्रोल करने की तरह ही, खींचने और छोड़ने का जेस्चर खत्म होने पर, उपयोगकर्ता के इरादे को एक वेलोसिटी में बदला जा सकता है. इसका इस्तेमाल, स्क्रोल किए जा सकने वाले कंटेनर को फ़्लिंग करने (ऐनिमेशन का इस्तेमाल करके स्क्रोल करना) के लिए किया जाता है. फ़्लिंग, नेस्ट किए गए स्क्रोल साइकल का भी हिस्सा है. साथ ही, खींचने वाले इवेंट से जनरेट हुई वेलोसिटी, एक जैसी फ़ेज़ से गुज़रती है: प्री-फ़्लिंग, नोड कंजम्पशन, और पोस्ट-फ़्लिंग. ध्यान दें कि फ़्लिंग ऐनिमेशन सिर्फ़ टच जेस्चर से जुड़ा होता है. यह a11y या हार्डवेयर स्क्रोल जैसे अन्य इवेंट से ट्रिगर नहीं होगा.
नेस्टेड स्क्रोलिंग साइकल में हिस्सा लेना
साइकल में हिस्सा लेने का मतलब है, हैरारकी के हिसाब से डेल्टा को इंटरसेप्ट करना, इस्तेमाल करना, और इस्तेमाल करने की रिपोर्ट करना. Compose, नेस्ट किए गए स्क्रोलिंग सिस्टम के काम करने के तरीके और उससे सीधे तौर पर इंटरैक्ट करने के तरीके को बेहतर बनाने के लिए टूल का एक सेट उपलब्ध कराता है. उदाहरण के लिए, जब आपको स्क्रोल किए जा सकने वाले कॉम्पोनेंट के स्क्रोल करना शुरू करने से पहले, स्क्रोल डेल्टा के साथ कुछ करना हो.
अगर नेस्ट किए गए स्क्रोल का साइकल, नोड की चेन पर काम करने वाला सिस्टम है, तो nestedScroll
मॉडिफ़ायर, इन बदलावों को रोकने और उनमें बदलाव करने का एक तरीका है. साथ ही, यह चेन में भेजे गए डेटा (स्क्रोल डेल्टा) को भी प्रभावित करता है. इस मॉडिफ़ायर को क्रम में कहीं भी रखा जा सकता है. यह ट्री में मौजूद नेस्ट किए गए स्क्रोल मॉडिफ़ायर इंस्टेंस के साथ कम्यूनिकेट करता है, ताकि इस चैनल के ज़रिए जानकारी शेयर की जा सके. इस मॉडिफ़ायर के बिल्डिंग ब्लॉक NestedScrollConnection
और NestedScrollDispatcher
हैं.
NestedScrollConnection
नेस्ट किए गए स्क्रोल साइकल के फ़ेज़ के हिसाब से जवाब देने का तरीका बताता है. साथ ही, नेस्ट किए गए स्क्रोल सिस्टम को प्रभावित करता है. इसमें चार कॉलबैक तरीके होते हैं. हर तरीका, खपत के एक चरण को दिखाता है: स्क्रोल करने से पहले/बाद में और फ़्लिंग करने से पहले/बाद में:
val nestedScrollConnection = object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { println("Received onPreScroll callback.") return Offset.Zero } override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource ): Offset { println("Received onPostScroll callback.") return Offset.Zero } }
हर कॉलबैक में, डेल्टा के बारे में भी जानकारी मिलती है:
available
उस फ़ेज़ के लिए डेल्टा और consumed
पिछले फ़ेज़ में इस्तेमाल किया गया डेल्टा. अगर आपको किसी भी समय, डेल्टा को हाइरार्की में ऊपर की ओर ले जाने से रोकना है, तो इसके लिए नेस्ट किए गए स्क्रोल कनेक्शन का इस्तेमाल करें:
val disabledNestedScrollConnection = remember { object : NestedScrollConnection { override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource ): Offset { return if (source == NestedScrollSource.SideEffect) { available } else { Offset.Zero } } } }
सभी कॉलबैक, NestedScrollSource
टाइप के बारे में जानकारी देते हैं.
NestedScrollDispatcher
नेस्ट किए गए स्क्रोल साइकल को शुरू करता है. डिसपैचर का इस्तेमाल करने और उसके तरीकों को कॉल करने से, साइकल ट्रिगर हो जाती है. स्क्रोल किए जा सकने वाले कंटेनर में, पहले से मौजूद एक डिस्पैचर होता है. यह डिस्पैचर, जेस्चर के दौरान कैप्चर किए गए डेल्टा को सिस्टम में भेजता है. इस वजह से, नेस्टेड स्क्रोलिंग को पसंद के मुताबिक बनाने के ज़्यादातर इस्तेमाल के मामलों में, नए डेल्टा भेजने के बजाय, पहले से मौजूद डेल्टा पर प्रतिक्रिया देने के लिए, डिस्पैचर के बजाय NestedScrollConnection
का इस्तेमाल किया जाता है.
इन्हें इस्तेमाल करने के अन्य तरीकों के बारे में जानने के लिए, NestedScrollDispatcherSample
पर जाएं.
स्क्रोल करने पर इमेज का साइज़ बदलना
उपयोगकर्ता के स्क्रोल करने पर, डाइनैमिक विज़ुअल इफ़ेक्ट बनाया जा सकता है. इसमें स्क्रोल की पोज़िशन के हिसाब से इमेज का साइज़ बदलता है.
स्क्रोल की पोज़िशन के आधार पर किसी इमेज का साइज़ बदलना
इस स्निपेट में, वर्टिकल स्क्रोल की पोज़िशन के आधार पर, LazyColumn
में मौजूद इमेज का साइज़ बदलने का तरीका दिखाया गया है. उपयोगकर्ता के नीचे की ओर स्क्रोल करने पर इमेज छोटी हो जाती है और ऊपर की ओर स्क्रोल करने पर बड़ी हो जाती है. हालांकि, इमेज का साइज़ तय की गई कम से कम और ज़्यादा से ज़्यादा सीमा के अंदर ही रहता है:
@Composable fun ImageResizeOnScrollExample( modifier: Modifier = Modifier, maxImageSize: Dp = 300.dp, minImageSize: Dp = 100.dp ) { var currentImageSize by remember { mutableStateOf(maxImageSize) } var imageScale by remember { mutableFloatStateOf(1f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { // Calculate the change in image size based on scroll delta val delta = available.y val newImageSize = currentImageSize + delta.dp val previousImageSize = currentImageSize // Constrain the image size within the allowed bounds currentImageSize = newImageSize.coerceIn(minImageSize, maxImageSize) val consumed = currentImageSize - previousImageSize // Calculate the scale for the image imageScale = currentImageSize / maxImageSize // Return the consumed scroll amount return Offset(0f, consumed.value) } } } Box(Modifier.nestedScroll(nestedScrollConnection)) { LazyColumn( Modifier .fillMaxWidth() .padding(15.dp) .offset { IntOffset(0, currentImageSize.roundToPx()) } ) { // Placeholder list items items(100, key = { it }) { Text( text = "Item: $it", style = MaterialTheme.typography.bodyLarge ) } } Image( painter = ColorPainter(Color.Red), contentDescription = "Red color image", Modifier .size(maxImageSize) .align(Alignment.TopCenter) .graphicsLayer { scaleX = imageScale scaleY = imageScale // Center the image vertically as it scales translationY = -(maxImageSize.toPx() - currentImageSize.toPx()) / 2f } ) } }
कोड के बारे में अहम जानकारी
- यह कोड, स्क्रोल इवेंट को इंटरसेप्ट करने के लिए
NestedScrollConnection
का इस्तेमाल करता है. onPreScroll
, स्क्रोल डेल्टा के आधार पर इमेज के साइज़ में होने वाले बदलाव का हिसाब लगाता है.currentImageSize
स्टेट वैरिएबल, इमेज के मौजूदा साइज़ को सेव करता है. यहminImageSize
औरmaxImageSize. imageScale
के बीच सीमित होता है. यहcurrentImageSize
से मिलता है.LazyColumn
,currentImageSize
के आधार पर ऑफ़सेट करता है.Image
, कैलकुलेट किए गए स्केल को लागू करने के लिए,graphicsLayer
मॉडिफ़ायर का इस्तेमाल करता है.graphicsLayer
में मौजूदtranslationY
यह पक्का करता है कि इमेज का साइज़ बदलते समय, वह लंबवत रूप से बीच में रहे.
नतीजा
ऊपर दिए गए स्निपेट से, स्क्रोल करने पर इमेज को बड़ा करने का इफ़ेक्ट मिलता है:
नेस्ट की गई स्क्रोलिंग इंटरऑप
स्क्रोल किए जा सकने वाले कंपोज़ेबल में स्क्रोल किए जा सकने वाले View
एलिमेंट नेस्ट करने या इसके उलट करने पर, आपको समस्याएं आ सकती हैं. सबसे ज़्यादा ध्यान देने वाली समस्याएं तब होती हैं, जब बच्चे को स्क्रोल करके उसके शुरू या खत्म होने की सीमा तक पहुंचा जाता है और यह उम्मीद की जाती है कि पैरंट स्क्रोलिंग को अपने हिसाब से मैनेज करेगा. हालांकि, ऐसा हो सकता है कि यह सुविधा काम न करे या उम्मीद के मुताबिक काम न करे.
यह समस्या, स्क्रोल किए जा सकने वाले कंपोज़ेबल में तय की गई उम्मीदों की वजह से होती है.
स्क्रोल किए जा सकने वाले कंपोज़ेबल में "nested-scroll-by-default" नियम होता है. इसका मतलब है कि स्क्रोल किए जा सकने वाले किसी भी कंटेनर को नेस्ट किए गए स्क्रोल चेन में शामिल होना चाहिए. इसके लिए, उसे NestedScrollConnection
के ज़रिए पैरंट के तौर पर और NestedScrollDispatcher
के ज़रिए चाइल्ड के तौर पर शामिल होना चाहिए.
इसके बाद, जब चाइल्ड कॉम्पोनेंट बाउंड्री पर होता है, तो वह पैरंट कॉम्पोनेंट के लिए नेस्टेड स्क्रोल को ट्रिगर करता है. उदाहरण के लिए, इस नियम की मदद से Compose Pager
और Compose LazyRow
को एक साथ बेहतर तरीके से काम करने की अनुमति मिलती है. हालांकि, जब ViewPager2
या RecyclerView
का इस्तेमाल करके इंटरऑपरेबिलिटी स्क्रोलिंग की जाती है, तब चाइल्ड से पैरंट तक लगातार स्क्रोल नहीं किया जा सकता. ऐसा इसलिए, क्योंकि ये NestedScrollingParent3
को लागू नहीं करते.
स्क्रोल किए जा सकने वाले View
एलिमेंट और दोनों दिशाओं में नेस्ट किए गए स्क्रोल किए जा सकने वाले कंपोज़ेबल के बीच, नेस्टेड स्क्रोलिंग इंटरऑप एपीआई चालू किया जा सकता है. इन समस्याओं को कम करने के लिए, नेस्टेड स्क्रोलिंग इंटरऑप एपीआई का इस्तेमाल किया जा सकता है. ऐसा इन स्थितियों में किया जा सकता है.
माता-पिता का एक खाता View
, जिसमें बच्चे का खाता ComposeView
शामिल है
सहयोग करने वाला अभिभावक View
वह होता है जो पहले से ही NestedScrollingParent3
लागू करता है. इसलिए, वह सहयोग करने वाले नेस्ट किए गए चाइल्ड कंपोज़ेबल से स्क्रोलिंग डेल्टा पा सकता है. इस मामले में ComposeView
, चाइल्ड क्लास के तौर पर काम करेगा और उसे NestedScrollingChild3
को (अप्रत्यक्ष रूप से) लागू करना होगा.
androidx.coordinatorlayout.widget.CoordinatorLayout
, एक साथ काम करने वाले पैरंट का एक उदाहरण है.
अगर आपको स्क्रोल किए जा सकने वाले View
पैरंट
कंटेनर और स्क्रोल किए जा सकने वाले नेस्ट किए गए चाइल्ड कंपोज़ेबल के बीच नेस्टेड स्क्रोलिंग इंटरऑपरेबिलिटी की ज़रूरत है, तो rememberNestedScrollInteropConnection()
का इस्तेमाल किया जा सकता है.
rememberNestedScrollInteropConnection()
इस कुकी का इस्तेमाल, NestedScrollConnection
को अनुमति देने और उसे सेव करने के लिए किया जाता है. NestedScrollConnection
की मदद से, View
पैरंट और Compose चाइल्ड के बीच नेस्ट किए गए स्क्रोल को इंटरऑपरेट किया जा सकता है. View
पैरंट, NestedScrollingParent3
को लागू करता है. इसका इस्तेमाल nestedScroll
मॉडिफ़ायर के साथ किया जाना चाहिए. Compose में नेस्टेड स्क्रोलिंग की सुविधा डिफ़ॉल्ट रूप से चालू होती है. इसलिए, इस कनेक्शन का इस्तेमाल करके View
में नेस्टेड स्क्रोलिंग की सुविधा चालू की जा सकती है. साथ ही, Views
और कंपोज़ेबल के बीच ज़रूरी ग्लू लॉजिक जोड़ा जा सकता है.
CoordinatorLayout
, CollapsingToolbarLayout
, और चाइल्ड कंपोज़ेबल का इस्तेमाल करने का एक सामान्य उदाहरण यहां दिया गया है:
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="100dp" android:fitsSystemWindows="true"> <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <!--...--> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.coordinatorlayout.widget.CoordinatorLayout>
आपको अपनी गतिविधि या फ़्रैगमेंट में, बच्चे के लिए कंपोज़ेबल और ज़रूरी NestedScrollConnection
सेट अप करना होगा:
open class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById<ComposeView>(R.id.compose_view).apply { setContent { val nestedScrollInterop = rememberNestedScrollInteropConnection() // Add the nested scroll connection to your top level @Composable element // using the nestedScroll modifier. LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) { items(20) { item -> Box( modifier = Modifier .padding(16.dp) .height(56.dp) .fillMaxWidth() .background(Color.Gray), contentAlignment = Alignment.Center ) { Text(item.toString()) } } } } } } }
चाइल्ड AndroidView
को शामिल करने वाला पैरंट कंपोज़ेबल
इस उदाहरण में, Compose साइड पर नेस्ट किए गए स्क्रोलिंग इंटरऑप एपीआई को लागू करने के बारे में बताया गया है. ऐसा तब किया जाता है, जब आपके पास पैरंट कंपोज़ेबल में चाइल्ड AndroidView
मौजूद हो. AndroidView
, NestedScrollDispatcher
को लागू करता है, क्योंकि यह Compose के स्क्रोल करने वाले पैरंट के लिए चाइल्ड के तौर पर काम करता है. साथ ही, NestedScrollingParent3
को भी लागू करता है, क्योंकि यह View
के स्क्रोल करने वाले चाइल्ड के लिए पैरंट के तौर पर काम करता है. इसके बाद, पैरंट कंपोज़िशन को नेस्ट किए गए स्क्रोल करने लायक चाइल्ड View
से, नेस्ट किए गए स्क्रोल के डेल्टा मिल पाएंगे.
यहां दिए गए उदाहरण में, इस स्थिति में नेस्ट की गई स्क्रोलिंग इंटरऑप को लागू करने का तरीका बताया गया है. साथ ही, इसमें कंपोज़ कोलैप्सिंग टूलबार का इस्तेमाल किया गया है:
@Composable
private fun NestedScrollInteropComposeParentWithAndroidChildExample() {
val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
// Sets up the nested scroll connection between the Box composable parent
// and the child AndroidView containing the RecyclerView
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// Updates the toolbar offset based on the scroll to enable
// collapsible behaviour
val delta = available.y
val newOffset = toolbarOffsetHeightPx.value + delta
toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
return Offset.Zero
}
}
}
Box(
Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
TopAppBar(
modifier = Modifier
.height(ToolbarHeight)
.offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }
)
AndroidView(
{ context ->
LayoutInflater.from(context)
.inflate(R.layout.view_in_compose_nested_scroll_interop, null).apply {
with(findViewById<RecyclerView>(R.id.main_list)) {
layoutManager = LinearLayoutManager(context, VERTICAL, false)
adapter = NestedScrollInteropAdapter()
}
}.also {
// Nested scrolling interop is enabled when
// nested scroll is enabled for the root View
ViewCompat.setNestedScrollingEnabled(it, true)
}
},
// ...
)
}
}
private class NestedScrollInteropAdapter :
Adapter<NestedScrollInteropAdapter.NestedScrollInteropViewHolder>() {
val items = (1..10).map { it.toString() }
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): NestedScrollInteropViewHolder {
return NestedScrollInteropViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
)
}
override fun onBindViewHolder(holder: NestedScrollInteropViewHolder, position: Int) {
// ...
}
class NestedScrollInteropViewHolder(view: View) : ViewHolder(view) {
fun bind(item: String) {
// ...
}
}
// ...
}
इस उदाहरण में, scrollable
मॉडिफ़ायर के साथ एपीआई का इस्तेमाल करने का तरीका बताया गया है:
@Composable
fun ViewInComposeNestedScrollInteropExample() {
Box(
Modifier
.fillMaxSize()
.scrollable(rememberScrollableState {
// View component deltas should be reflected in Compose
// components that participate in nested scrolling
it
}, Orientation.Vertical)
) {
AndroidView(
{ context ->
LayoutInflater.from(context)
.inflate(android.R.layout.list_item, null)
.apply {
// Nested scrolling interop is enabled when
// nested scroll is enabled for the root View
ViewCompat.setNestedScrollingEnabled(this, true)
}
}
)
}
}
आखिर में, इस उदाहरण में दिखाया गया है कि नेस्टेड स्क्रोलिंग इंटरऑप एपीआई का इस्तेमाल BottomSheetDialogFragment
के साथ कैसे किया जाता है, ताकि खींचकर छोड़ने की सुविधा काम कर सके:
class BottomSheetFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootView: View = inflater.inflate(R.layout.fragment_bottom_sheet, container, false)
rootView.findViewById<ComposeView>(R.id.compose_view).apply {
setContent {
val nestedScrollInterop = rememberNestedScrollInteropConnection()
LazyColumn(
Modifier
.nestedScroll(nestedScrollInterop)
.fillMaxSize()
) {
item {
Text(text = "Bottom sheet title")
}
items(10) {
Text(
text = "List item number $it",
modifier = Modifier.fillMaxWidth()
)
}
}
}
return rootView
}
}
}
ध्यान दें कि
rememberNestedScrollInteropConnection()
को जिस एलिमेंट से अटैच किया जाता है उसमें
NestedScrollConnection
इंस्टॉल हो जाएगा. NestedScrollConnection
, कंपोज़ लेवल से View
लेवल तक डेल्टा ट्रांसमिट करने के लिए ज़िम्मेदार है. इससे एलिमेंट को नेस्टेड स्क्रोलिंग में हिस्सा लेने की अनुमति मिलती है. हालांकि, इससे एलिमेंट अपने-आप स्क्रोल नहीं होते हैं. अपने-आप स्क्रोल न होने वाले कंपोज़ेबल, जैसे कि Box
या Column
के लिए, ऐसे कॉम्पोनेंट पर स्क्रोल डेल्टा, नेस्ट किए गए स्क्रोल सिस्टम में नहीं फैलेंगे. साथ ही, डेल्टा, rememberNestedScrollInteropConnection()
से मिले NestedScrollConnection
तक नहीं पहुंचेंगे. इसलिए, वे डेल्टा पैरंट View
कॉम्पोनेंट तक नहीं पहुंचेंगे. इस समस्या को हल करने के लिए, पक्का करें कि आपने नेस्ट किए गए इस तरह के कंपोज़ेबल के लिए, स्क्रोल किए जा सकने वाले मॉडिफ़ायर भी सेट किए हों. ज़्यादा जानकारी के लिए, नेस्टेड स्क्रोलिंग के बारे में बताया गया पिछला सेक्शन देखें.
सहयोग न करने वाले माता-पिता View
के साथ बच्चा ComposeView
ऐसे व्यू को नॉन-कोऑपरेटिंग व्यू कहा जाता है जो View
साइड पर ज़रूरी NestedScrolling
इंटरफ़ेस लागू नहीं करता. ध्यान दें कि इसका मतलब है कि इन Views
के साथ नेस्टेड स्क्रोलिंग इंटरऑपरेबिलिटी, बॉक्स से बाहर काम नहीं करती है. सहयोग न करने वाले Views
, RecyclerView
और ViewPager2
हैं.
अन्य संसाधन
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- जेस्चर के बारे में जानकारी
CoordinatorLayout
से Compose पर माइग्रेट करना- ईमेल लिखते समय व्यू का इस्तेमाल करना