স্ক্রোল, স্ক্রোল

স্ক্রোল মডিফায়ার

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 { mutableFloatStateOf(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())
    }
}

একটি UI উপাদান যা আঙুলের প্রেস সনাক্ত করে এবং আঙুলের অবস্থানের সাংখ্যিক মান প্রদর্শন করে।

নেস্টেড স্ক্রোলিং

নেস্টেড স্ক্রোলিং এমন একটি সিস্টেম যেখানে একে অপরের মধ্যে থাকা একাধিক স্ক্রোলিং উপাদান একসাথে কাজ করে একটি একক স্ক্রোল অঙ্গভঙ্গিতে প্রতিক্রিয়া দেখিয়ে এবং তাদের স্ক্রোলিং ডেল্টা (পরিবর্তন) যোগাযোগ করে।

নেস্টেড স্ক্রোলিং সিস্টেম স্ক্রোলযোগ্য এবং শ্রেণিবদ্ধভাবে সংযুক্ত উপাদানগুলির মধ্যে সমন্বয় সাধন করে (প্রায়শই একই প্যারেন্ট ভাগ করে)। এই সিস্টেমটি স্ক্রোলিং কন্টেইনারগুলিকে লিঙ্ক করে এবং স্ক্রোলিং ডেল্টাগুলির সাথে মিথস্ক্রিয়া করার অনুমতি দেয় যা প্রচারিত এবং ভাগ করা হচ্ছে।

কম্পোজ কম্পোজেবলের মধ্যে নেস্টেড স্ক্রোলিং পরিচালনা করার একাধিক উপায় প্রদান করে। নেস্টেড স্ক্রোলিংয়ের একটি সাধারণ উদাহরণ হল অন্য তালিকার ভিতরে একটি তালিকা, এবং আরও জটিল ক্ষেত্রে হল একটি কলাপসিং টুলবার

স্বয়ংক্রিয় নেস্টেড স্ক্রোলিং

সহজ নেস্টেড স্ক্রোলিংয়ের জন্য আপনার কোনও পদক্ষেপের প্রয়োজন নেই। স্ক্রলিং ক্রিয়া শুরু করে এমন অঙ্গভঙ্গিগুলি শিশু থেকে পিতামাতার কাছে স্বয়ংক্রিয়ভাবে প্রেরণ করা হয়, যাতে শিশু যখন আর স্ক্রোল করতে না পারে, তখন অঙ্গভঙ্গিটি তার মূল উপাদান দ্বারা পরিচালিত হয়।

স্বয়ংক্রিয় নেস্টেড স্ক্রোলিং কম্পোজের কিছু উপাদান এবং সংশোধক দ্বারা সমর্থিত এবং বাক্সের বাইরে সরবরাহ করা হয়: verticalScroll , horizontalScroll , scrollable , Lazy APIs এবং 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)
                    )
                }
            }
        }
    }
}

দুটি নেস্টেড উল্লম্ব স্ক্রোলিং UI উপাদান, ভিতরের উপাদানের ভিতরে এবং বাইরে অঙ্গভঙ্গির প্রতি সাড়া দেয়।

nestedScroll মডিফায়ার ব্যবহার করে

যদি আপনার একাধিক উপাদানের মধ্যে একটি উন্নত সমন্বিত স্ক্রোল তৈরি করার প্রয়োজন হয়, তাহলে nestedScroll মডিফায়ার আপনাকে একটি nested scrolling hierarchy সংজ্ঞায়িত করে আরও নমনীয়তা প্রদান করে। পূর্ববর্তী বিভাগে যেমন উল্লেখ করা হয়েছে, কিছু উপাদানের অন্তর্নির্মিত নেস্টেড স্ক্রোল সমর্থন রয়েছে। তবে, যেসব কম্পোজেবল স্বয়ংক্রিয়ভাবে স্ক্রোলযোগ্য নয়, যেমন Box বা Column , তাদের ক্ষেত্রে এই ধরনের উপাদানের স্ক্রোল ডেল্টাগুলি নেস্টেড স্ক্রোল সিস্টেমে প্রচারিত হবে না এবং ডেল্টাগুলি NestedScrollConnection বা মূল উপাদানে পৌঁছাবে না। এটি সমাধানের জন্য, আপনি কাস্টম উপাদান সহ অন্যান্য উপাদানগুলিতে এই ধরনের সমর্থন প্রদান করতে nestedScroll ব্যবহার করতে পারেন।

নেস্টেড স্ক্রোলিং চক্র

নেস্টেড স্ক্রোল চক্র হল স্ক্রোল ডেল্টার প্রবাহ যা নেস্টেড স্ক্রোলিং সিস্টেমের অংশ এমন সমস্ত উপাদান (অথবা নোড) এর মাধ্যমে হায়ারার্কি ট্রির উপরে এবং নীচে প্রেরণ করা হয়, উদাহরণস্বরূপ স্ক্রোলযোগ্য উপাদান এবং সংশোধক, অথবা nestedScroll ব্যবহার করে।

নেস্টেড স্ক্রোলিং চক্রের পর্যায়গুলি

যখন কোনও স্ক্রোলযোগ্য উপাদান দ্বারা কোনও ট্রিগার ইভেন্ট (উদাহরণস্বরূপ, একটি অঙ্গভঙ্গি) সনাক্ত করা হয়, প্রকৃত স্ক্রোলিং ক্রিয়াটি ট্রিগার হওয়ার আগেই, উৎপন্ন ডেল্টাগুলি নেস্টেড স্ক্রোল সিস্টেমে পাঠানো হয় এবং তিনটি পর্যায়ের মধ্য দিয়ে যায়: প্রি-স্ক্রোল, নোড খরচ এবং পোস্ট-স্ক্রোল।

নেস্টেড স্ক্রোলিং চক্রের পর্যায়গুলি

প্রথম, প্রাক-স্ক্রোল পর্যায়ে, যে উপাদানটি ট্রিগার ইভেন্ট ডেল্টা পেয়েছে তারা সেই ইভেন্টগুলিকে হায়ারার্কি ট্রির মাধ্যমে শীর্ষতম প্যারেন্টের কাছে প্রেরণ করবে। ডেল্টা ইভেন্টগুলি তখন বুদবুদ হয়ে যাবে, যার অর্থ হল ডেল্টাগুলি মূল-সর্বাধিক প্যারেন্ট থেকে নীচে সেই সন্তানের দিকে প্রচারিত হবে যেটি নেস্টেড স্ক্রোল চক্র শুরু করেছিল।

প্রাক-স্ক্রোল পর্যায় - প্রেরণ আপ

এটি নেস্টেড স্ক্রোল প্যারেন্টদের ( nestedScroll বা স্ক্রোলযোগ্য মডিফায়ার ব্যবহার করে কম্পোজেবল) নোড নিজেই এটি ব্যবহার করার আগে ডেল্টা দিয়ে কিছু করার সুযোগ দেয়।

প্রাক-স্ক্রোল পর্যায় - বুদবুদ ডাউন

নোড কনজাম্পশন ফেজে, নোড নিজেই এমন কিছু ডেল্টা ব্যবহার করবে যা তার পিতামাতারা ব্যবহার করেনি। এটি তখনই হয় যখন স্ক্রলিং মুভমেন্টটি আসলে সম্পন্ন হয় এবং দৃশ্যমান হয়।

নোড খরচ পর্যায়

এই পর্যায়ে, শিশুটি বাকি স্ক্রোলটির সম্পূর্ণ বা আংশিক অংশ খেতে পারে। বাকি যা কিছু থাকবে তা স্ক্রোল-পরবর্তী পর্যায়ে যাওয়ার জন্য আবার পাঠানো হবে।

অবশেষে, পোস্ট-স্ক্রোল পর্যায়ে, নোড নিজেই যা গ্রাস করেনি তা আবার তার পূর্বপুরুষদের কাছে ব্যবহারের জন্য পাঠানো হবে।

স্ক্রোল-পরবর্তী পর্যায় - প্রেরণ উপরে

স্ক্রোল-পরবর্তী পর্যায়টি প্রাক-স্ক্রোল পর্যায়ের মতোই কাজ করে, যেখানে অভিভাবকদের মধ্যে যে কেউ খেতে বা না খেতে বেছে নিতে পারেন।

স্ক্রোল-পরবর্তী পর্যায় - বুদবুদ ডাউন

স্ক্রোলের মতোই, যখন একটি ড্র্যাগ জেসচার শেষ হয়, তখন ব্যবহারকারীর উদ্দেশ্য এমন একটি বেগে রূপান্তরিত হতে পারে যা স্ক্রোলযোগ্য কন্টেইনারটি ফ্লিং (অ্যানিমেশন ব্যবহার করে স্ক্রোল) করতে ব্যবহৃত হয়। ফ্লিং নেস্টেড স্ক্রোল চক্রেরও অংশ, এবং ড্র্যাগ ইভেন্ট দ্বারা উৎপন্ন বেগ একই ধরণের পর্যায়গুলির মধ্য দিয়ে যায়: প্রাক-ফ্লিং, নোড খরচ এবং পোস্ট-ফ্লিং। মনে রাখবেন যে ফ্লিং অ্যানিমেশন শুধুমাত্র স্পর্শ জেসচারের সাথে সম্পর্কিত এবং অন্যান্য ইভেন্ট, যেমন a11y বা হার্ডওয়্যার স্ক্রোল দ্বারা ট্রিগার হবে না।

নেস্টেড স্ক্রোলিং চক্রে অংশগ্রহণ করুন

চক্রে অংশগ্রহণের অর্থ হল শ্রেণিবিন্যাসের সাথে ডেল্টার ব্যবহারকে আটকানো, গ্রহণ করা এবং রিপোর্ট করা। কম্পোজ নেস্টেড স্ক্রোলিং সিস্টেম কীভাবে কাজ করে এবং এর সাথে সরাসরি কীভাবে ইন্টারঅ্যাক্ট করতে হয় তা প্রভাবিত করার জন্য এক সেট সরঞ্জাম সরবরাহ করে, উদাহরণস্বরূপ যখন স্ক্রোলযোগ্য উপাদান স্ক্রোল করা শুরু করার আগে আপনাকে স্ক্রোল ডেল্টাগুলির সাথে কিছু করার প্রয়োজন হয়।

যদি নেস্টেড স্ক্রোল সাইকেলটি নোডের একটি শৃঙ্খলের উপর কাজ করে এমন একটি সিস্টেম হয়, 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 এলিমেন্ট নেস্ট করার চেষ্টা করেন, অথবা অন্যভাবে, তখন আপনার সমস্যার সম্মুখীন হতে পারেন। সবচেয়ে লক্ষণীয় সমস্যাগুলি তখনই ঘটবে যখন আপনি শিশুটিকে স্ক্রোল করে তার শুরু বা শেষ সীমানায় পৌঁছান এবং অভিভাবকদের কাছ থেকে স্ক্রোলিংয়ের কাজটি নেওয়ার আশা করেন। তবে, এই প্রত্যাশিত আচরণটি হয় নাও হতে পারে অথবা প্রত্যাশা অনুযায়ী কাজ নাও করতে পারে।

এই সমস্যাটি স্ক্রোলযোগ্য কম্পোজেবলের মধ্যে থাকা প্রত্যাশার ফলাফল। স্ক্রোলযোগ্য কম্পোজেবলের একটি "নেস্টেড-স্ক্রোল-বাই-ডিফল্ট" নিয়ম থাকে, যার অর্থ হল যে কোনও স্ক্রোলযোগ্য কন্টেইনারকে নেস্টেড স্ক্রোল চেইনে অংশগ্রহণ করতে হবে, উভয় ক্ষেত্রেই NestedScrollConnection এর মাধ্যমে অভিভাবক হিসেবে এবং NestedScrollDispatcher এর মাধ্যমে শিশু হিসেবে। শিশুটি যখন সীমানায় থাকবে তখন শিশুটি পিতামাতার জন্য একটি নেস্টেড স্ক্রোল চালাবে। উদাহরণস্বরূপ, এই নিয়মটি Compose Pager এবং Compose LazyRow একসাথে ভালভাবে কাজ করার অনুমতি দেয়। তবে, যখন ViewPager2 বা RecyclerView এর সাথে ইন্টারঅপারেবিলিটি স্ক্রোলিং করা হচ্ছে, যেহেতু এগুলি NestedScrollingParent3 বাস্তবায়ন করে না, তাই শিশু থেকে পিতামাতার কাছে ক্রমাগত স্ক্রোলিং সম্ভব নয়।

স্ক্রোলযোগ্য View এলিমেন্ট এবং স্ক্রোলযোগ্য কম্পোজেবল উভয় দিকেই নেস্টেড থাকা নেস্টেড স্ক্রোলিং ইন্টারঅপ API সক্ষম করতে, আপনি নিম্নলিখিত পরিস্থিতিতে এই সমস্যাগুলি কমাতে নেস্টেড স্ক্রোলিং ইন্টারঅপ API ব্যবহার করতে পারেন।

একটি সহযোগী অভিভাবক View যেখানে একটি চাইল্ড ComposeView থাকে

একটি সহযোগী অভিভাবক View হল এমন একটি ভিউ যা ইতিমধ্যেই NestedScrollingParent3 প্রয়োগ করে এবং তাই একটি সহযোগী নেস্টেড চাইল্ড কম্পোজেবল থেকে স্ক্রোলিং ডেল্টা গ্রহণ করতে সক্ষম। এই ক্ষেত্রে ComposeView একটি শিশু হিসাবে কাজ করবে এবং (পরোক্ষভাবে) NestedScrollingChild3 প্রয়োগ করতে হবে। সহযোগী অভিভাবকের একটি উদাহরণ হল androidx.coordinatorlayout.widget.CoordinatorLayout

যদি আপনার স্ক্রোলযোগ্য View প্যারেন্ট কন্টেইনার এবং নেস্টেড স্ক্রোলযোগ্য চাইল্ড কম্পোজেবলের মধ্যে নেস্টেড স্ক্রোলিং ইন্টারঅপারেবিলিটি প্রয়োজন হয়, তাহলে আপনি rememberNestedScrollInteropConnection() ব্যবহার করতে পারেন।

rememberNestedScrollInteropConnection() NestedScrollConnection মঞ্জুরি দেয় এবং মনে রাখে যা NestedScrollingParent3 প্রয়োগকারী একটি View প্যারেন্ট এবং একটি কম্পোজ চাইল্ডের মধ্যে নেস্টেড স্ক্রোল ইন্টারঅপারেবিলিটি সক্ষম করে। এটি একটি nestedScroll মডিফায়ারের সাথে একত্রে ব্যবহার করা উচিত। যেহেতু নেস্টেড স্ক্রোলিং কম্পোজ সাইডে ডিফল্টরূপে সক্ষম থাকে, তাই আপনি 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>

আপনার Activity অথবা Fragment-এ, আপনার সন্তানের কম্পোজেবল এবং প্রয়োজনীয় 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 সাইডে নেস্টেড স্ক্রোলিং ইন্টারঅপ API বাস্তবায়নকে অন্তর্ভুক্ত করে - যখন আপনার একটি প্যারেন্ট কম্পোজেবল থাকে যার মধ্যে একটি চাইল্ড AndroidView থাকে। AndroidView NestedScrollDispatcher প্রয়োগ করে, কারণ এটি একটি Compose স্ক্রোলিং প্যারেন্টের জন্য একটি চাইল্ড হিসেবে কাজ করে, পাশাপাশি NestedScrollingParent3 ও প্রয়োগ করে, কারণ এটি একটি View স্ক্রোলিং চাইল্ডের জন্য একটি প্যারেন্ট হিসেবে কাজ করে। Compose প্যারেন্ট তখন একটি নেস্টেড স্ক্রোলেবল চাইল্ড 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 মডিফায়ার দিয়ে API ব্যবহার করতে পারেন:

@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)
                    }
            }
        )
    }
}

এবং পরিশেষে, এই উদাহরণটি দেখায় যে কীভাবে নেস্টেড স্ক্রোলিং ইন্টারঅপ API 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 Compose লেভেল থেকে View লেভেলে ডেল্টা ট্রান্সমিট করার জন্য দায়ী। এটি উপাদানটিকে নেস্টেড স্ক্রলিংয়ে অংশগ্রহণ করতে সক্ষম করে, কিন্তু এটি স্বয়ংক্রিয়ভাবে উপাদানগুলির স্ক্রলিং সক্ষম করে না। Box বা Column মতো স্বয়ংক্রিয়ভাবে স্ক্রোলযোগ্য নয় এমন composables-এর ক্ষেত্রে, এই ধরনের উপাদানগুলিতে স্ক্রোল ডেল্টাগুলি নেস্টেড স্ক্রোল সিস্টেমে প্রচারিত হবে না এবং deltas rememberNestedScrollInteropConnection() দ্বারা প্রদত্ত NestedScrollConnection এ পৌঁছাবে না, তাই সেই ডেল্টাগুলি প্যারেন্ট View কম্পোনেন্টে পৌঁছাবে না। এটি সমাধানের জন্য, নিশ্চিত করুন যে আপনি এই ধরণের নেস্টেড কম্পোজেবলগুলিতে স্ক্রোলযোগ্য মডিফায়ার সেট করেছেন। আরও বিস্তারিত তথ্যের জন্য আপনি নেস্টেড স্ক্রলিং- এর পূর্ববর্তী বিভাগটি দেখতে পারেন।

একটি অসহযোগী অভিভাবক View যার মধ্যে একটি শিশু ComposeView রয়েছে

একটি অ-সহযোগিতামূলক ভিউ হল এমন একটি ভিউ যা View সাইডে প্রয়োজনীয় NestedScrolling ইন্টারফেসগুলি বাস্তবায়ন করে না। মনে রাখবেন যে এর অর্থ হল এই Views সাথে নেস্টেড স্ক্রোলিং ইন্টারঅপারেবিলিটি বাক্সের বাইরে কাজ করে না। অ-সহযোগিতামূলক Views হল RecyclerView এবং ViewPager2

অতিরিক্ত সম্পদ

{% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %} {% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %}