ইঙ্গিত এবং Ripple API এ স্থানান্তর করুন

Modifier.clickable ব্যবহার করে এমন ইন্টারেক্টিভ কম্পোনেন্টগুলির কম্পোজিশন কর্মক্ষমতা উন্নত করতে, আমরা নতুন API চালু করেছি। এই APIগুলি আরও দক্ষ Indication বাস্তবায়নের অনুমতি দেয়, যেমন রিপলস।

androidx.compose.foundation:foundation:1.7.0+ এবং androidx.compose.material:material-ripple:1.7.0+ নিম্নলিখিত API পরিবর্তনগুলি অন্তর্ভুক্ত করে:

অবচয়

প্রতিস্থাপন

Indication#rememberUpdatedInstance

IndicationNodeFactory

rememberRipple()

এর পরিবর্তে মেটেরিয়াল লাইব্রেরিতে দেওয়া নতুন ripple() APIs।

দ্রষ্টব্য: এই প্রসঙ্গে, "মেটেরিয়াল লাইব্রেরি" বলতে androidx.compose.material:material , androidx.compose.material3:material3 , androidx.wear.compose:compose-material এবং androidx.wear.compose:compose-material3.

RippleTheme

হয়:

  • উপাদান লাইব্রেরি RippleConfiguration APIs ব্যবহার করুন, বা
  • আপনার নিজস্ব ডিজাইন সিস্টেম রিপল বাস্তবায়ন তৈরি করুন

এই পৃষ্ঠাটি আচরণ পরিবর্তনের প্রভাব এবং নতুন API-এ স্থানান্তরিত করার নির্দেশাবলী বর্ণনা করে।

আচরণ পরিবর্তন

নিম্নলিখিত লাইব্রেরি সংস্করণগুলির মধ্যে একটি লহরী আচরণ পরিবর্তন অন্তর্ভুক্ত:

  • androidx.compose.material:material:1.7.0+
  • androidx.compose.material3:material3:1.3.0+
  • androidx.wear.compose:compose-material:1.4.0+

ম্যাটেরিয়াল লাইব্রেরিগুলির এই সংস্করণগুলি আর rememberRipple() ব্যবহার করে না; পরিবর্তে, তারা নতুন রিপল API ব্যবহার করে। ফলস্বরূপ, তারা LocalRippleTheme জিজ্ঞাসা করে না। অতএব, আপনি যদি আপনার অ্যাপ্লিকেশনে LocalRippleTheme সেট করেন, উপাদান উপাদানগুলি এই মানগুলি ব্যবহার করবে না

নিম্নোক্ত বিভাগটি বর্ণনা করে কিভাবে অস্থায়ীভাবে স্থানান্তর না করে পুরানো আচরণে ফিরে আসা যায়; যাইহোক, আমরা নতুন API-এ স্থানান্তরিত করার পরামর্শ দিই। মাইগ্রেশন নির্দেশাবলীর জন্য, rememberRipple থেকে ripple এবং পরবর্তী বিভাগগুলিতে মাইগ্রেট দেখুন।

স্থানান্তর ছাড়াই উপাদান লাইব্রেরি সংস্করণ আপগ্রেড করুন

আপগ্রেড লাইব্রেরি সংস্করণ আনব্লক করতে, আপনি পুরানো আচরণে ফিরে আসার জন্য উপাদান উপাদানগুলি কনফিগার করতে অস্থায়ী LocalUseFallbackRippleImplementation CompositionLocal API ব্যবহার করতে পারেন:

CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
    MaterialTheme {
        App()
    }
}

MaterialTheme বাইরে এটি প্রদান করা নিশ্চিত করুন যাতে LocalIndication এর মাধ্যমে পুরানো রিপলস প্রদান করা যায়।

নিম্নলিখিত বিভাগগুলি বর্ণনা করে যে কীভাবে নতুন API-এ স্থানান্তর করা যায়।

rememberRipple থেকে ripple মাইগ্রেট করুন

একটি উপাদান লাইব্রেরি ব্যবহার করে

আপনি যদি একটি ম্যাটেরিয়াল লাইব্রেরি ব্যবহার করেন, তাহলে সংশ্লিষ্ট লাইব্রেরি থেকে ripple() কে কল করার সাথে rememberRipple() সরাসরি প্রতিস্থাপন করুন। এই API উপাদান থিম API থেকে প্রাপ্ত মান ব্যবহার করে একটি লহর তৈরি করে। তারপরে, ফিরে আসা বস্তুটিকে Modifier.clickable এবং/অথবা অন্যান্য উপাদানে পাস করুন।

উদাহরণস্বরূপ, নিম্নোক্ত স্নিপেট অবনমিত API ব্যবহার করে:

Box(
    Modifier.clickable(
        onClick = {},
        interactionSource = remember { MutableInteractionSource() },
        indication = rememberRipple()
    )
) {
    // ...
}

আপনার উপরের স্নিপেটটি এতে সংশোধন করা উচিত:

@Composable
private fun RippleExample() {
    Box(
        Modifier.clickable(
            onClick = {},
            interactionSource = remember { MutableInteractionSource() },
            indication = ripple()
        )
    ) {
        // ...
    }
}

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

কাস্টম ডিজাইন সিস্টেম বাস্তবায়ন

আপনি যদি আপনার নিজস্ব ডিজাইন সিস্টেম বাস্তবায়ন করেন, এবং আপনি রিপল কনফিগার করার জন্য একটি কাস্টম RippleTheme এর সাথে rememberRipple() ব্যবহার করে থাকেন, তাহলে আপনার নিজের রিপল API প্রদান করা উচিত যা material-ripple উন্মোচিত রিপল নোড এপিআই-কে প্রতিনিধিত্ব করে। তারপর, আপনার উপাদানগুলি আপনার নিজস্ব লহর ব্যবহার করতে পারে যা সরাসরি আপনার থিমের মানগুলিকে গ্রাস করে। আরও তথ্যের জন্য, RippleTheme থেকে মাইগ্রেট দেখুন।

RippleTheme থেকে মাইগ্রেট করুন

সাময়িকভাবে আচরণ পরিবর্তন অপ্ট আউট

ম্যাটেরিয়াল লাইব্রেরিগুলির একটি অস্থায়ী CompositionLocal , LocalUseFallbackRippleImplementation আছে, যা আপনি rememberRipple ব্যবহার করে ফিরে আসার জন্য সমস্ত উপাদান উপাদান কনফিগার করতে ব্যবহার করতে পারেন। এইভাবে, rememberRipple LocalRippleTheme জিজ্ঞাসা করতে থাকে।

নিম্নলিখিত কোড স্নিপেট প্রদর্শন করে কিভাবে LocalUseFallbackRippleImplementation CompositionLocal API ব্যবহার করতে হয়:

CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
    MaterialTheme {
        App()
    }
}

আপনি যদি একটি কাস্টম অ্যাপ থিম ব্যবহার করেন যা উপাদানের উপরে তৈরি করা হয়, তাহলে আপনি আপনার অ্যাপের থিমের অংশ হিসেবে সুরক্ষিতভাবে কম্পোজিশন স্থানীয় প্রদান করতে পারেন:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
        MaterialTheme(content = content)
    }
}

আরও তথ্যের জন্য, স্থানান্তরিত না করে আপগ্রেড মেটেরিয়াল লাইব্রেরি সংস্করণ দেখুন।

একটি প্রদত্ত উপাদানের জন্য একটি লহর নিষ্ক্রিয় করতে RippleTheme ব্যবহার করে৷

material এবং material3 লাইব্রেরিগুলি RippleConfiguration এবং LocalRippleConfiguration প্রকাশ করে, যা আপনাকে একটি সাবট্রির মধ্যে তরঙ্গের উপস্থিতি কনফিগার করতে দেয়। নোট করুন যে RippleConfiguration এবং LocalRippleConfiguration পরীক্ষামূলক, এবং শুধুমাত্র প্রতি-কম্পোনেন্ট কাস্টমাইজেশনের উদ্দেশ্যে। গ্লোবাল/থিম-ওয়াইড কাস্টমাইজেশন এই APIগুলির সাথে সমর্থিত নয়; সেই ব্যবহারের ক্ষেত্রে আরও তথ্যের জন্য একটি অ্যাপ্লিকেশনের সমস্ত লহরকে বিশ্বব্যাপী পরিবর্তন করতে RippleTheme ব্যবহার করা দেখুন।

উদাহরণস্বরূপ, নিম্নোক্ত স্নিপেট অবনমিত API ব্যবহার করে:

private object DisabledRippleTheme : RippleTheme {

    @Composable
    override fun defaultColor(): Color = Color.Transparent

    @Composable
    override fun rippleAlpha(): RippleAlpha = RippleAlpha(0f, 0f, 0f, 0f)
}

// ...
    CompositionLocalProvider(LocalRippleTheme provides DisabledRippleTheme) {
        Button {
            // ...
        }
    }

আপনার উপরের স্নিপেটটি এতে সংশোধন করা উচিত:

CompositionLocalProvider(LocalRippleConfiguration provides null) {
    Button {
        // ...
    }
}

একটি প্রদত্ত উপাদানের জন্য একটি লহরের রঙ/আলফা পরিবর্তন করতে RippleTheme ব্যবহার করে

পূর্ববর্তী বিভাগে বর্ণিত হিসাবে, RippleConfiguration এবং LocalRippleConfiguration হল পরীক্ষামূলক API এবং শুধুমাত্র প্রতি-কম্পোনেন্ট কাস্টমাইজেশনের উদ্দেশ্যে।

উদাহরণস্বরূপ, নিম্নোক্ত স্নিপেট অবনমিত API ব্যবহার করে:

private object DisabledRippleThemeColorAndAlpha : RippleTheme {

    @Composable
    override fun defaultColor(): Color = Color.Red

    @Composable
    override fun rippleAlpha(): RippleAlpha = MyRippleAlpha
}

// ...
    CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) {
        Button {
            // ...
        }
    }

আপনার উপরের স্নিপেটটি এতে সংশোধন করা উচিত:

@OptIn(ExperimentalMaterialApi::class)
private val MyRippleConfiguration =
    RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha)

// ...
    CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) {
        Button {
            // ...
        }
    }

একটি অ্যাপ্লিকেশনের সমস্ত লহরকে বিশ্বব্যাপী পরিবর্তন করতে RippleTheme ব্যবহার করে

পূর্বে, আপনি থিম-ব্যাপী স্তরে রিপল আচরণ সংজ্ঞায়িত করতে LocalRippleTheme ব্যবহার করতে পারেন। এটি মূলত কাস্টম ডিজাইন সিস্টেম কম্পোজিশন লোকাল এবং রিপলের মধ্যে একটি ইন্টিগ্রেশন পয়েন্ট ছিল। একটি জেনেরিক থিমিং আদিম উন্মুক্ত করার পরিবর্তে, material-ripple এখন একটি createRippleModifierNode() ফাংশন প্রকাশ করে। এই ফাংশনটি ডিজাইন সিস্টেম লাইব্রেরিগুলিকে উচ্চতর অর্ডার wrapper ইমপ্লিমেন্টেশন তৈরি করার অনুমতি দেয়, যা তাদের থিমের মানগুলি জিজ্ঞাসা করে এবং তারপর এই ফাংশন দ্বারা তৈরি নোডে রিপল ইমপ্লিমেন্টেশন অর্পণ করে।

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

গাইডেন্সের জন্য, মেটেরিয়াল লাইব্রেরিতে রিপল এপিআই ইমপ্লিমেন্টেশন দেখুন এবং আপনার নিজস্ব ডিজাইন সিস্টেমের জন্য প্রয়োজনীয় মেটেরিয়াল কম্পোজিশন লোকালদের কলগুলিকে প্রতিস্থাপন করুন।

Indication থেকে IndicationNodeFactory এ স্থানান্তর করুন

Indication কাছাকাছি পাস

আপনি যদি শুধু একটি Indication তৈরি করেন, যেমন Modifier.clickable বা Modifier.indication এ পাস করার জন্য একটি লহর তৈরি করেন, তাহলে আপনাকে কোনো পরিবর্তন করতে হবে না। IndicationNodeFactory Indication থেকে উত্তরাধিকার সূত্রে প্রাপ্ত, তাই সবকিছু কম্পাইল এবং কাজ চালিয়ে যাবে।

Indication তৈরি করা

আপনি যদি নিজের Indication বাস্তবায়ন তৈরি করেন, তবে বেশিরভাগ ক্ষেত্রে মাইগ্রেশন সহজ হওয়া উচিত। উদাহরণস্বরূপ, একটি Indication বিবেচনা করুন যা প্রেসে একটি স্কেল প্রভাব প্রয়োগ করে:

object ScaleIndication : Indication {
    @Composable
    override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
        // key the remember against interactionSource, so if it changes we create a new instance
        val instance = remember(interactionSource) { ScaleIndicationInstance() }

        LaunchedEffect(interactionSource) {
            interactionSource.interactions.collectLatest { interaction ->
                when (interaction) {
                    is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
                    is PressInteraction.Release -> instance.animateToResting()
                    is PressInteraction.Cancel -> instance.animateToResting()
                }
            }
        }

        return instance
    }
}

private class ScaleIndicationInstance : IndicationInstance {
    var currentPressPosition: Offset = Offset.Zero
    val animatedScalePercent = Animatable(1f)

    suspend fun animateToPressed(pressPosition: Offset) {
        currentPressPosition = pressPosition
        animatedScalePercent.animateTo(0.9f, spring())
    }

    suspend fun animateToResting() {
        animatedScalePercent.animateTo(1f, spring())
    }

    override fun ContentDrawScope.drawIndication() {
        scale(
            scale = animatedScalePercent.value,
            pivot = currentPressPosition
        ) {
            this@drawIndication.drawContent()
        }
    }
}

আপনি এটি দুটি ধাপে স্থানান্তর করতে পারেন:

  1. DrawModifierNode হতে ScaleIndicationInstance মাইগ্রেট করুন। DrawModifierNode এর জন্য API পৃষ্ঠটি IndicationInstance এর অনুরূপ: এটি একটি ContentDrawScope#draw() ফাংশন প্রকাশ করে যা কার্যত IndicationInstance#drawContent() এর সমতুল্য। আপনাকে সেই ফাংশনটি পরিবর্তন করতে হবে, এবং তারপর Indication পরিবর্তে সরাসরি নোডের ভিতরে collectLatest যুক্তি প্রয়োগ করতে হবে।

    উদাহরণস্বরূপ, নিম্নোক্ত স্নিপেট অবনমিত API ব্যবহার করে:

    private class ScaleIndicationInstance : IndicationInstance {
        var currentPressPosition: Offset = Offset.Zero
        val animatedScalePercent = Animatable(1f)
    
        suspend fun animateToPressed(pressPosition: Offset) {
            currentPressPosition = pressPosition
            animatedScalePercent.animateTo(0.9f, spring())
        }
    
        suspend fun animateToResting() {
            animatedScalePercent.animateTo(1f, spring())
        }
    
        override fun ContentDrawScope.drawIndication() {
            scale(
                scale = animatedScalePercent.value,
                pivot = currentPressPosition
            ) {
                this@drawIndication.drawContent()
            }
        }
    }

    আপনার উপরের স্নিপেটটি এতে সংশোধন করা উচিত:

    private class ScaleIndicationNode(
        private val interactionSource: InteractionSource
    ) : Modifier.Node(), DrawModifierNode {
        var currentPressPosition: Offset = Offset.Zero
        val animatedScalePercent = Animatable(1f)
    
        private suspend fun animateToPressed(pressPosition: Offset) {
            currentPressPosition = pressPosition
            animatedScalePercent.animateTo(0.9f, spring())
        }
    
        private suspend fun animateToResting() {
            animatedScalePercent.animateTo(1f, spring())
        }
    
        override fun onAttach() {
            coroutineScope.launch {
                interactionSource.interactions.collectLatest { interaction ->
                    when (interaction) {
                        is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
                        is PressInteraction.Release -> animateToResting()
                        is PressInteraction.Cancel -> animateToResting()
                    }
                }
            }
        }
    
        override fun ContentDrawScope.draw() {
            scale(
                scale = animatedScalePercent.value,
                pivot = currentPressPosition
            ) {
                this@draw.drawContent()
            }
        }
    }

  2. IndicationNodeFactory বাস্তবায়ন করতে ScaleIndication মাইগ্রেট করুন। কারণ সংগ্রহের যুক্তি এখন নোডে সরানো হয়েছে, এটি একটি খুব সাধারণ ফ্যাক্টরি অবজেক্ট যার একমাত্র দায়িত্ব একটি নোড উদাহরণ তৈরি করা।

    উদাহরণস্বরূপ, নিম্নোক্ত স্নিপেট অবনমিত API ব্যবহার করে:

    object ScaleIndication : Indication {
        @Composable
        override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
            // key the remember against interactionSource, so if it changes we create a new instance
            val instance = remember(interactionSource) { ScaleIndicationInstance() }
    
            LaunchedEffect(interactionSource) {
                interactionSource.interactions.collectLatest { interaction ->
                    when (interaction) {
                        is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
                        is PressInteraction.Release -> instance.animateToResting()
                        is PressInteraction.Cancel -> instance.animateToResting()
                    }
                }
            }
    
            return instance
        }
    }

    আপনার উপরের স্নিপেটটি এতে সংশোধন করা উচিত:

    object ScaleIndicationNodeFactory : IndicationNodeFactory {
        override fun create(interactionSource: InteractionSource): DelegatableNode {
            return ScaleIndicationNode(interactionSource)
        }
    
        override fun hashCode(): Int = -1
    
        override fun equals(other: Any?) = other === this
    }

একটি IndicationInstance তৈরি করতে Indication ব্যবহার করে

বেশিরভাগ ক্ষেত্রে, আপনার একটি উপাদানের জন্য Indication প্রদর্শন করতে Modifier.indication ব্যবহার করা উচিত। যাইহোক, বিরল ক্ষেত্রে যে আপনি ম্যানুয়ালি rememberUpdatedInstance ব্যবহার করে একটি IndicationInstance তৈরি করছেন, Indication একটি IndicationNodeFactory কিনা তা পরীক্ষা করার জন্য আপনাকে আপনার বাস্তবায়ন আপডেট করতে হবে যাতে আপনি একটি হালকা বাস্তবায়ন ব্যবহার করতে পারেন। উদাহরণস্বরূপ, Modifier.indication তৈরি করা নোডকে অভ্যন্তরীণভাবে অর্পণ করবে যদি এটি একটি IndicationNodeFactory হয়। যদি তা না হয়, তাহলে এটি rememberUpdatedInstance কল করতে Modifier.composed ব্যবহার করবে।