জেটপ্যাক রচনা পর্যায়গুলি

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

কম্পোজ এবং স্টেটে চিন্তাভাবনা এবং জেটপ্যাক কম্পোজ সহ আমাদের রচনা ডক্স জুড়ে রচনাটি বর্ণনা করা হয়েছে।

একটি ফ্রেমের তিনটি পর্যায়

রচনার তিনটি প্রধান পর্যায় রয়েছে:

  1. রচনা : কি UI দেখাতে হবে। রচনা কম্পোজযোগ্য ফাংশন চালায় এবং আপনার UI এর একটি বিবরণ তৈরি করে।
  2. লেআউট : UI কোথায় রাখবেন। এই পর্বটি দুটি ধাপ নিয়ে গঠিত: পরিমাপ এবং বসানো। লেআউট উপাদানগুলি পরিমাপ করে এবং লেআউট ট্রিতে প্রতিটি নোডের জন্য 2D স্থানাঙ্কে নিজেদের এবং যেকোনো শিশু উপাদানকে রাখে।
  3. অঙ্কন : এটি কিভাবে রেন্ডার করে। UI উপাদানগুলি একটি ক্যানভাসে আঁকা হয়, সাধারণত একটি ডিভাইসের পর্দা।
তিনটি পর্যায়ের একটি চিত্র যেখানে রচনা ডেটাকে UI এ রূপান্তরিত করে (ক্রম অনুসারে, ডেটা, রচনা, বিন্যাস, অঙ্কন, UI)।
চিত্র 1. যে তিনটি ধাপে কম্পোজ ডেটাকে UI-তে রূপান্তরিত করে।

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

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

পর্যায়গুলি বুঝুন

এই বিভাগটি বর্ণনা করে যে কীভাবে কম্পোজেবলের জন্য তিনটি রচনা পর্যায় আরো বিস্তারিতভাবে সম্পাদিত হয়।

রচনা

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

চিত্র 2. আপনার UI প্রতিনিধিত্বকারী ট্রি যা রচনা পর্বে তৈরি করা হয়েছে।

কোড এবং UI গাছের একটি উপবিভাগ নিম্নলিখিত মত দেখায়:

পাঁচটি কম্পোজেবল এবং ফলস্বরূপ UI ট্রি সহ একটি কোড স্নিপেট, যার মধ্যে চাইল্ড নোডগুলি তাদের প্যারেন্ট নোড থেকে শাখা হয়৷
চিত্র 3. সংশ্লিষ্ট কোড সহ একটি UI গাছের একটি উপবিভাগ।

এই উদাহরণগুলিতে, কোডের প্রতিটি কম্পোজযোগ্য ফাংশন UI ট্রিতে একটি একক লেআউট নোডে ম্যাপ করে। আরও জটিল উদাহরণে, কম্পোজেবলে যুক্তি এবং নিয়ন্ত্রণ প্রবাহ থাকতে পারে এবং বিভিন্ন রাজ্যে একটি ভিন্ন গাছ তৈরি করতে পারে।

লেআউট

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

চিত্র 4. লেআউট পর্বের সময় UI ট্রিতে প্রতিটি লেআউট নোডের পরিমাপ এবং স্থাপন।

লেআউট পর্বের সময়, নিম্নলিখিত তিনটি ধাপের অ্যালগরিদম ব্যবহার করে গাছটি অতিক্রম করা হয়:

  1. শিশুদের পরিমাপ করুন : একটি নোড তার শিশুদের পরিমাপ করে যদি থাকে।
  2. নিজের আকার নির্ধারণ করুন : এই পরিমাপের উপর ভিত্তি করে, একটি নোড তার নিজস্ব আকার নির্ধারণ করে।
  3. শিশুদের রাখুন : প্রতিটি চাইল্ড নোড একটি নোডের নিজস্ব অবস্থানের সাপেক্ষে স্থাপন করা হয়।

এই পর্যায়ের শেষে, প্রতিটি লেআউট নোড আছে:

  • একটি নির্ধারিত প্রস্থ এবং উচ্চতা
  • একটি x, y স্থানাঙ্ক যেখানে এটি আঁকা উচিত

পূর্ববর্তী বিভাগ থেকে UI ট্রি স্মরণ করুন:

পাঁচটি কম্পোজেবল এবং ফলস্বরূপ UI ট্রি সহ একটি কোড স্নিপেট, চাইল্ড নোডগুলি তাদের প্যারেন্ট নোড থেকে শাখাযুক্ত

এই গাছের জন্য, অ্যালগরিদম নিম্নরূপ কাজ করে:

  1. Row তার সন্তান, Image এবং Column পরিমাপ করে।
  2. Image পরিমাপ করা হয়। এটির কোনো সন্তান নেই, তাই এটি নিজের আকার নির্ধারণ করে এবং Row আকারটি রিপোর্ট করে।
  3. Column পরবর্তী পরিমাপ করা হয়. এটি প্রথমে তার নিজের বাচ্চাদের পরিমাপ করে (দুটি Text কম্পোজেবল)।
  4. প্রথম Text পরিমাপ করা হয়. এটির কোনো সন্তান নেই তাই এটি নিজের আকার নির্ধারণ করে এবং Column তার আকারের প্রতিবেদন করে।
    1. দ্বিতীয় Text পরিমাপ করা হয়. এটির কোন সন্তান নেই তাই এটি নিজের আকার নির্ধারণ করে এবং Column এটিকে রিপোর্ট করে৷
  5. Column তার নিজের আকার নির্ধারণ করতে চাইল্ড পরিমাপ ব্যবহার করে। এটি সর্বাধিক চাইল্ড প্রস্থ এবং এর বাচ্চাদের উচ্চতার সমষ্টি ব্যবহার করে।
  6. Column তার সন্তানদেরকে নিজের সাপেক্ষে রাখে, তাদের একে অপরের নীচে উল্লম্বভাবে রাখে।
  7. Row তার নিজের আকার নির্ধারণ করতে চাইল্ড পরিমাপ ব্যবহার করে। এটি সর্বোচ্চ শিশু উচ্চতা এবং এর শিশুদের প্রস্থের সমষ্টি ব্যবহার করে। এটি তারপর তার সন্তানদের রাখে।

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

অঙ্কন

অঙ্কন পর্বে, গাছটি আবার উপরে থেকে নিচ পর্যন্ত অতিক্রম করা হয় এবং প্রতিটি নোড পালাক্রমে পর্দায় নিজেকে আঁকতে থাকে।

চিত্র 5. অঙ্কন পর্ব পর্দায় পিক্সেল আঁকে।

পূর্ববর্তী উদাহরণ ব্যবহার করে, গাছের বিষয়বস্তু নিম্নলিখিত উপায়ে আঁকা হয়েছে:

  1. Row এটিতে থাকতে পারে এমন যেকোনো বিষয়বস্তু আঁকে, যেমন একটি পটভূমির রঙ।
  2. Image নিজেই আঁকে।
  3. Column নিজেই আঁকা.
  4. প্রথম এবং দ্বিতীয় Text যথাক্রমে নিজেদের আঁকা।

চিত্র 6. একটি UI গাছ এবং এর আঁকা উপস্থাপনা।

রাজ্য পড়ে

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

রাজ্যটি সাধারণত mutableStateOf() ব্যবহার করে তৈরি করা হয় এবং তারপরে দুটি উপায়ের মধ্যে একটির মাধ্যমে অ্যাক্সেস করা হয়: সরাসরি value সম্পত্তি অ্যাক্সেস করে, অথবা বিকল্পভাবে একটি Kotlin সম্পত্তি প্রতিনিধি ব্যবহার করে। আপনি কম্পোজেবল রাজ্যে তাদের সম্পর্কে আরও পড়তে পারেন। এই গাইডের উদ্দেশ্যে, একটি "স্টেট রিড" সেই সমতুল্য অ্যাক্সেস পদ্ধতিগুলির যে কোনো একটিকে বোঝায়।

// State read without property delegate.
val paddingState: MutableState<Dp> = remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(paddingState.value)
)

// State read with property delegate.
var padding: Dp by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(padding)
)

সম্পত্তি প্রতিনিধির হুডের অধীনে, "গেটার" এবং "সেটার" ফাংশনগুলি রাজ্যের value অ্যাক্সেস এবং আপডেট করতে ব্যবহৃত হয়। এই গেটার এবং সেটার ফাংশনগুলি শুধুমাত্র তখনই ব্যবহার করা হয় যখন আপনি সম্পত্তিটিকে একটি মান হিসাবে উল্লেখ করেন, এবং যখন এটি তৈরি করা হয় তখন নয়, যার কারণে উপরের দুটি উপায় সমতুল্য।

কোডের প্রতিটি ব্লক যা রি-এক্সিকিউট করা যেতে পারে যখন একটি রিড স্টেট পরিবর্তন হয় একটি রিস্টার্ট স্কোপ । কম্পোজ রাষ্ট্রীয় মান পরিবর্তনের ট্র্যাক রাখে এবং বিভিন্ন ধাপে সুযোগগুলি পুনরায় চালু করে।

পর্যায়ক্রমে রাষ্ট্র পড়ে

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

আসুন প্রতিটি পর্বের মধ্য দিয়ে যান এবং যখন একটি রাষ্ট্রীয় মান এটির মধ্যে পড়া হয় তখন কী ঘটে তা বর্ণনা করি।

পর্যায় 1: রচনা

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

রচনার ফলাফলের উপর নির্ভর করে, রচনা UI লেআউট এবং অঙ্কন পর্যায়গুলি চালায়। বিষয়বস্তু একই থাকে এবং আকার এবং বিন্যাস পরিবর্তন না হলে এটি এই পর্যায়গুলি এড়িয়ে যেতে পারে।

var padding by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    // The `padding` state is read in the composition phase
    // when the modifier is constructed.
    // Changes in `padding` will invoke recomposition.
    modifier = Modifier.padding(padding)
)

পর্যায় 2: লেআউট

লেআউট ফেজ দুটি ধাপ নিয়ে গঠিত: পরিমাপ এবং বসানো । পরিমাপের ধাপটি Layout কম্পোজেবলে পাস করা পরিমাপ ল্যাম্বডা, LayoutModifier ইন্টারফেসের MeasureScope.measure পদ্ধতি এবং আরও অনেক কিছু চালায়। প্লেসমেন্ট ধাপটি layout ফাংশনের প্লেসমেন্ট ব্লক, Modifier.offset { … } , ইত্যাদি চালায়।

এই প্রতিটি ধাপে স্টেট রিড লেআউট এবং সম্ভাব্য অঙ্কন পর্বকে প্রভাবিত করে। যখন রাষ্ট্রের মান পরিবর্তিত হয়, তখন রচনা UI লেআউট পর্বের সময়সূচী নির্ধারণ করে। আকার বা অবস্থান পরিবর্তিত হলে এটি অঙ্কন পর্বও চালায়।

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

var offsetX by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.offset {
        // The `offsetX` state is read in the placement step
        // of the layout phase when the offset is calculated.
        // Changes in `offsetX` restart the layout.
        IntOffset(offsetX.roundToPx(), 0)
    }
)

পর্যায় 3: অঙ্কন

অঙ্কন কোডের সময় রাজ্য পড়া অঙ্কন পর্বকে প্রভাবিত করে। সাধারণ উদাহরণের মধ্যে রয়েছে Canvas() , Modifier.drawBehind , এবং Modifier.drawWithContent । যখন রাষ্ট্রের মান পরিবর্তিত হয়, কম্পোজ UI শুধুমাত্র ড্র ফেজ চালায়।

var color by remember { mutableStateOf(Color.Red) }
Canvas(modifier = modifier) {
    // The `color` state is read in the drawing phase
    // when the canvas is rendered.
    // Changes in `color` restart the drawing.
    drawRect(color)
}

অপ্টিমাইজিং স্টেট রিড

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

চলুন একটি উদাহরণ কটাক্ষপাত করা যাক. এখানে আমাদের কাছে একটি Image() রয়েছে যা অফসেট মডিফায়ার ব্যবহার করে এর চূড়ান্ত লেআউট অবস্থান অফসেট করে, যার ফলে ব্যবহারকারী স্ক্রোল করার সাথে সাথে একটি প্যারালাক্স প্রভাব তৈরি করে।

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        // Non-optimal implementation!
        Modifier.offset(
            with(LocalDensity.current) {
                // State read of firstVisibleItemScrollOffset in composition
                (listState.firstVisibleItemScrollOffset / 2).toDp()
            }
        )
    )

    LazyColumn(state = listState) {
        // ...
    }
}

এই কোড কাজ করে, কিন্তু ফলাফল অ-অনুকূল কর্মক্ষমতা. লেখার মতো, কোডটি firstVisibleItemScrollOffset অবস্থার মান পড়ে এবং এটি Modifier.offset(offset: Dp) ফাংশনে পাস করে। ব্যবহারকারী স্ক্রোল করার সাথে সাথে firstVisibleItemScrollOffset মান পরিবর্তন হবে। আমরা জানি, কম্পোজ যেকোনো স্টেট রিড ট্র্যাক করে যাতে এটি রিডিং কোড রিস্টার্ট (পুনরায় আহ্বান) করতে পারে, যা আমাদের উদাহরণে Box বিষয়বস্তু।

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

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

অফসেট মডিফায়ারের আরেকটি সংস্করণ উপলব্ধ রয়েছে: Modifier.offset(offset: Density.() -> IntOffset)

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

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        Modifier.offset {
            // State read of firstVisibleItemScrollOffset in Layout
            IntOffset(x = 0, y = listState.firstVisibleItemScrollOffset / 2)
        }
    )

    LazyColumn(state = listState) {
        // ...
    }
}

তাহলে কেন এই বেশি পারফরম্যান্স? আমরা মডিফায়ারকে যে ল্যাম্বডা ব্লক প্রদান করি সেটি লেআউট পর্বের সময় (বিশেষত, লেআউট পর্বের প্লেসমেন্ট ধাপের সময়) আহ্বান করা হয়, যার অর্থ আমাদের firstVisibleItemScrollOffset অবস্থাটি রচনার সময় আর পড়া হয় না। যেহেতু কম্পোজ ট্র্যাক যখন স্টেট রিড করা হয়, এই পরিবর্তনের মানে হল যদি firstVisibleItemScrollOffset মান পরিবর্তন হয়, কম্পোজকে শুধুমাত্র লেআউট এবং অঙ্কন পর্যায়গুলি পুনরায় চালু করতে হবে।

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

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

পুনর্গঠন লুপ (চক্রীয় পর্যায় নির্ভরতা)

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

Box {
    var imageHeightPx by remember { mutableStateOf(0) }

    Image(
        painter = painterResource(R.drawable.rectangle),
        contentDescription = "I'm above the text",
        modifier = Modifier
            .fillMaxWidth()
            .onSizeChanged { size ->
                // Don't do this
                imageHeightPx = size.height
            }
    )

    Text(
        text = "I'm below the image",
        modifier = Modifier.padding(
            top = with(LocalDensity.current) { imageHeightPx.toDp() }
        )
    )
}

এখানে আমরা (খারাপভাবে) একটি উল্লম্ব কলাম প্রয়োগ করেছি, যার উপরের চিত্রটি রয়েছে এবং তারপরে এটির নীচে পাঠ্য রয়েছে। আমরা চিত্রটির সমাধানকৃত আকার জানতে Modifier.onSizeChanged() ব্যবহার করছি এবং তারপরে এটিকে নামানোর জন্য টেক্সটে Modifier.padding() ব্যবহার করছি। Px থেকে Dp এ ফিরে আসা অস্বাভাবিক রূপান্তর ইতিমধ্যেই নির্দেশ করে যে কোডটিতে কিছু সমস্যা আছে।

এই উদাহরণের সাথে সমস্যা হল যে আমরা একটি একক ফ্রেমের মধ্যে "চূড়ান্ত" লেআউটে পৌঁছাই না। কোডটি ঘটতে থাকা একাধিক ফ্রেমের উপর নির্ভর করে, যা অপ্রয়োজনীয় কাজ করে এবং এর ফলে ব্যবহারকারীর জন্য UI স্ক্রিনে ঘুরে বেড়ায়।

কি ঘটছে তা দেখতে প্রতিটি ফ্রেমের মধ্য দিয়ে ধাপে ধাপে চলুন:

প্রথম ফ্রেমের রচনা পর্যায়ে, imageHeightPx এর মান 0 এবং পাঠ্যটি Modifier.padding(top = 0) দিয়ে দেওয়া হয়। তারপরে, বিন্যাস পর্যায়টি অনুসরণ করে, এবং onSizeChanged সংশোধকের জন্য কলব্যাক বলা হয়। এটি হল যখন imageHeightPx ছবিটির প্রকৃত উচ্চতায় আপডেট করা হয়। পরবর্তী ফ্রেমের জন্য সময়সূচী পুনর্গঠন রচনা করুন। অঙ্কন পর্যায়ে, টেক্সটটি 0 এর প্যাডিং দিয়ে রেন্ডার করা হয় যেহেতু মান পরিবর্তন এখনও প্রতিফলিত হয়নি।

রচনা তারপর imageHeightPx এর মান পরিবর্তন দ্বারা নির্ধারিত দ্বিতীয় ফ্রেম শুরু করে। রাজ্যটি বক্স বিষয়বস্তু ব্লকে পড়া হয়, এবং এটি রচনা পর্বে আহ্বান করা হয়। এই সময়, পাঠ্যটি চিত্রের উচ্চতার সাথে মেলে একটি প্যাডিং সহ দেওয়া হয়েছে। লেআউট পর্যায়ে, কোডটি আবার imageHeightPx এর মান সেট করে, কিন্তু মান একই থাকে বলে কোনো পুনর্গঠন নির্ধারিত হয় না।

শেষ পর্যন্ত, আমরা পাঠ্যের উপর পছন্দসই প্যাডিং পাই, কিন্তু প্যাডিং মানটিকে একটি ভিন্ন পর্যায়ে ফেরাতে অতিরিক্ত ফ্রেম ব্যয় করা অনুকূল নয় এবং এর ফলে ওভারল্যাপিং সামগ্রী সহ একটি ফ্রেম তৈরি হবে৷

এই উদাহরণটি কল্পিত মনে হতে পারে, তবে এই সাধারণ প্যাটার্নটি সম্পর্কে সতর্ক থাকুন:

  • Modifier.onSizeChanged() , onGloballyPositioned() , বা অন্য কিছু লেআউট অপারেশন
  • কিছু রাষ্ট্র আপডেট
  • লেআউট মডিফায়ারে ইনপুট হিসাবে সেই অবস্থাটি ব্যবহার করুন ( padding() , height() , বা অনুরূপ)
  • সম্ভাব্য পুনরাবৃত্তি

উপরের নমুনার জন্য সমাধান হল সঠিক বিন্যাস আদিম ব্যবহার করা। উপরের উদাহরণটি একটি সাধারণ Column() দিয়ে প্রয়োগ করা যেতে পারে, তবে আপনার কাছে আরও জটিল উদাহরণ থাকতে পারে যার জন্য কাস্টম কিছু প্রয়োজন, যার জন্য একটি কাস্টম লেআউট লেখার প্রয়োজন হবে। আরও তথ্যের জন্য কাস্টম লেআউট গাইড দেখুন।

এখানে সাধারণ নীতি হল একাধিক UI উপাদানগুলির জন্য সত্যের একটি একক উত্স থাকা যা একে অপরের সাথে পরিমাপ করা এবং স্থাপন করা উচিত। একটি সঠিক বিন্যাস আদিম ব্যবহার করা বা একটি কাস্টম বিন্যাস তৈরি করার অর্থ হল ন্যূনতম ভাগ করা অভিভাবক সত্যের উত্স হিসাবে কাজ করে যা একাধিক উপাদানের মধ্যে সম্পর্ককে সমন্বয় করতে পারে। একটি গতিশীল রাষ্ট্র প্রবর্তন এই নীতি ভঙ্গ করে.

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %} ,

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

কম্পোজ এবং স্টেটে চিন্তাভাবনা এবং জেটপ্যাক কম্পোজ সহ আমাদের রচনা ডক্স জুড়ে রচনাটি বর্ণনা করা হয়েছে।

একটি ফ্রেমের তিনটি পর্যায়

রচনার তিনটি প্রধান পর্যায় রয়েছে:

  1. রচনা : কি UI দেখাতে হবে। রচনা কম্পোজযোগ্য ফাংশন চালায় এবং আপনার UI এর একটি বিবরণ তৈরি করে।
  2. লেআউট : UI কোথায় রাখবেন। এই পর্বটি দুটি ধাপ নিয়ে গঠিত: পরিমাপ এবং বসানো। লেআউট উপাদানগুলি পরিমাপ করে এবং লেআউট ট্রিতে প্রতিটি নোডের জন্য 2D স্থানাঙ্কে নিজেদের এবং যেকোনো শিশু উপাদানকে রাখে।
  3. অঙ্কন : এটি কিভাবে রেন্ডার করে। UI উপাদানগুলি একটি ক্যানভাসে আঁকা হয়, সাধারণত একটি ডিভাইসের পর্দা।
তিনটি পর্যায়ের একটি চিত্র যেখানে রচনা ডেটাকে UI এ রূপান্তরিত করে (ক্রম অনুসারে, ডেটা, রচনা, বিন্যাস, অঙ্কন, UI)।
চিত্র 1. যে তিনটি ধাপে কম্পোজ ডেটাকে UI-তে রূপান্তরিত করে।

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

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

পর্যায়গুলি বুঝুন

এই বিভাগটি বর্ণনা করে যে কীভাবে কম্পোজেবলের জন্য তিনটি রচনা পর্যায় আরো বিস্তারিতভাবে সম্পাদিত হয়।

রচনা

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

চিত্র 2. আপনার UI প্রতিনিধিত্বকারী ট্রি যা রচনা পর্বে তৈরি করা হয়েছে।

কোড এবং UI গাছের একটি উপবিভাগ নিম্নলিখিত মত দেখায়:

পাঁচটি কম্পোজেবল এবং ফলস্বরূপ UI ট্রি সহ একটি কোড স্নিপেট, যার মধ্যে চাইল্ড নোডগুলি তাদের প্যারেন্ট নোড থেকে শাখা হয়৷
চিত্র 3. সংশ্লিষ্ট কোড সহ একটি UI গাছের একটি উপবিভাগ।

এই উদাহরণগুলিতে, কোডের প্রতিটি কম্পোজযোগ্য ফাংশন UI ট্রিতে একটি একক লেআউট নোডে ম্যাপ করে। আরও জটিল উদাহরণে, কম্পোজেবলে যুক্তি এবং নিয়ন্ত্রণ প্রবাহ থাকতে পারে এবং বিভিন্ন রাজ্যে একটি ভিন্ন গাছ তৈরি করতে পারে।

লেআউট

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

চিত্র 4. লেআউট পর্বের সময় UI ট্রিতে প্রতিটি লেআউট নোডের পরিমাপ এবং স্থাপন।

লেআউট পর্বের সময়, নিম্নলিখিত তিনটি ধাপের অ্যালগরিদম ব্যবহার করে গাছটি অতিক্রম করা হয়:

  1. শিশুদের পরিমাপ করুন : একটি নোড তার শিশুদের পরিমাপ করে যদি থাকে।
  2. নিজের আকার নির্ধারণ করুন : এই পরিমাপের উপর ভিত্তি করে, একটি নোড তার নিজস্ব আকার নির্ধারণ করে।
  3. শিশুদের রাখুন : প্রতিটি চাইল্ড নোড একটি নোডের নিজস্ব অবস্থানের সাপেক্ষে স্থাপন করা হয়।

এই পর্যায়ের শেষে, প্রতিটি লেআউট নোড আছে:

  • একটি নির্ধারিত প্রস্থ এবং উচ্চতা
  • একটি x, y স্থানাঙ্ক যেখানে এটি আঁকা উচিত

পূর্ববর্তী বিভাগ থেকে UI ট্রি স্মরণ করুন:

পাঁচটি কম্পোজেবল এবং ফলস্বরূপ UI ট্রি সহ একটি কোড স্নিপেট, চাইল্ড নোডগুলি তাদের প্যারেন্ট নোড থেকে শাখাযুক্ত

এই গাছের জন্য, অ্যালগরিদম নিম্নরূপ কাজ করে:

  1. Row তার সন্তান, Image এবং Column পরিমাপ করে।
  2. Image পরিমাপ করা হয়। এটির কোনো সন্তান নেই, তাই এটি নিজের আকার নির্ধারণ করে এবং Row আকারটি রিপোর্ট করে।
  3. Column পরবর্তী পরিমাপ করা হয়. এটি প্রথমে তার নিজের বাচ্চাদের পরিমাপ করে (দুটি Text কম্পোজেবল)।
  4. প্রথম Text পরিমাপ করা হয়. এটির কোনো সন্তান নেই তাই এটি নিজের আকার নির্ধারণ করে এবং Column তার আকারের প্রতিবেদন করে।
    1. দ্বিতীয় Text পরিমাপ করা হয়. এটির কোন সন্তান নেই তাই এটি নিজের আকার নির্ধারণ করে এবং Column এটিকে রিপোর্ট করে৷
  5. Column তার নিজের আকার নির্ধারণ করতে চাইল্ড পরিমাপ ব্যবহার করে। এটি সর্বাধিক চাইল্ড প্রস্থ এবং এর বাচ্চাদের উচ্চতার সমষ্টি ব্যবহার করে।
  6. Column তার সন্তানদেরকে নিজের সাপেক্ষে রাখে, তাদের একে অপরের নীচে উল্লম্বভাবে রাখে।
  7. Row তার নিজের আকার নির্ধারণ করতে চাইল্ড পরিমাপ ব্যবহার করে। এটি সর্বোচ্চ শিশু উচ্চতা এবং এর শিশুদের প্রস্থের সমষ্টি ব্যবহার করে। এটি তারপর তার সন্তানদের রাখে।

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

অঙ্কন

অঙ্কন পর্বে, গাছটি আবার উপরে থেকে নিচ পর্যন্ত অতিক্রম করা হয় এবং প্রতিটি নোড পালাক্রমে পর্দায় নিজেকে আঁকতে থাকে।

চিত্র 5. অঙ্কন পর্ব পর্দায় পিক্সেল আঁকে।

পূর্ববর্তী উদাহরণ ব্যবহার করে, গাছের বিষয়বস্তু নিম্নলিখিত উপায়ে আঁকা হয়েছে:

  1. Row এটিতে থাকতে পারে এমন যেকোনো বিষয়বস্তু আঁকে, যেমন একটি পটভূমির রঙ।
  2. Image নিজেই আঁকে।
  3. Column নিজেই আঁকা.
  4. প্রথম এবং দ্বিতীয় Text যথাক্রমে নিজেদের আঁকা।

চিত্র 6. একটি UI গাছ এবং এর আঁকা উপস্থাপনা।

রাজ্য পড়ে

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

রাজ্যটি সাধারণত mutableStateOf() ব্যবহার করে তৈরি করা হয় এবং তারপরে দুটি উপায়ের মধ্যে একটির মাধ্যমে অ্যাক্সেস করা হয়: সরাসরি value সম্পত্তি অ্যাক্সেস করে, অথবা বিকল্পভাবে একটি Kotlin সম্পত্তি প্রতিনিধি ব্যবহার করে। আপনি কম্পোজেবল রাজ্যে তাদের সম্পর্কে আরও পড়তে পারেন। এই গাইডের উদ্দেশ্যে, একটি "স্টেট রিড" সেই সমতুল্য অ্যাক্সেস পদ্ধতিগুলির যে কোনো একটিকে বোঝায়।

// State read without property delegate.
val paddingState: MutableState<Dp> = remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(paddingState.value)
)

// State read with property delegate.
var padding: Dp by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(padding)
)

সম্পত্তি প্রতিনিধির হুডের অধীনে, "গেটার" এবং "সেটার" ফাংশনগুলি রাজ্যের value অ্যাক্সেস এবং আপডেট করতে ব্যবহৃত হয়। এই গেটার এবং সেটার ফাংশনগুলি শুধুমাত্র তখনই ব্যবহার করা হয় যখন আপনি সম্পত্তিটিকে একটি মান হিসাবে উল্লেখ করেন, এবং যখন এটি তৈরি করা হয় তখন নয়, যার কারণে উপরের দুটি উপায় সমতুল্য।

কোডের প্রতিটি ব্লক যা রি-এক্সিকিউট করা যেতে পারে যখন একটি রিড স্টেট পরিবর্তন হয় একটি রিস্টার্ট স্কোপ । কম্পোজ রাষ্ট্রীয় মান পরিবর্তনের ট্র্যাক রাখে এবং বিভিন্ন ধাপে সুযোগগুলি পুনরায় চালু করে।

পর্যায়ক্রমে রাষ্ট্র পড়ে

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

আসুন প্রতিটি পর্বের মধ্য দিয়ে যান এবং যখন একটি রাষ্ট্রীয় মান এটির মধ্যে পড়া হয় তখন কী ঘটে তা বর্ণনা করি।

পর্যায় 1: রচনা

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

রচনার ফলাফলের উপর নির্ভর করে, রচনা UI লেআউট এবং অঙ্কন পর্যায়গুলি চালায়। বিষয়বস্তু একই থাকে এবং আকার এবং বিন্যাস পরিবর্তন না হলে এটি এই পর্যায়গুলি এড়িয়ে যেতে পারে।

var padding by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    // The `padding` state is read in the composition phase
    // when the modifier is constructed.
    // Changes in `padding` will invoke recomposition.
    modifier = Modifier.padding(padding)
)

পর্যায় 2: লেআউট

লেআউট ফেজ দুটি ধাপ নিয়ে গঠিত: পরিমাপ এবং বসানো । পরিমাপের ধাপটি Layout কম্পোজেবলে পাস করা পরিমাপ ল্যাম্বডা, LayoutModifier ইন্টারফেসের MeasureScope.measure পদ্ধতি এবং আরও অনেক কিছু চালায়। প্লেসমেন্ট ধাপটি layout ফাংশনের প্লেসমেন্ট ব্লক, Modifier.offset { … } , ইত্যাদি চালায়।

এই প্রতিটি ধাপে স্টেট রিড লেআউট এবং সম্ভাব্য অঙ্কন পর্বকে প্রভাবিত করে। যখন রাষ্ট্রের মান পরিবর্তিত হয়, তখন রচনা UI লেআউট পর্বের সময়সূচী নির্ধারণ করে। আকার বা অবস্থান পরিবর্তিত হলে এটি অঙ্কন পর্বও চালায়।

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

var offsetX by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.offset {
        // The `offsetX` state is read in the placement step
        // of the layout phase when the offset is calculated.
        // Changes in `offsetX` restart the layout.
        IntOffset(offsetX.roundToPx(), 0)
    }
)

পর্যায় 3: অঙ্কন

অঙ্কন কোডের সময় রাজ্য পড়া অঙ্কন পর্বকে প্রভাবিত করে। সাধারণ উদাহরণের মধ্যে রয়েছে Canvas() , Modifier.drawBehind , এবং Modifier.drawWithContent । যখন রাষ্ট্রের মান পরিবর্তিত হয়, কম্পোজ UI শুধুমাত্র ড্র ফেজ চালায়।

var color by remember { mutableStateOf(Color.Red) }
Canvas(modifier = modifier) {
    // The `color` state is read in the drawing phase
    // when the canvas is rendered.
    // Changes in `color` restart the drawing.
    drawRect(color)
}

অপ্টিমাইজিং স্টেট রিড

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

চলুন একটি উদাহরণ কটাক্ষপাত করা যাক. এখানে আমাদের কাছে একটি Image() রয়েছে যা অফসেট মডিফায়ার ব্যবহার করে এর চূড়ান্ত লেআউট অবস্থান অফসেট করে, যার ফলে ব্যবহারকারী স্ক্রোল করার সাথে সাথে একটি প্যারালাক্স প্রভাব তৈরি করে।

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        // Non-optimal implementation!
        Modifier.offset(
            with(LocalDensity.current) {
                // State read of firstVisibleItemScrollOffset in composition
                (listState.firstVisibleItemScrollOffset / 2).toDp()
            }
        )
    )

    LazyColumn(state = listState) {
        // ...
    }
}

এই কোড কাজ করে, কিন্তু ফলাফল অ-অনুকূল কর্মক্ষমতা. লেখার মতো, কোডটি firstVisibleItemScrollOffset অবস্থার মান পড়ে এবং এটি Modifier.offset(offset: Dp) ফাংশনে পাস করে। ব্যবহারকারী স্ক্রোল করার সাথে সাথে firstVisibleItemScrollOffset মান পরিবর্তন হবে। আমরা জানি, কম্পোজ যেকোনো স্টেট রিড ট্র্যাক করে যাতে এটি রিডিং কোড রিস্টার্ট (পুনরায় আহ্বান) করতে পারে, যা আমাদের উদাহরণে Box বিষয়বস্তু।

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

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

অফসেট মডিফায়ারের আরেকটি সংস্করণ উপলব্ধ রয়েছে: Modifier.offset(offset: Density.() -> IntOffset)

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

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        Modifier.offset {
            // State read of firstVisibleItemScrollOffset in Layout
            IntOffset(x = 0, y = listState.firstVisibleItemScrollOffset / 2)
        }
    )

    LazyColumn(state = listState) {
        // ...
    }
}

তাহলে কেন এই বেশি পারফরম্যান্স? আমরা মডিফায়ারকে যে ল্যাম্বডা ব্লক প্রদান করি সেটি লেআউট পর্বের সময় (বিশেষত, লেআউট পর্বের প্লেসমেন্ট ধাপের সময়) আহ্বান করা হয়, যার অর্থ আমাদের firstVisibleItemScrollOffset অবস্থাটি রচনার সময় আর পড়া হয় না। যেহেতু কম্পোজ ট্র্যাক যখন স্টেট রিড করা হয়, এই পরিবর্তনের মানে হল যদি firstVisibleItemScrollOffset মান পরিবর্তন হয়, কম্পোজকে শুধুমাত্র লেআউট এবং অঙ্কন পর্যায়গুলি পুনরায় চালু করতে হবে।

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

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

পুনর্গঠন লুপ (চক্রীয় পর্যায় নির্ভরতা)

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

Box {
    var imageHeightPx by remember { mutableStateOf(0) }

    Image(
        painter = painterResource(R.drawable.rectangle),
        contentDescription = "I'm above the text",
        modifier = Modifier
            .fillMaxWidth()
            .onSizeChanged { size ->
                // Don't do this
                imageHeightPx = size.height
            }
    )

    Text(
        text = "I'm below the image",
        modifier = Modifier.padding(
            top = with(LocalDensity.current) { imageHeightPx.toDp() }
        )
    )
}

এখানে আমরা (খারাপভাবে) একটি উল্লম্ব কলাম প্রয়োগ করেছি, যার উপরের চিত্রটি রয়েছে এবং তারপরে এটির নীচে পাঠ্য রয়েছে। আমরা চিত্রটির সমাধানকৃত আকার জানতে Modifier.onSizeChanged() ব্যবহার করছি এবং তারপরে এটিকে নামানোর জন্য টেক্সটে Modifier.padding() ব্যবহার করছি। Px থেকে Dp এ ফিরে আসা অস্বাভাবিক রূপান্তর ইতিমধ্যেই নির্দেশ করে যে কোডটিতে কিছু সমস্যা আছে।

এই উদাহরণের সাথে সমস্যা হল যে আমরা একটি একক ফ্রেমের মধ্যে "চূড়ান্ত" লেআউটে পৌঁছাই না। কোডটি ঘটতে থাকা একাধিক ফ্রেমের উপর নির্ভর করে, যা অপ্রয়োজনীয় কাজ করে এবং এর ফলে ব্যবহারকারীর জন্য UI স্ক্রিনে ঘুরে বেড়ায়।

কি ঘটছে তা দেখতে প্রতিটি ফ্রেমের মধ্য দিয়ে ধাপে ধাপে চলুন:

প্রথম ফ্রেমের রচনা পর্যায়ে, imageHeightPx এর মান 0 এবং পাঠ্যটি Modifier.padding(top = 0) দিয়ে দেওয়া হয়। তারপরে, বিন্যাস পর্যায়টি অনুসরণ করে, এবং onSizeChanged সংশোধকের জন্য কলব্যাক বলা হয়। এটি হল যখন imageHeightPx ছবিটির প্রকৃত উচ্চতায় আপডেট করা হয়। পরবর্তী ফ্রেমের জন্য সময়সূচী পুনর্গঠন রচনা করুন। অঙ্কন পর্যায়ে, টেক্সটটি 0 এর প্যাডিং দিয়ে রেন্ডার করা হয় যেহেতু মান পরিবর্তন এখনও প্রতিফলিত হয়নি।

রচনা তারপর imageHeightPx এর মান পরিবর্তন দ্বারা নির্ধারিত দ্বিতীয় ফ্রেম শুরু করে। রাজ্যটি বক্স বিষয়বস্তু ব্লকে পড়া হয়, এবং এটি রচনা পর্বে আহ্বান করা হয়। এই সময়, পাঠ্যটি চিত্রের উচ্চতার সাথে মেলে একটি প্যাডিং সহ দেওয়া হয়েছে। লেআউট পর্যায়ে, কোডটি আবার imageHeightPx এর মান সেট করে, কিন্তু মান একই থাকে বলে কোনো পুনর্গঠন নির্ধারিত হয় না।

শেষ পর্যন্ত, আমরা পাঠ্যের উপর পছন্দসই প্যাডিং পাই, কিন্তু প্যাডিং মানটিকে একটি ভিন্ন পর্যায়ে ফেরাতে অতিরিক্ত ফ্রেম ব্যয় করা অনুকূল নয় এবং এর ফলে ওভারল্যাপিং সামগ্রী সহ একটি ফ্রেম তৈরি হবে৷

এই উদাহরণটি কল্পিত মনে হতে পারে, তবে এই সাধারণ প্যাটার্নটি সম্পর্কে সতর্ক থাকুন:

  • Modifier.onSizeChanged() , onGloballyPositioned() , বা অন্য কিছু লেআউট অপারেশন
  • কিছু রাষ্ট্র আপডেট
  • লেআউট মডিফায়ারে ইনপুট হিসাবে সেই অবস্থাটি ব্যবহার করুন ( padding() , height() , বা অনুরূপ)
  • সম্ভাব্য পুনরাবৃত্তি

উপরের নমুনার জন্য সমাধান হল সঠিক বিন্যাস আদিম ব্যবহার করা। উপরের উদাহরণটি একটি সাধারণ Column() দিয়ে প্রয়োগ করা যেতে পারে, তবে আপনার কাছে আরও জটিল উদাহরণ থাকতে পারে যার জন্য কাস্টম কিছু প্রয়োজন, যার জন্য একটি কাস্টম লেআউট লেখার প্রয়োজন হবে। আরও তথ্যের জন্য কাস্টম লেআউট গাইড দেখুন।

এখানে সাধারণ নীতি হল একাধিক UI উপাদানগুলির জন্য সত্যের একটি একক উত্স থাকা যা একে অপরের সাথে পরিমাপ করা এবং স্থাপন করা উচিত। একটি সঠিক বিন্যাস আদিম ব্যবহার করা বা একটি কাস্টম বিন্যাস তৈরি করার অর্থ হল ন্যূনতম ভাগ করা অভিভাবক সত্যের উত্স হিসাবে কাজ করে যা একাধিক উপাদানের মধ্যে সম্পর্ককে সমন্বয় করতে পারে। একটি গতিশীল রাষ্ট্র প্রবর্তন এই নীতি ভঙ্গ করে.

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %}