কাস্টম মডিফায়ার তৈরি করুন

কম্পোজ সাধারণ আচরণের জন্য অনেকগুলি মডিফায়ার সরাসরি বাক্সের বাইরে সরবরাহ করে, তবে আপনি নিজস্ব কাস্টম মডিফায়ারও তৈরি করতে পারেন।

মডিফায়ারের একাধিক অংশ থাকে:

  • একটি সংশোধক কারখানা
    • এটি Modifier এর একটি এক্সটেনশন ফাংশন, যা আপনার মডিফায়ারের জন্য একটি ইডিওম্যাটিক API প্রদান করে এবং মডিফায়ারগুলিকে একসাথে চেইন করার অনুমতি দেয়। মডিফায়ার ফ্যাক্টরি আপনার UI পরিবর্তন করতে কম্পোজ দ্বারা ব্যবহৃত মডিফায়ার উপাদানগুলি তৈরি করে।
  • একটি সংশোধক উপাদান
    • এখানেই আপনি আপনার মডিফায়ারের আচরণ বাস্তবায়ন করতে পারবেন।

প্রয়োজনীয় কার্যকারিতার উপর নির্ভর করে একটি কাস্টম মডিফায়ার বাস্তবায়নের একাধিক উপায় রয়েছে। প্রায়শই, একটি কাস্টম মডিফায়ার বাস্তবায়নের সবচেয়ে সহজ উপায় হল একটি কাস্টম মডিফায়ার ফ্যাক্টরি বাস্তবায়ন করা যা ইতিমধ্যেই সংজ্ঞায়িত অন্যান্য মডিফায়ার ফ্যাক্টরিগুলিকে একত্রিত করে। যদি আপনার আরও কাস্টম আচরণের প্রয়োজন হয়, তাহলে Modifier.Node API ব্যবহার করে মডিফায়ার উপাদানটি বাস্তবায়ন করুন, যা নিম্ন স্তরের কিন্তু আরও নমনীয়তা প্রদান করে।

বিদ্যমান মডিফায়ারগুলিকে একসাথে চেইন করুন

বিদ্যমান মডিফায়ার ব্যবহার করে প্রায়শই কাস্টম মডিফায়ার তৈরি করা সম্ভব। উদাহরণস্বরূপ, Modifier.clip() graphicsLayer মডিফায়ার ব্যবহার করে বাস্তবায়িত হয়। এই কৌশলটি বিদ্যমান মডিফায়ার উপাদান ব্যবহার করে, এবং আপনি আপনার নিজস্ব কাস্টম মডিফায়ার কারখানা সরবরাহ করেন।

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

fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true)

অথবা, যদি আপনি দেখতে পান যে আপনি একই গ্রুপের মডিফায়ারগুলি প্রায়শই পুনরাবৃত্তি করছেন, তাহলে আপনি সেগুলিকে আপনার নিজস্ব মডিফায়ারের মধ্যে মুড়ে দিতে পারেন:

fun Modifier.myBackground(color: Color) = padding(16.dp)
    .clip(RoundedCornerShape(8.dp))
    .background(color)

একটি কম্পোজেবল মডিফায়ার ফ্যাক্টরি ব্যবহার করে একটি কাস্টম মডিফায়ার তৈরি করুন

আপনি একটি বিদ্যমান মডিফায়ারে মান পাস করার জন্য একটি কম্পোজেবল ফাংশন ব্যবহার করে একটি কাস্টম মডিফায়ার তৈরি করতে পারেন। এটি একটি কম্পোজেবল মডিফায়ার ফ্যাক্টরি নামে পরিচিত।

একটি কম্পোজেবল মডিফায়ার ফ্যাক্টরি ব্যবহার করে একটি মডিফায়ার তৈরি করলে আপনি উচ্চ স্তরের কম্পোজ API ব্যবহার করতে পারবেন, যেমন animate*AsState এবং অন্যান্য Compose state সমর্থিত অ্যানিমেশন API । উদাহরণস্বরূপ, নিম্নলিখিত স্নিপেটে একটি মডিফায়ার দেখানো হয়েছে যা সক্রিয়/অক্ষম করা অবস্থায় একটি আলফা পরিবর্তনকে অ্যানিমেট করে:

@Composable
fun Modifier.fade(enable: Boolean): Modifier {
    val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f)
    return this then Modifier.graphicsLayer { this.alpha = alpha }
}

যদি আপনার কাস্টম মডিফায়ারটি CompositionLocal থেকে ডিফল্ট মান প্রদানের জন্য একটি সুবিধাজনক পদ্ধতি হয়, তাহলে এটি বাস্তবায়নের সবচেয়ে সহজ উপায় হল একটি কম্পোজেবল মডিফায়ার ফ্যাক্টরি ব্যবহার করা:

@Composable
fun Modifier.fadedBackground(): Modifier {
    val color = LocalContentColor.current
    return this then Modifier.background(color.copy(alpha = 0.5f))
}

এই পদ্ধতির কিছু সতর্কতা রয়েছে, যা নিম্নলিখিত বিভাগগুলিতে বিস্তারিতভাবে বর্ণনা করা হয়েছে।

CompositionLocal মানগুলি মডিফায়ার কারখানার কল সাইটে সমাধান করা হয়

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

@Composable
fun Modifier.myBackground(): Modifier {
    val color = LocalContentColor.current
    return this then Modifier.background(color.copy(alpha = 0.5f))
}

@Composable
fun MyScreen() {
    CompositionLocalProvider(LocalContentColor provides Color.Green) {
        // Background modifier created with green background
        val backgroundModifier = Modifier.myBackground()

        // LocalContentColor updated to red
        CompositionLocalProvider(LocalContentColor provides Color.Red) {

            // Box will have green background, not red as expected.
            Box(modifier = backgroundModifier)
        }
    }
}

যদি আপনার মডিফায়ারটি এভাবে কাজ করবে বলে আপনি আশা করেন না, তাহলে একটি কাস্টম Modifier.Node ব্যবহার করুন, কারণ কম্পোজিশন লোকালগুলি ব্যবহারের স্থানে সঠিকভাবে সমাধান করা হবে এবং নিরাপদে উত্তোলন করা যাবে।

কম্পোজেবল ফাংশন মডিফায়ারগুলি কখনই বাদ দেওয়া হয় না

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

কম্পোজেবল ফাংশন মডিফায়ারগুলিকে কম্পোজেবল ফাংশনের মধ্যে ডাকা আবশ্যক।

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

val extractedModifier = Modifier.background(Color.Red) // Hoisted to save allocations

@Composable
fun Modifier.composableModifier(): Modifier {
    val color = LocalContentColor.current.copy(alpha = 0.5f)
    return this then Modifier.background(color)
}

@Composable
fun MyComposable() {
    val composedModifier = Modifier.composableModifier() // Cannot be extracted any higher
}

Modifier.Node ব্যবহার করে কাস্টম মডিফায়ার আচরণ বাস্তবায়ন করুন

Modifier.Node হল Compose-এ মডিফায়ার তৈরির জন্য একটি নিম্ন স্তরের API। এটি একই API যেখানে Compose তার নিজস্ব মডিফায়ার প্রয়োগ করে এবং এটি কাস্টম মডিফায়ার তৈরির সবচেয়ে কার্যকর উপায়।

Modifier.Node ব্যবহার করে একটি কাস্টম মডিফায়ার প্রয়োগ করুন।

Modifier.Node ব্যবহার করে একটি কাস্টম মডিফায়ার বাস্তবায়নের তিনটি অংশ রয়েছে:

  • একটি Modifier.Node বাস্তবায়ন যা আপনার মডিফায়ারের যুক্তি এবং অবস্থা ধারণ করে।
  • একটি ModifierNodeElement যা মডিফায়ার নোড ইনস্ট্যান্স তৈরি এবং আপডেট করে।
  • পূর্বে বিস্তারিতভাবে উল্লেখ করা হয়েছে, একটি ঐচ্ছিক সংশোধক কারখানা।

ModifierNodeElement ক্লাসগুলি স্টেটলেস এবং প্রতিটি রিকম্পোজিশনে নতুন ইনস্ট্যান্স বরাদ্দ করা হয়, যেখানে Modifier.Node ক্লাসগুলি স্টেটফুল হতে পারে এবং একাধিক রিকম্পোজিশন জুড়ে টিকে থাকতে পারে, এমনকি পুনঃব্যবহারও করা যেতে পারে।

নিম্নলিখিত অংশটি প্রতিটি অংশের বর্ণনা দেয় এবং একটি বৃত্ত আঁকার জন্য একটি কাস্টম মডিফায়ার তৈরির উদাহরণ দেখায়।

Modifier.Node

Modifier.Node বাস্তবায়ন (এই উদাহরণে, CircleNode ) আপনার কাস্টম মডিফায়ারের কার্যকারিতা বাস্তবায়ন করে।

// Modifier.Node
private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() {
    override fun ContentDrawScope.draw() {
        drawCircle(color)
    }
}

এই উদাহরণে, এটি মডিফায়ার ফাংশনে রঙটি প্রবেশ করানোর মাধ্যমে বৃত্তটি আঁকে।

একটি নোড Modifier.Node এবং শূন্য বা তার বেশি নোড টাইপ প্রয়োগ করে। আপনার মডিফায়ারের প্রয়োজনীয় কার্যকারিতার উপর ভিত্তি করে বিভিন্ন নোড টাইপ রয়েছে। পূর্ববর্তী উদাহরণটি অঙ্কন করতে সক্ষম হওয়া প্রয়োজন, তাই এটি DrawModifierNode প্রয়োগ করে, যা এটি draw পদ্ধতিকে ওভাররাইড করতে দেয়।

উপলব্ধ প্রকারগুলি নিম্নরূপ:

নোড

ব্যবহার

নমুনা লিঙ্ক

LayoutModifierNode

একটি Modifier.Node যা এর মোড়ানো বিষয়বস্তু পরিমাপ এবং বিন্যাসের পদ্ধতি পরিবর্তন করে।

নমুনা

DrawModifierNode

একটি Modifier.Node যা লেআউটের স্থানের মধ্যে টানে।

নমুনা

CompositionLocalConsumerModifierNode

এই ইন্টারফেসটি বাস্তবায়ন করলে আপনার Modifier.Node লোকাল কম্পোজিশন পড়তে পারবে।

নমুনা

SemanticsModifierNode

একটি Modifier.Node যা পরীক্ষা, অ্যাক্সেসিবিলিটি এবং অনুরূপ ব্যবহারের ক্ষেত্রে ব্যবহারের জন্য শব্দার্থবিদ্যা কী/মান যোগ করে।

নমুনা

PointerInputModifierNode

একটি Modifier.Node যা PointerInputChanges গ্রহণ করে।

নমুনা

ParentDataModifierNode

একটি Modifier.Node যা প্যারেন্ট লেআউটে ডেটা সরবরাহ করে।

নমুনা

LayoutAwareModifierNode

একটি Modifier.Node যা onMeasured এবং onPlaced কলব্যাক গ্রহণ করে।

নমুনা

GlobalPositionAwareModifierNode

একটি Modifier.Node যা কন্টেন্টের গ্লোবাল অবস্থান পরিবর্তিত হলে লেআউটের চূড়ান্ত LayoutCoordinates সহ একটি onGloballyPositioned কলব্যাক গ্রহণ করে।

নমুনা

ObserverModifierNode

Modifier.Node যারা ObserverNode বাস্তবায়ন করে তারা onObservedReadsChanged এর নিজস্ব বাস্তবায়ন প্রদান করতে পারে যা একটি observeReads ব্লকের মধ্যে পঠিত স্ন্যাপশট বস্তুর পরিবর্তনের প্রতিক্রিয়া হিসাবে ডাকা হবে।

নমুনা

DelegatingNode

একটি Modifier.Node যা অন্যান্য Modifier.Node ইনস্ট্যান্সে কাজ অর্পণ করতে সক্ষম।

এটি একাধিক নোড বাস্তবায়নকে একটিতে রচনা করতে কার্যকর হতে পারে।

নমুনা

TraversableNode

Modifier.Node ক্লাসগুলিকে একই ধরণের ক্লাসের জন্য বা একটি নির্দিষ্ট কী-এর জন্য নোড ট্রির উপরে/নিচে যাওয়ার অনুমতি দেয়।

নমুনা

নোডগুলি যখন তাদের সংশ্লিষ্ট উপাদানে আপডেট ডাকা হয় তখন স্বয়ংক্রিয়ভাবে অবৈধ হয়ে যায়। যেহেতু আমাদের উদাহরণটি একটি DrawModifierNode , তাই উপাদানটিতে যেকোনো সময় আপডেট ডাকা হলে, নোডটি একটি redraw ট্রিগার করে এবং এর রঙ সঠিকভাবে আপডেট হয়। স্বয়ংক্রিয়-অবৈধকরণ থেকে অপ্ট আউট করা সম্ভব, যেমনটি নোড স্বয়ংক্রিয়-অবৈধকরণ বিভাগে বিস্তারিতভাবে বর্ণনা করা হয়েছে।

ModifierNodeElement

একটি ModifierNodeElement হল একটি অপরিবর্তনীয় ক্লাস যা আপনার কাস্টম মডিফায়ার তৈরি বা আপডেট করার জন্য ডেটা ধারণ করে:

// ModifierNodeElement
private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create() = CircleNode(color)

    override fun update(node: CircleNode) {
        node.color = color
    }
}

ModifierNodeElement বাস্তবায়নের জন্য নিম্নলিখিত পদ্ধতিগুলি ওভাররাইড করতে হবে:

  1. create : এটি হল সেই ফাংশন যা আপনার মডিফায়ার নোডকে ইনস্ট্যান্টিয়েট করে। যখন আপনার মডিফায়ার প্রথম প্রয়োগ করা হয় তখন এটি নোড তৈরি করার জন্য ডাকা হয়। সাধারণত, এটি নোডটি তৈরি করে এবং মডিফায়ার ফ্যাক্টরিতে পাস করা প্যারামিটারগুলির সাথে এটি কনফিগার করে।
  2. update : এই ফাংশনটি তখনই কল করা হয় যখন এই মডিফায়ারটি একই স্থানে দেওয়া হয় যেখানে এই নোডটি ইতিমধ্যেই বিদ্যমান, কিন্তু একটি বৈশিষ্ট্য পরিবর্তিত হয়েছে। এটি ক্লাসের equals পদ্ধতি দ্বারা নির্ধারিত হয়। পূর্বে তৈরি করা মডিফায়ার নোডটি update কলে একটি প্যারামিটার হিসাবে পাঠানো হয়। এই মুহুর্তে, আপনার নোডের বৈশিষ্ট্যগুলি আপডেট করা প্যারামিটারগুলির সাথে সামঞ্জস্যপূর্ণভাবে আপডেট করা উচিত। এইভাবে নোডগুলির পুনঃব্যবহারের ক্ষমতা Modifier.Node যে কর্মক্ষমতা অর্জন করে তার মূল চাবিকাঠি; অতএব, update পদ্ধতিতে একটি নতুন নোড তৈরি করার পরিবর্তে আপনাকে বিদ্যমান নোডটি আপডেট করতে হবে। আমাদের সার্কেল উদাহরণে, নোডের রঙ আপডেট করা হয়েছে।

অতিরিক্তভাবে, ModifierNodeElement বাস্তবায়নের জন্য equals এবং hashCode প্রয়োগ করতে হবে। update কেবল তখনই কল করা হবে যদি পূর্ববর্তী উপাদানের সাথে equals তুলনা মিথ্যা ফেরত দেয়।

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

মডিফায়ার কারখানা

এটি আপনার মডিফায়ারের পাবলিক API সারফেস। বেশিরভাগ বাস্তবায়ন মডিফায়ার উপাদান তৈরি করে এবং এটি মডিফায়ার চেইনে যোগ করে:

// Modifier factory
fun Modifier.circle(color: Color) = this then CircleElement(color)

সম্পূর্ণ উদাহরণ

এই তিনটি অংশ একত্রিত হয়ে Modifier.Node API ব্যবহার করে একটি বৃত্ত আঁকার জন্য কাস্টম মডিফায়ার তৈরি করে:

// Modifier factory
fun Modifier.circle(color: Color) = this then CircleElement(color)

// ModifierNodeElement
private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create() = CircleNode(color)

    override fun update(node: CircleNode) {
        node.color = color
    }
}

// Modifier.Node
private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() {
    override fun ContentDrawScope.draw() {
        drawCircle(color)
    }
}

Modifier.Node ব্যবহার করে সাধারণ পরিস্থিতি

Modifier.Node দিয়ে কাস্টম মডিফায়ার তৈরি করার সময়, এখানে কিছু সাধারণ পরিস্থিতির সম্মুখীন হতে পারেন।

শূন্য পরামিতি

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

fun Modifier.fixedPadding() = this then FixedPaddingElement

data object FixedPaddingElement : ModifierNodeElement<FixedPaddingNode>() {
    override fun create() = FixedPaddingNode()
    override fun update(node: FixedPaddingNode) {}
}

class FixedPaddingNode : LayoutModifierNode, Modifier.Node() {
    private val PADDING = 16.dp

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        val paddingPx = PADDING.roundToPx()
        val horizontal = paddingPx * 2
        val vertical = paddingPx * 2

        val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))

        val width = constraints.constrainWidth(placeable.width + horizontal)
        val height = constraints.constrainHeight(placeable.height + vertical)
        return layout(width, height) {
            placeable.place(paddingPx, paddingPx)
        }
    }
}

রেফারেন্স রচনা স্থানীয়রা

Modifier.Node মডিফায়ারগুলি CompositionLocal এর মতো Compose state অবজেক্টের পরিবর্তনগুলি স্বয়ংক্রিয়ভাবে পর্যবেক্ষণ করে না। Modifier.Node মডিফায়ারগুলির সুবিধা হল যে তারা আপনার UI ট্রিতে যেখানে মডিফায়ার ব্যবহার করা হয়েছে সেখান থেকে স্থানীয় কম্পোজিশনের মান পড়তে পারে, যেখানে currentValueOf বরাদ্দ করা হয়েছে সেখান থেকে নয়।

তবে, মডিফায়ার নোড ইনস্ট্যান্সগুলি স্বয়ংক্রিয়ভাবে অবস্থার পরিবর্তনগুলি পর্যবেক্ষণ করে না। স্থানীয় পরিবর্তনের জন্য স্বয়ংক্রিয়ভাবে প্রতিক্রিয়া জানাতে, আপনি একটি স্কোপের ভিতরে এর বর্তমান মানটি পড়তে পারেন:

এই উদাহরণে LocalContentColor এর মান পর্যবেক্ষণ করে রঙের উপর ভিত্তি করে একটি ব্যাকগ্রাউন্ড আঁকতে হয়। ContentDrawScope যেমন স্ন্যাপশটের পরিবর্তনগুলি পর্যবেক্ষণ করে, LocalContentColor এর মান পরিবর্তন হলে এটি স্বয়ংক্রিয়ভাবে পুনরায় অঙ্কন করে:

class BackgroundColorConsumerNode :
    Modifier.Node(),
    DrawModifierNode,
    CompositionLocalConsumerModifierNode {
    override fun ContentDrawScope.draw() {
        val currentColor = currentValueOf(LocalContentColor)
        drawRect(color = currentColor)
        drawContent()
    }
}

স্কোপের বাইরে অবস্থার পরিবর্তনের প্রতিক্রিয়া জানাতে এবং আপনার মডিফায়ার স্বয়ংক্রিয়ভাবে আপডেট করতে, একটি ObserverModifierNode ব্যবহার করুন।

উদাহরণস্বরূপ, Modifier.scrollable LocalDensity এর পরিবর্তনগুলি পর্যবেক্ষণ করার জন্য এই কৌশলটি ব্যবহার করে। নিম্নলিখিত উদাহরণে একটি সরলীকৃত উদাহরণ দেখানো হয়েছে:

class ScrollableNode :
    Modifier.Node(),
    ObserverModifierNode,
    CompositionLocalConsumerModifierNode {

    // Place holder fling behavior, we'll initialize it when the density is available.
    val defaultFlingBehavior = DefaultFlingBehavior(splineBasedDecay(UnityDensity))

    override fun onAttach() {
        updateDefaultFlingBehavior()
        observeReads { currentValueOf(LocalDensity) } // monitor change in Density
    }

    override fun onObservedReadsChanged() {
        // if density changes, update the default fling behavior.
        updateDefaultFlingBehavior()
    }

    private fun updateDefaultFlingBehavior() {
        val density = currentValueOf(LocalDensity)
        defaultFlingBehavior.flingDecay = splineBasedDecay(density)
    }
}

একটি মডিফায়ার অ্যানিমেট করুন

Modifier.Node বাস্তবায়নের জন্য একটি coroutineScope অ্যাক্সেস থাকে। এটি Compose Animatable API ব্যবহারের অনুমতি দেয়। উদাহরণস্বরূপ, এই স্নিপেটটি পূর্বে দেখানো CircleNode বারবার ফেইড ইন এবং আউট করার জন্য পরিবর্তন করে:

class CircleNode(var color: Color) : Modifier.Node(), DrawModifierNode {
    private lateinit var alpha: Animatable<Float, AnimationVector1D>

    override fun ContentDrawScope.draw() {
        drawCircle(color = color, alpha = alpha.value)
        drawContent()
    }

    override fun onAttach() {
        alpha = Animatable(1f)
        coroutineScope.launch {
            alpha.animateTo(
                0f,
                infiniteRepeatable(tween(1000), RepeatMode.Reverse)
            ) {
            }
        }
    }
}

ডেলিগেশন ব্যবহার করে মডিফায়ারের মধ্যে অবস্থা ভাগ করুন

Modifier.Node modifiers অন্যান্য নোডের কাছে ডেলিগেট করতে পারে। এর জন্য অনেকগুলি ব্যবহারের উদাহরণ রয়েছে, যেমন বিভিন্ন modifiers জুড়ে সাধারণ বাস্তবায়নগুলি বের করা, তবে এটি modifiers জুড়ে সাধারণ অবস্থা ভাগ করে নেওয়ার জন্যও ব্যবহার করা যেতে পারে।

উদাহরণস্বরূপ, একটি ক্লিকযোগ্য মডিফায়ার নোডের একটি মৌলিক বাস্তবায়ন যা ইন্টারঅ্যাকশন ডেটা ভাগ করে:

class ClickableNode : DelegatingNode() {
    val interactionData = InteractionData()
    val focusableNode = delegate(
        FocusableNode(interactionData)
    )
    val indicationNode = delegate(
        IndicationNode(interactionData)
    )
}

নোডের স্বয়ংক্রিয়-বৈধতা থেকে বেরিয়ে আসুন

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

এটি বিশেষভাবে কার্যকর যদি আপনার কাস্টম মডিফায়ার লেআউট এবং ড্র উভয়ই পরিবর্তন করে। স্বয়ংক্রিয়-অবৈধকরণ থেকে বেরিয়ে আসার মাধ্যমে আপনি কেবল তখনই ড্র বাতিল করতে পারবেন যখন শুধুমাত্র ড্র-সম্পর্কিত বৈশিষ্ট্য, যেমন color , পরিবর্তিত হয়। এটি লেআউট বাতিল করা এড়ায় এবং আপনার মডিফায়ারের কর্মক্ষমতা উন্নত করতে পারে।

এর একটি কাল্পনিক উদাহরণ নিম্নলিখিত উদাহরণে দেখানো হয়েছে একটি মডিফায়ারের সাথে যার বৈশিষ্ট্য হল color , size , এবং onClick lambda। এই মডিফায়ার শুধুমাত্র প্রয়োজনীয় জিনিসগুলিকেই অবৈধ করে, যেকোনো অপ্রয়োজনীয় অবৈধতা এড়িয়ে যায়:

class SampleInvalidatingNode(
    var color: Color,
    var size: IntSize,
    var onClick: () -> Unit
) : DelegatingNode(), LayoutModifierNode, DrawModifierNode {
    override val shouldAutoInvalidate: Boolean
        get() = false

    private val clickableNode = delegate(
        ClickablePointerInputNode(onClick)
    )

    fun update(color: Color, size: IntSize, onClick: () -> Unit) {
        if (this.color != color) {
            this.color = color
            // Only invalidate draw when color changes
            invalidateDraw()
        }

        if (this.size != size) {
            this.size = size
            // Only invalidate layout when size changes
            invalidateMeasurement()
        }

        // If only onClick changes, we don't need to invalidate anything
        clickableNode.update(onClick)
    }

    override fun ContentDrawScope.draw() {
        drawRect(color)
    }

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        val size = constraints.constrain(size)
        val placeable = measurable.measure(constraints)
        return layout(size.width, size.height) {
            placeable.place(0, 0)
        }
    }
}