کانال بله, جهت پشتیبانی و اطلاع رسانی کانال بله, جهت پشتیبانی و اطلاع رسانی
عضویت

آموزش کار با List و Grid در اندروید، اندروید استودیو

آموزش جامع و عمیق لیست‌ها در اندروید (از ListView تا RecyclerView و Jetpack Compose) - آپدیت ۲۰۲۵

هر بار که اپلیکیشنی مانند اینستاگرام، تلگرام یا لیست مخاطبین خود را باز می‌کنید، در حال تعامل با یک لیست هستید. نمایش بهینه و روان مجموعه‌ای از داده‌ها، سنگ بنای یک تجربه کاربری عالی است. در اکوسیستم اندروید، ابزارهای ساخت این لیست‌ها مسیری پر از تکامل را طی کرده‌اند.

در این راهنمای جامع و دقیق، ما به صورت عمیق به بررسی این ابزارها می‌پردازیم. ابتدا دلیل منسوخ شدن ListView را بررسی می‌کنیم، سپس با تمام جزئیات به سراغ استاندارد فعلی یعنی RecyclerView می‌رویم و یاد می‌گیریم چگونه آن را به صورت حرفه‌ای پیاده‌سازی و شخصی‌سازی کنیم. در نهایت، با نگاهی به آینده، قدرت و سادگی Jetpack Compose را کشف خواهیم کرد.


روش قدیمی (Legacy): چرا باید ListView را فراموش کنیم؟

ListView اولین ابزار اندروید برای نمایش لیست‌ها بود، اما دو ضعف ساختاری بزرگ داشت که منجر به کنار گذاشته شدن آن شد:

  • عدم بازیافت View به صورت پیش‌فرض: ListView برای هر آیتم در لیست، یک آبجکت View جدید در حافظه می‌ساخت. در لیست‌های طولانی، این کار به سرعت حافظه (RAM) دستگاه را پر می‌کرد و باعث کندی شدید و حتی کرش کردن اپلیکیشن می‌شد.
  • الگوی ViewHolder اختیاری بود: برای بهینه‌سازی ListView، برنامه‌نویسان باید به صورت دستی الگویی به نام ViewHolder را پیاده‌سازی می‌کردند. در این الگو، فراخوانی‌های مکرر و پرهزینه findViewById() کاهش می‌یافت. اما چون این الگو اجباری نبود، بسیاری از توسعه‌دهندگان آن را نادیده می‌گرفتند و کدهای ناکارآمد تولید می‌شد.
💡 نتیجه‌گیری فنی: RecyclerView با اجباری کردن الگوی ViewHolder و مدیریت هوشمندانه بازیافت View، این دو مشکل اساسی را به صورت ساختاری حل کرد.

روش مدرن و استاندارد: تسلط بر RecyclerView

RecyclerView یک کامپوننت فوق‌العاده قدرتمند است که برای نمایش بهینه لیست‌های بزرگ و پویا طراحی شده است. فلسفه اصلی آن "بازیافت" است: به جای ساختن View برای تک‌تک آیتم‌ها، تنها به تعداد آیتم‌های قابل مشاهده در صفحه، View ساخته و با اسکرول کاربر، محتوای آن‌ها را با داده‌های جدید جایگزین می‌کند.

جریان کار RecyclerView: اجزا چگونه با هم کار می‌کنند؟

درک تعامل بین اجزای RecyclerView برای تسلط بر آن کلیدی است:

  1. شروع کار: RecyclerView از LayoutManager می‌پرسد: "چگونه باید آیتم‌ها را بچینم؟" (عمودی، افقی، جدولی؟).
  2. نیاز به View: RecyclerView متوجه می‌شود که برای پر کردن صفحه به چند View نیاز دارد. بنابراین از Adapter می‌خواهد: "برای این موقعیت، یک ViewHolder به من بده".
  3. ساخت ViewHolder: متد onCreateViewHolder در Adapter شما فراخوانی می‌شود. در این متد، شما فایل XML مربوط به یک آیتم را به آبجکت View تبدیل (inflate) کرده و آن را درون یک ViewHolder جدید قرار می‌دهید و به RecyclerView تحویل می‌دهید.
  4. اتصال داده‌ها: حالا RecyclerView یک ViewHolder خالی دارد. دوباره به Adapter مراجعه می‌کند و می‌گوید: "این داده را بگیر و به این ViewHolder متصل کن".
  5. نمایش داده: متد onBindViewHolder در Adapter شما فراخوانی می‌شود. شما داده مربوط به آن موقعیت را از لیست خود برداشته و در Viewهای داخل ViewHolder (مثلاً TextViewها) قرار می‌دهید.
  6. بازیافت: وقتی کاربر اسکرول می‌کند و یک آیتم از صفحه خارج می‌شود، RecyclerView آن View را از رده خارج نمی‌کند، بلکه آن را برای استفاده مجدد نگه می‌دارد. وقتی یک آیتم جدید وارد صفحه می‌شود، RecyclerView همان ViewHolder بازیافتی را برداشته و مستقیماً به مرحله ۴ (اتصال داده‌ها) می‌رود و تنها متد onBindViewHolder را برای آن فراخوانی می‌کند. این کار از تکرار مرحله پرهزینه ۳ (ساخت ViewHolder) جلوگیری می‌کند.

پیاده‌سازی گام به گام (با مدیریت کلیک)

در این راهنما، قابلیت پرکاربرد مدیریت کلیک روی آیتم‌ها را نیز پیاده‌سازی می‌کنیم.

قدم اول و دوم: وابستگی‌ها و طراحی لایوت‌ها

این بخش مانند قبل است. وابستگی recyclerview را اضافه کرده و viewBinding را فعال کنید. سپس فایل‌های list_item.xml و activity_main.xml را بسازید.

قدم سوم: ساخت یک Adapter هوشمند با قابلیت کلیک

ما یک پارامتر جدید به سازنده (constructor) Adapter اضافه می‌کنیم: یک تابع لامبدا (lambda) که هنگام کلیک روی یک آیتم، فراخوانی شود.

// در فایلی به نام MyListAdapter.kt
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.app.databinding.ListItemBinding

// یک پارامتر جدید به نام onItemClicked اضافه می‌کنیم
// این یک تابع است که یک String به عنوان ورودی می‌گیرد و خروجی ندارد
class MyListAdapter(private val onItemClicked: (String) -> Unit) :
    ListAdapter(DiffCallback()) {

    // ViewHolder بدون تغییر باقی می‌ماند
    class ItemViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ItemViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val currentItem = getItem(position)
        
        // اتصال داده‌ها
        holder.binding.itemTitle.text = currentItem
        holder.binding.itemDescription.text = "این توضیحات آیتم شماره ${position + 1} است."
        
        // تنظیم شنونده کلیک برای ریشه View آیتم
        holder.itemView.setOnClickListener {
            onItemClicked(currentItem)
        }
    }

    // DiffCallback بدون تغییر باقی می‌ماند
    class DiffCallback : DiffUtil.ItemCallback() {
        override fun areItemsTheSame(oldItem: String, newItem: String): Boolean = oldItem == newItem
        override fun areContentsTheSame(oldItem: String, newItem: String): Boolean = oldItem == newItem
    }
}
    

قدم چهارم: تنظیم نهایی در Activity

حالا در MainActivity، هنگام ساخت Adapter، تابعی که می‌خواهیم در زمان کلیک اجرا شود را به آن پاس می‌دهیم.

// MainActivity.kt
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.app.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // هنگام ساخت Adapter، یک لامبدا برای مدیریت کلیک به آن می‌دهیم
        val adapter = MyListAdapter { clickedItem ->
            // کدی که اینجا می‌نویسید، هنگام کلیک روی هر آیتم اجرا می‌شود
            Toast.makeText(this, "روی آیتم کلیک شد: $clickedItem", Toast.LENGTH_SHORT).show()
        }

        binding.mainRecyclerView.adapter = adapter
        binding.mainRecyclerView.layoutManager = LinearLayoutManager(this)
        
        val sampleData = List(50) { "آیتم شماره ${it + 1}" }
        adapter.submitList(sampleData)
    }
}
    

شخصی‌سازی ظاهری: افزودن خط جداکننده (Divider)

برای افزودن خط جداکننده بین آیتم‌ها، می‌توانید از DividerItemDecoration استفاده کنید. این کار به سادگی در MainActivity انجام می‌شود:

// در MainActivity.kt، بعد از تنظیم LayoutManager
import androidx.recyclerview.widget.DividerItemDecoration

// ...
binding.mainRecyclerView.layoutManager = LinearLayoutManager(this)

// افزودن خط جداکننده
val divider = DividerItemDecoration(this, (binding.mainRecyclerView.layoutManager as LinearLayoutManager).orientation)
binding.mainRecyclerView.addItemDecoration(divider)
// ...
    

آینده نمایش لیست‌ها: درک عمیق‌تر Jetpack Compose

Jetpack Compose یک تغییر پارادایم از برنامه نویسی دستوری (Imperative) به توصیفی (Declarative) است. به جای اینکه به سیستم بگوییم "یک TextView بساز، سپس متنش را این قرار بده"، ما به سادگی وضعیت نهایی UI را توصیف می‌کنیم: "یک متن با این مقدار باید اینجا باشد". Compose خودش وظیفه ساخت، آپدیت و مدیریت آن را بر عهده می‌گیرد. این رویکرد به شدت از کدهای تکراری (boilerplate) می‌کاهد و مدیریت وضعیت (State) را بسیار ساده‌تر می‌کند.

برای لیست‌ها، LazyColumn این فلسفه را پیاده می‌کند. شما فقط توصیف می‌کنید که هر آیتم چگونه باید به نظر برسد؛ خود Compose وظیفه سنگین بازیافت و نمایش بهینه آیتم‌ها را در پس‌زمینه انجام می‌دهد.

در اینجا نحوه مدیریت کلیک در Jetpack Compose را نیز می‌بینید:

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

// ...

@Composable
fun MyComposeList(items: List, onItemClicked: (String) -> Unit) {
    LazyColumn {
        items(items) { item ->
            ListItemView(item = item, onItemClicked = onItemClicked)
        }
    }
}

@Composable
fun ListItemView(item: String, onItemClicked: (String) -> Unit) {
    Column(
        modifier = Modifier
            .padding(16.dp)
            .clickable { onItemClicked(item) } // به همین سادگی کلیک مدیریت می‌شود
    ) {
        // ... محتوای آیتم
    }
}
    

جمع‌بندی نهایی

  • ListView: یک درس تاریخی در توسعه اندروید. از آن استفاده نکنید.
  • RecyclerView: ابزار استاندارد، قدرتمند و انعطاف‌پذیر برای سیستم View. با یادگیری کامل اجزای آن، مدیریت کلیک و شخصی‌سازی‌های ظاهری، می‌توانید هر نوع لیستی را به صورت بهینه پیاده‌سازی کنید. همیشه از ListAdapter برای آپدیت‌های هوشمند استفاده کنید.
  • Jetpack Compose (LazyColumn): روش مدرن، ساده و آینده توسعه اندروید. با کدنویسی کمتر و خوانایی بیشتر، بهترین انتخاب برای پروژه‌های جدید است.
1404/06/23 8982 2390
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...