Compose লেআউটের প্রাথমিক বিষয়

Jetpack Compose আপনার অ্যাপের UI ডিজাইন ও তৈরি করার কাজকে অনেক সহজ করে দেয়। Compose নিম্নলিখিত উপায়ে স্টেটকে UI এলিমেন্টে ট্রান্সফর্ম করে:

  1. উপাদানের কম্পোজিশন
  2. এলিমেন্টের লেআউট
  3. উপাদান আঁকা

কম্পোজিশন, লেআউট, ড্রয়িংয়ের মাধ্যমে UI-তে ট্রান্সফর্ম করা স্টেট কম্পোজ করা

এই ডকুমেন্ট এলিমেন্টের লেআউটের উপর ফোকাস করে এবং আপনার UI এলিমেন্ট লেআউট করতে সাহায্য করার জন্য Compose-এর দেওয়া কিছু বিল্ডিং ব্লক ব্যাখ্যা করে।

Compose-এ লেআউটের লক্ষ্য

লেআউট সিস্টেমের Jetpack Compose প্রয়োগের দুটি প্রধান লক্ষ্য রয়েছে:

কম্পোজেবল ফাংশনের প্রাথমিক বিষয়

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

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

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

আপনি কীভাবে সেগুলি সাজাতে চান সেই বিষয়ে কোনও নির্দেশিকা না থাকলে, Compose টেক্সট এলিমেন্টগুলিকে একটির উপরে আরেকটি স্ট্যাক করে, ফলে সেগুলি আর পড়া যায় না:

দুটি টেক্সট এলিমেন্ট একে অপরের উপরে ড্র করা হয়েছে, যার ফলে টেক্সটটি পড়া যাচ্ছে না

Compose আপনাকে রেডি-টু-ইউজ লেআউটের একটি সংগ্রহ প্রদান করে যা আপনার UI এলিমেন্ট সাজাতে সাহায্য করে এবং এটি আপনার নিজস্ব, আরও বিশেষ লেআউটকে সহজে সংজ্ঞায়িত করে।

স্ট্যান্ডার্ড লেআউট কম্পোনেন্ট

অনেক ক্ষেত্রে, আপনি শুধু কম্পোজের স্ট্যান্ডার্ড লেআউট এলিমেন্ট ব্যবহার করতে পারবেন।

স্ক্রিনে আইটেম উল্লম্বভাবে প্লেস করতে Column ব্যবহার করুন।

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

কলাম লেআউটে সাজানো দুটি টেক্সট এলিমেন্ট, যাতে টেক্সটটি পড়া যায়

একইভাবে, স্ক্রিনে আইটেমগুলি অনুভূমিকভাবে প্লেস করতে Row ব্যবহার করুন। Column ও Row, দু'টিই সেগুলিতে থাকা এলিমেন্টের অ্যালাইনমেন্ট কনফিগার করার সুবিধা দেয়।

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

টেক্সট এলিমেন্টের কলামের পাশে একটি ছোট গ্রাফিক সহ আরও জটিল লেআউট দেখায়

একটি এলিমেন্টের উপরে আরেকটি এলিমেন্ট রাখতে Box ব্যবহার করুন। Box-এ থাকা এলিমেন্টগুলির নির্দিষ্ট অ্যালাইনমেন্ট কনফিগার করার সুবিধাও এতে আছে।

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

দুটি এলিমেন্ট একটির উপরে আরেকটি স্ট্যাক করে দেখানো হয়

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

তিনটি সাধারণ লেআউট কম্পোজ করার উপযুক্ত আইটেম তুলনা করে: কলাম, সারি ও বক্স

Row-এর মধ্যে সন্তানের পজিশন সেট করতে, horizontalArrangement এবং verticalAlignment আর্গুমেন্ট সেট করুন। Column-এর জন্য, verticalArrangement এবং horizontalAlignment আর্গুমেন্ট সেট করুন:

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

আইটেমগুলি ডানদিকে অ্যালাইন করা আছে

লেআউট মডেল

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

সংক্ষেপে, বাচ্চাদের আগে অভিভাবকের ছবি তোলা হয়, কিন্তু বাচ্চাদের পরে তাদের ছবি সাইজ ও প্লেস করা হয়।

নিচের SearchResult ফাংশনটি বিবেচনা করুন।

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

এই ফাংশন নিম্নলিখিত UI ট্রি তৈরি করে।

SearchResult
  Row
    Image
    Column
      Text
      Text

SearchResult উদাহরণে, UI ট্রি লেআউট এই ক্রম অনুসরণ করে:

  1. রুট নোড Row পরিমাপ করতে বলা হয়েছে।
  2. রুট নোড Row তার প্রথম চাইল্ড Image-কে পরিমাপ করতে বলে।
  3. Image হল একটি লিফ নোড (অর্থাৎ, এর কোনও চাইল্ড নোড নেই), তাই এটি একটি সাইজ রিপোর্ট করে এবং প্লেসমেন্ট সংক্রান্ত নির্দেশাবলী রিটার্ন করে।
  4. রুট নোড Row তার দ্বিতীয় চাইল্ড Column-কে পরিমাপ করতে বলে।
  5. Column নোড তার প্রথম Text চাইল্ডকে পরিমাপ করতে বলে।
  6. প্রথম Text নোড হল একটি লিফ নোড, তাই এটি একটি সাইজ রিপোর্ট করে এবং প্লেসমেন্ট সংক্রান্ত নির্দেশাবলী ফেরত দেয়।
  7. Column নোড তার দ্বিতীয় Text চাইল্ডকে পরিমাপ করতে বলে।
  8. দ্বিতীয় Text নোডটি হল লিফ নোড, তাই এটি একটি সাইজ রিপোর্ট করে এবং প্লেসমেন্ট সংক্রান্ত নির্দেশাবলী রিটার্ন করে।
  9. Column নোড যেহেতু এর চাইল্ড নোড পরিমাপ, সাইজ ও প্লেস করেছে, তাই এটি নিজের সাইজ ও প্লেসমেন্ট নির্ধারণ করতে পারে।
  10. এখন যেহেতু রুট নোড Row তার চাইল্ড নোড পরিমাপ, সাইজ ও প্লেস করেছে, তাই এটি নিজের সাইজ ও প্লেসমেন্ট নির্ধারণ করতে পারবে।

Search Result UI ট্রি-তে পরিমাপ, সাইজ ও প্লেসমেন্টের ক্রম

পারফর্ম্যান্স

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

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

যেহেতু পরিমাপ ও প্লেসমেন্ট লেআউট পাসের স্বতন্ত্র উপ-ফেজ, তাই কোনও পরিবর্তন যা শুধুমাত্র আইটেমের প্লেসমেন্টকে প্রভাবিত করে, পরিমাপকে নয়, তা আলাদাভাবে এক্সিকিউট করা যেতে পারে।

আপনার লেআউটে মডিফায়ার ব্যবহার করা

কম্পোজ মডিফায়ার নিবন্ধে আলোচনা করা হয়েছে, আপনার কম্পোজ করার উপযুক্ত আইটেমকে সাজাতে বা আরও উন্নত করতে আপনি মডিফায়ার ব্যবহার করতে পারবেন। আপনার লেআউট কাস্টমাইজ করার জন্য মডিফায়ার অপরিহার্য। যেমন, এখানে আমরা ArtistCard কাস্টমাইজ করতে একাধিক মডিফায়ার চেইন করি:

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

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

উপরের কোডে, একসাথে ব্যবহার করা বিভিন্ন মডিফায়ার ফাংশন লক্ষ্য করুন।

  • clickable ব্যবহারকারীর ইনপুটের জন্য কম্পোজ করা যায় এমন প্রতিক্রিয়া তৈরি করে এবং রিপেল দেখায়।
  • padding কোনও এলিমেন্টের চারপাশে স্পেস তৈরি করে।
  • fillMaxWidth কম্পোজ করার উপযুক্ত আইটেমকে এর পেরেন্ট থেকে পাওয়া সর্বাধিক প্রস্থে পূর্ণ করে।
  • size() কোনও এলিমেন্টের পছন্দের প্রস্থ ও উচ্চতা নির্দিষ্ট করে।

স্ক্রল করা যায় এমন লেআউট

Compose জেসচার ডকুমেন্টেশন থেকে স্ক্রল করা যায় এমন লেআউট সম্পর্কে আরও জানুন।

তালিকা ও অলস তালিকার জন্য, কম্পোজ তালিকা সংক্রান্ত ডকুমেন্টেশন দেখুন।

রেসপন্সিভ লেআউট

বিভিন্ন স্ক্রিন ওরিয়েন্টেশন ও ফর্ম ফ্যাক্টর সাইজের কথা মাথায় রেখে লেআউট ডিজাইন করতে হবে। Compose-এ বিভিন্ন স্ক্রিন কনফিগারেশনের সাথে আপনার কম্পোজ করা যায় এমন লেআউট অ্যাডজাস্ট করার জন্য কয়েকটি মেকানিজম আগে থেকেই দেওয়া আছে।

সীমাবদ্ধতা

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

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

স্লট-ভিত্তিক লেআউট

UI তৈরি করা সহজ করতে, Compose Material Design-এর উপর ভিত্তি করে androidx.compose.material:material নির্ভরতা (Android Studio-তে Compose প্রোজেক্ট তৈরি করার সময় অন্তর্ভুক্ত) সহ বিভিন্ন ধরনের কম্পোজ করার উপযুক্ত আইটেম প্রদান করে। Drawer, FloatingActionButton, এবং TopAppBar-এর মতো এলিমেন্ট সবই প্রদান করা হয়েছে।

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

Material Components অ্যাপ বারে উপলভ্য স্লট দেখানো একটি ডায়াগ্রাম

কম্পোজ করার উপযুক্ত আইটেম সাধারণত content কম্পোজ করার উপযুক্ত ল্যাম্বডা ( content: @Composable () -> Unit) নেয়। স্লট API নির্দিষ্ট ব্যবহারের জন্য একাধিক content প্যারামিটার এক্সপোজ করে। যেমন, TopAppBar আপনাকে title, navigationIcon ও actions-এর জন্য কন্টেন্ট প্রদান করতে দেয়।

যেমন, Scaffold আপনাকে সাধারণ মেটেরিয়াল ডিজাইন লেআউট স্ট্রাকচার সহ UI প্রয়োগ করতে দেয়। Scaffoldসবচেয়ে সাধারণ টপ-লেভেল মেটেরিয়াল কম্পোনেন্টের জন্য স্লট প্রদান করে, যেমন TopAppBar, BottomAppBar, FloatingActionButton, এবং Drawer. Scaffold ব্যবহার করে, এইসব কম্পোনেন্ট সঠিকভাবে পজিশন করা হয়েছে কিনা এবং একসাথে সঠিকভাবে কাজ করছে কিনা তা সহজেই নিশ্চিত করা যায়।

JetNews স্যাম্পেল অ্যাপ, যা একাধিক এলিমেন্ট পজিশন করার জন্য Scaffold ব্যবহার করে

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}