ListView: چیست، لیست‌های ساده در برنامه‌های Android

نویسنده: IT Sectr منتشر شده: 2026-02-23 زمان مطالعه: 8 دقیقه

ListView — کامپوننت پایه Android برای نمایش لیست‌های عمودی، قابل دسترس از API 1 (Android 1.0). ListView با استفاده از ArrayAdapter از المان‌ها لیست ایجاد می‌کند و از طرح‌بندی سلول سفارشی پشتیبانی می‌کند. می‌فهمیم ListView چیست، چگونه آن را پیکربندی کنیم و چه مشکلاتی این کامپوننت قدیمی اما همچنان مورد استفاده پنهان می‌کند. در IT Sectr ما ListView را در پروژه‌های legacy ملاقات می‌کنیم و سازگاری معکوس با Android 4.4 را پشتیبانی می‌کنیم. برای مقایسه با جایگزین مدرن مقاله RecyclerView را بخوانید.

نکات اصلی

  • ListView — کامپوننت داخلی Android SDK برای لیست‌های عمودی، موجود از Android 1.0.
  • ArrayAdapter — آداپتور استاندارد برای اتصال آرایه داده به ListView از طریق toString() یا طرح‌بندی سفارشی.
  • SimpleAdapter — آداپتور برای لیست‌های داده‌های Map، مناسب برای صفحات ساده بدون ViewModel.
  • onItemClickListener — هندلر کلیک داخلی که نیاز به پیاده‌سازی اضافی در آداپتور ندارد.
  • convertView — مکانیزم استفاده مجدد از View در ListView که برنامه‌نویس باید دستی پیاده‌سازی کند (مشابه ViewHolder).

ListView در Android چیست؟

ListView — یک گروه View از بسته android.widget است که برای نمایش لیست قابل پیمایش از المان‌ها طراحی شده است. ListView الگوی AdapterView را پیاده‌سازی می‌کند: داده‌ها از طریق آداپتور (Adapter) منتقل می‌شوند که المان‌های داده را به View نمایش داده شده روی صفحه تبدیل می‌کند. ListView از نسخه اول (API 1) در Android SDK وجود دارد و به کتابخانه‌های اضافی نیاز ندارد.

معماری ListView ساده‌تر از RecyclerView است: LayoutManager داخلی است (فقط پیمایش عمودی)، انیمیشن تغییرات وجود ندارد، جداکننده‌ها از طریق ویژگی XML android:divider تنظیم می‌شوند. ListView به طور خودکار OverScrollView و برجسته‌سازی در فوکوس را اضافه می‌کند که نمونه‌سازی اولیه را ساده‌تر می‌کند. طبق داده‌های Statista (2025)، حدود 15% از برنامه‌های Android در top-100 Play Store هنوز حاوی ListView هستند — عمدتاً پروژه‌های legacy با پشتیبانی حداقلی.

تاریخچه و relevance

ListView از 2008 تا 2014 کامپوننت اصلی لیست‌ها در Android بود، تا زمانی که در Google I/O 2014 RecyclerView معرفی شد. علیرغم وضعیت نیمه‌منسوخ (soft-deprecated)، ListView در تمام نسخه‌های Android در دسترس باقی می‌ماند و در SDK API 35 به عنوان @Deprecated علامت‌گذاری نشده است. اگر پروژه شما از Android 4.4 (API 19) و پایین‌تر پشتیبانی می‌کند، ListView تنها انتخاب داخلی است، زیرا RecyclerView نیاز به AndroidX و وابستگی‌های gradle دارد.

انواع آداپتورها: ArrayAdapter و SimpleAdapter

ArrayAdapter — محبوب‌ترین آداپتور برای ListView است. این آداپتور List<T> و طرح‌بندی برای هر المان را می‌پذیرد. به طور پیش‌فرض ArrayAdapter برای هر شیء toString() را فراخوانی می‌کند و متن را در simple_list_item_1 (یک TextView) نمایش می‌دهد. برای نمایش سفارشی باید getView() را بازنویسی کرد یا طرح‌بندی سفارشی در سازنده ارسال کرد.

kotlin
// ListView با ArrayAdapter (متن ساده)
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val countries = listOf(
        "روسیه", "ایالات متحده", "آلمان",
        "فرانسه", "ژاپن", "چین", "برزیل"
    )

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

        val adapter = ArrayAdapter<String>(
            this,
            android.R.layout.simple_list_item_1,
            countries
        )
        binding.listView.adapter = adapter

        // مدیریت کلیک
        binding.listView.onItemClickListener =
            AdapterView.OnItemClickListener { _, _, position, _ ->
                Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
            }
    }
}

SimpleAdapter برای لیست‌های داده‌های Map طراحی شده است. این آداپتور List<Map<String, Any>> و آرایه from/to برای اتصال کلیدهای Map به ID ویجت‌ها در طرح‌بندی را می‌پذیرد. SimpleAdapter برای نمونه‌سازی سریع زمانی که کلاس آداپتور جداگانه نیاز نیست مفید است. عیب: عدم وجود type-safe و پیچیدگی با منطق سفارشی.

xml
<!-- طرح‌بندی با ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@color/divider"
        android:dividerHeight="1px"
        android:scrollbars="vertical" />
</LinearLayout>

ایجاد آداپتور سفارشی با ViewHolder

آداپتور سفارشی برای ListView متد getView() را بازنویسی می‌کند که RecyclerView آن را به onCreateViewHolder + onBindViewHolder تقسیم می‌کند. در ListView هر دو مرحله ترکیب شده‌اند، بنابراین برنامه‌نویس باید الگوی ViewHolder را از طریق convertView (View قابل استفاده مجدد) و تگ setTag() به طور مستقل پیاده‌سازی کند.

kotlin
// مدل داده
data class Product(
    name: String,
    price: String,
    imageResId: Int
)

// آداپتور سفارشی با ViewHolder
class ProductAdapter(
    context: Context,
    private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {

    // ViewHolder برای کش کردن
    private class ViewHolder(view: View) {
        val nameText: TextView = view.findViewById(R.id.tvName)
        val priceText: TextView = view.findViewById(R.id.tvPrice)
        val imageView: ImageView = view.findViewById(R.id.ivProduct)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view = convertView ?: LayoutInflater.from(context)
            .inflate(R.layout.item_product, parent, false)
            .also { it.tag = ViewHolder(it) }

        val holder = view.tag as ViewHolder
        val product = getItem(position)

        holder.nameText.text = product.name
        holder.priceText.text = product.price
        holder.imageView.setImageResource(product.imageResId)

        return view
    }
}

تفاوت کلیدی با RecyclerView: در ListView متد getView() برای هر بار ظاهر شدن المان روی صفحه فراخوانی می‌شود. اگر از convertView و ViewHolder استفاده نکنید، هر اسکرول یک شیء View جدید ایجاد می‌کند که منجر به نشت حافظه و لگ در لیست‌های با 50+ المان می‌شود. ViewHolder با view.tag — بهینه‌سازی اجباری برای ListView با طرح‌بندی‌های سفارشی است.

محدودیت‌های ListView: چرا Google RecyclerView را توصیه می‌کند

ListView چهار محدودیت معماری دارد که Google در RecyclerView برطرف کرده است: (1) فقط جهت عمودی — بدون شبکه، لیست افقی یا آبشاری؛ (2) عدم وجود اعلان‌های نقطه‌ای — هر تغییر داده باعث بازنویسی کامل از طریق notifyDataSetChanged() می‌شود که لرزش ایجاد می‌کند؛ (3) بدون انیمیشن داخلی برای افزودن/حذف المان‌ها؛ (4) عدم تفکیک وظایف — LayoutManager داخلی است، ItemAnimator وجود ندارد.

xml
<!-- item_product.xml — طرح‌بندی المان ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="12dp">

    <ImageView
        android:id="@+id/ivProduct"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:scaleType="centerCrop" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingStart="12dp">

        <TextView
            android:id="@+id/tvName"
            android:textSize="16sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/tvPrice"
            android:textSize="14sp"
            android:textColor="@color/price" />
    </LinearLayout>
</LinearLayout>

پیامدهای عملی: در لیست‌های تا 20 المان ListView به سرعت RecyclerView کار می‌کند. با 100+ المان تفاوت قابل توجه می‌شود: ListView به دلیل عدم محاسبات تفاضلی و ViewHolder اجباری شروع به افت FPS می‌کند. برای لیست‌های با به‌روزرسانی مکرر (چت، فید، لاگ‌ها) ListView مناسب نیست — از RecyclerView با Paging 3 استفاده کنید. در IT Sectr ما ListView را به RecyclerView در هر کاری روی صفحات legacy با بیش از 50 المان مهاجرت می‌دهیم.

پیکربندی ListView از طریق ویژگی‌های XML

ListView مجموعه‌ای از ویژگی‌های XML برای پیکربندی سریع بدون کد ارائه می‌دهد: android:divider (رنگ/تصویر جداکننده)، android:dividerHeight (ضخامت)، android:scrollbars (نوارهای پیمایش)، android:fastScrollEnabled (پیمایش سریع با دسته)، android:choiceMode (حالت انتخاب: singleChoice، multipleChoice). ویژگی android:entries امکان پر کردن لیست از آرایه منبع (string-array) بدون آداپتور را فراهم می‌کند.

ویژگیتوضیحمقدار نمونه
android:dividerرنگ یا drawable جداکننده@color/gray_light
android:dividerHeightضخامت جداکننده بر حسب px/dp1dp
android:entriesآرایه رشته‌ها از منابع@array/countries
android:choiceModeحالت انتخاب المانsingleChoice
android:fastScrollEnabledپیمایش سریعtrue
android:overlapAnchorلنگر برای لیست‌های بازشونده (API 21+)true

حالت انتخاب: choiceMode="singleChoice" سبک RadioButton را فعال می‌کند که المان انتخاب شده برجسته می‌شود. choiceMode="multipleChoice" — چک‌باکس برای چند المان. داده‌های انتخاب از طریق listView.checkedItemPosition یا listView.checkedItemIds قابل دسترس هستند. مهم: در حالت choiceMode ListView از StateListDrawable برای پس‌زمینه استفاده می‌کند — اگر آبی استاندارد با طراحی شما هماهنگ نیست، انتخاب‌گر خود را تنظیم کنید.

ListView در مقابل RecyclerView: مقایسه قابلیت‌ها

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

پارامترListViewRecyclerView
حجم کتابخانهداخلی در SDK (≈60 کیلوبایت)AndroidX (≈300 کیلوبایت با وابستگی‌ها)
حداقل نسخهAPI 1API 14 (از طریق AndroidX)
جهتفقط عمودیعمودی، افقی، شبکه، آبشاری
ViewHolderپیاده‌سازی دستیاجباری بر اساس معماری
انیمیشنفقط fadeDefaultItemAnimator (add, remove, move, change)
به‌روزرسانی نقطه‌ایخیر (فقط notifyDataSetChanged)notifyItemInserted/Removed/Changed
Click listeneronItemClickListener (داخلی)از طریق ViewHolder (داخلی وجود ندارد)
عملکردکافی برای لیست‌های تا 50 المان60 FPS پایدار برای 1000+ المان

نتیجه: برای پروژه‌های جدید از RecyclerView استفاده کنید. ListView را فقط در کد legacy بگذارید که مهاجرت به‌طور غیرمنطقی گران است. طبق راهنمای Material Design 3 (2026)، لیست‌های با هر پیچیدگی باید از RecyclerView استفاده کنند تا با استانداردهای مدرن عملکرد و دسترس‌پذیری مطابقت داشته باشند.

سوالات متداول

آیا ListView منسوخ شده است یا خیر؟

ListView در Android SDK API 35 به عنوان @Deprecated علامت‌گذاری نشده است، اما Google استفاده از آن را در پروژه‌های جدید توصیه نمی‌کند (soft-deprecated). این کامپوننت در تمام نسخه‌های Android کارآمد باقی می‌ماند. RecyclerView از سال 2014 جایگزین رسمی است. اگر پروژه جدیدی شروع می‌کنید — RecyclerView را انتخاب کنید. اگر پروژه موجود را پشتیبانی می‌کنید — ListView بدون تغییر به کار خود ادامه می‌دهد.

چگونه header/footer به ListView اضافه کنیم؟

ListView از متدهای addHeaderView(View, Object, boolean) و addFooterView(View, Object, boolean) پشتیبانی می‌کند. Header و footer به لیست کلی المان‌ها اضافه می‌شوند اما در آداپتور شرکت نمی‌کنند — آنها همیشه در ابتدا و انتهای لیست نمایش داده می‌شوند. مهم: header/footer را قبل از تنظیم آداپتور (setAdapter()) اضافه کنید، در غیر این صورت در نظر گرفته نمی‌شوند. پس از افزودن header، ID موقعیت‌های المان به تعداد headerها جابه‌جا می‌شوند.

چرا ListView داخل ScrollView اسکرول نمی‌شود؟

ListView و ScrollView هر دو حرکت عمودی را پردازش می‌کنند که باعث تعارض می‌شود: ScrollView بیرونی اسکرول را رهگیری می‌کند و ListView پیمایش نمی‌شود. راه‌حل: (1) ListView را در ScrollView قرار ندهید — از RecyclerView با NestedScrollingChild استفاده کنید، (2) ScrollView را با NestedScrollView (API 21+) جایگزین کنید، (3) از ListView با ارتفاع ثابت (android:layout_height="...dp") داخل ScrollView استفاده کنید، اما این کار تطبیق‌پذیری را از بین می‌برد.

چگونه ListView را پاک کنیم و داده‌ها را به‌روز کنیم؟

پاک کردن داده‌ها در ListView به نوع آداپتور بستگی دارد. برای ArrayAdapter: adapter.clear() و adapter.addAll(newList) با adapter.notifyDataSetChanged() بعدی. برای آداپتور سفارشی: لیست داده را به‌روز کنید و notifyDataSetChanged() را فراخوانی کنید. توجه: listView.invalidateViews() فقط View را دوباره ترسیم می‌کند اما داده‌ها را به‌روز نمی‌کند. همیشه داده‌ها را از طریق آداپتور تغییر دهید، نه مستقیم.

چگونه ارتفاع المان ListView را تغییر دهیم؟

ارتفاع المان ListView در طرح‌بندی item تنظیم می‌شود. اگر طرح‌بندی از layout_height="wrap_content" استفاده کند، ارتفاع با محتوا تنظیم می‌شود. برای ارتفاع ثابت همه المان‌ها از آداپتور سفارشی که LayoutParams را در getView() تنظیم می‌کند استفاده کنید: view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). برای ارتفاع‌های مختلف از RecyclerView با انواع نمایش مختلف استفاده کنید.

خلاصه

  • ListView — کامپوننت پایه Android SDK برای لیست‌های عمودی، قابل دسترس از API 1.
  • ArrayAdapter — ساده‌ترین آداپتور برای لیست رشته‌ها؛ برای طرح‌بندی سفارشی به getView() بازنویسی شده نیاز دارد.
  • ViewHolder در ListView به صورت دستی از طریق convertView و view.tag پیاده‌سازی می‌شود — شرط اجباری عملکرد.
  • ListView از choiceMode (single/multiple)، header/footer، fastScroll و android:entries بدون آداپتور پشتیبانی می‌کند.
  • محدودیت‌های ListView: فقط جهت عمودی، بدون انیمیشن، بازنویسی کامل در هر تغییر.
  • ListView به عنوان منسوخ علامت‌گذاری نشده است، اما Google RecyclerView را برای همه پروژه‌های جدید توصیه می‌کند.
  • برای لیست‌های تا 50 المان ListView کافی است؛ برای هر چیزی پیچیده‌تر — از RecyclerView استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید