Modifier.clickable ব্যবহার করে এমন ইন্ট্যার্যাক্টিভ কম্পোনেন্টের কম্পোজিশন পারফর্ম্যান্স উন্নত করতে
আমরা নতুন API নিয়ে এসেছি। এইসব API আরও
দক্ষ Indication প্রয়োগের অনুমতি দেয়, যেমন রিপেল।
androidx.compose.foundation:foundation:1.7.0+ এবং
androidx.compose.material:material-ripple:1.7.0+-এ নিম্নলিখিত API
পরিবর্তন অন্তর্ভুক্ত:
বন্ধ করে দেওয়া হয়েছে |
পরিবর্তন |
|---|---|
|
|
|
নতুন মনে রাখবেন: এই প্রসঙ্গে, "মেটেরিয়াল লাইব্রেরি" বলতে |
|
যেকোনও একটি:
|
এই পৃষ্ঠায় আচরণগত পরিবর্তনের প্রভাব এবং নতুন API-তে মাইগ্রেট করার নির্দেশাবলী বর্ণনা করা হয়েছে।
আচরণগত পরিবর্তন
নিম্নলিখিত লাইব্রেরি ভার্সনে রিপেল আচরণ সংক্রান্ত পরিবর্তন অন্তর্ভুক্ত আছে:
androidx.compose.material:material:1.7.0+androidx.compose.material3:material3:1.3.0+androidx.wear.compose:compose-material:1.4.0+
Material লাইব্রেরির এই ভার্সনগুলি আর rememberRipple() ব্যবহার করে না; পরিবর্তে,
এগুলি নতুন রিপল API ব্যবহার করে। ফলে, তারা LocalRippleTheme কোয়েরি করেন না।
তাই, আপনার অ্যাপ্লিকেশনে LocalRippleTheme সেট করলে, Material
কম্পোনেন্ট এই ভ্যালু ব্যবহার করবে না।
নতুন API-তে কীভাবে মাইগ্রেট করবেন তা নিম্নলিখিত বিভাগে বর্ণনা করা হয়েছে।
rememberRipple থেকে ripple-এ মাইগ্রেট করুন
মেটেরিয়াল লাইব্রেরি ব্যবহার করা
আপনি কোনও মেটেরিয়াল লাইব্রেরি ব্যবহার করলে, সরাসরি rememberRipple()-এর পরিবর্তে সংশ্লিষ্ট লাইব্রেরি থেকে
ripple()-কে কল করুন। এই API, Material থিম 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-এ এক্সপোজ করা রিপেল নোড
API-তে ডেলিগেট করে। তারপরে, আপনার কম্পোনেন্ট আপনার নিজস্ব রিপেল ব্যবহার করতে পারবে
যা সরাসরি আপনার থিমের ভ্যালু ব্যবহার করে। আরও তথ্যের জন্য, থেকে
মাইগ্রেট করুনRippleTheme দেখুন।
RippleTheme থেকে মাইগ্রেট করা
নির্দিষ্ট কম্পোনেন্টের জন্য রিপেল বন্ধ করতে 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 ব্যবহার করে:
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 যা সেই কন্ট্র্যাক্টকে ডিফাইন করে, থিম থেকে
ইমপ্লিসিটভাবে পাওয়া যায় না।
নির্দেশাবলীর জন্য, Material লাইব্রেরিতে রিপল API প্রয়োগ দেখুন এবং আপনার নিজস্ব ডিজাইন সিস্টেমের প্রয়োজন অনুযায়ী Material কম্পোজিশন লোকালকে কল করার পরিবর্তে অন্য কিছু ব্যবহার করুন।
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() } } }
আপনি দুটি ধাপে এটি মাইগ্রেট করতে পারবেন:
ScaleIndicationInstanceথেকে মাইগ্রেট করেDrawModifierNodeহয়ে যান।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() } } }
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 ব্যবহার করা
বেশিরভাগ ক্ষেত্রে, কোনও কম্পোনেন্টের জন্য Modifier.indication Indication ব্যবহার করা উচিত।
তবে, আপনি যদি ম্যানুয়ালি কোনও
IndicationInstance তৈরি করে থাকেন rememberUpdatedInstance, তাহলে আপনাকে নিজের
ইমপ্লিমেন্টেশন আপডেট করে দেখতে হবে যে Indication হল IndicationNodeFactory কিনা, যাতে আপনি
আরও হালকা ইমপ্লিমেন্টেশন ব্যবহার করতে পারেন। যেমন, Modifier.indication ইন্টার্নালি
তৈরি করা নোডে ডেলিগেট করবে যদি এটি একটি IndicationNodeFactory হয়। না থাকলে, rememberUpdatedInstance-কে কল করার জন্য Modifier.composed ব্যবহার করা হবে।