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

Jetpack Compose আপনার অ্যাপের UI ডিজাইন ও তৈরি করা অনেক সহজ করে তোলে। Compose স্টেটকে UI এলিমেন্টে রূপান্তরিত করে, এর মাধ্যমে:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

স্ক্রিনে আইটেম উল্লম্বভাবে সাজাতে 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 ফলাফল 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 তালিকার ডকুমেন্টেশন দেখুন।

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

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

সীমাবদ্ধতা

In order to know the constraints coming from the parent and design the layout accordingly, you can use a BoxWithConstraints. পরিমাপের সীমাবদ্ধতা কন্টেন্ট ল্যাম্বডার স্কোপে পাওয়া যাবে। আপনি বিভিন্ন স্ক্রিন কনফিগারেশনের জন্য বিভিন্ন লেআউট কম্পোজ করতে এই পরিমাপের সীমাবদ্ধতা ব্যবহার করতে পারেন:

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

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

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

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

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

কম্পোজেবল সাধারণত একটি 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 ->
            // ...
        }
    }
}