ListView: ما هو، قوائم بسيطة في تطبيقات Android

المؤلف: IT Sectr نُشر: 2026-02-23 وقت القراءة: 8 دق

ListView هو مكون أساسي في Android لعرض القوائم العمودية، متاح منذ API 1 (Android 1.0). يقوم ListView بإنشاء قائمة من العناصر باستخدام ArrayAdapter ويدعم تخطيطات خلايا مخصصة. دعنا نفهم ما هو ListView، كيف نقوم بتكوينه وما هي المشاكل التي يخفيها هذا المكون القديم ولكن لا يزال مستخدماً. في IT Sectr نواجه ListView في المشاريع القديمة ونحافظ على التوافق مع Android 4.4. للمقارنة مع البديل الحديث، اقرأ المقال عن RecyclerView.

النقاط الرئيسية

  • ListView هو مكون مضمن في SDK أندرويد للقوائم العمودية، ظهر في Android 1.0.
  • ArrayAdapter هو محول قياسي لربط مصفوفة بيانات بـ ListView عبر toString() أو تخطيط مخصص.
  • SimpleAdapter هو محول للقوائم من بيانات Map، مناسب للشاشات البسيطة دون ViewModel.
  • onItemClickListener هو معالج نقر مضمن لا يتطلب تطبيقاً إضافياً في المحول.
  • convertView هو آلية إعادة استخدام View في ListView يجب على المطور تنفيذها يدوياً (مماثل لـ ViewHolder).

ما هو ListView في Android؟

ListView هو ViewGroup من حزمة android.widget مصمم لعرض قائمة قابلة للتمرير من العناصر. ينفذ ListView نمط AdapterView: تمر البيانات عبر محول (Adapter)، الذي يحول عناصر البيانات إلى Views تظهر على الشاشة. ListView موجود في SDK Android منذ الإصدار الأول (API 1) ولا يتطلب مكتبات إضافية.

المعمارية لـ ListView أبسط من RecyclerView: LayoutManager مضمن (تمرير عمودي فقط)، رسوم الحركة غائبة، وتضبط الفواصل عبر سمة XML android:divider. يضيف ListView تلقائياً OverScrollView وتمييز التركيز، مما يبسط إنشاء النماذج الأولية. وفقاً لـ Statista (2025)، حوالي 15% من تطبيقات Android في أعلى 100 في Play Store لا تزال تحتوي على ListView — بشكل أساسي مشاريع قديمة مع دعم ضئيل.

التاريخ والأهمية

كان ListView هو مكون القوائم الرئيسي في Android من 2008 إلى 2014، عندما تم تقديم RecyclerView في Google I/O 2014. على الرغم من كونه مهملاً جزئياً (soft-deprecated)، يظل ListView متاحاً في جميع إصدارات Android وليس مميزاً بـ @Deprecated في SDK API 35. إذا كان مشروعك يدعم 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 بمعرفات الودجات في التخطيط. SimpleAdapter مناسب للنماذج الأولية السريعة عندما لا تكون فئة محول منفصلة مطلوبة. الجانب السلبي هو عدم وجود كتابة آمنة للأنواع والتعقيد مع المنطق المخصص.

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 في فقدان إطارات FPS بسبب عدم وجود حسابات الفرق و ViewHolder المفروض. للقوائم ذات التحديثات المتكررة (الدردشة، التغذية، سجلات البيانات)، ListView غير مناسب — استخدم RecyclerView مع Paging 3. في IT Sectr، نقوم بترحيل ListView إلى RecyclerView عندما نعمل على شاشات قديمة بها أكثر من 50 عنصراً.

تكوين ListView عبر سمات XML

ListView يوفر مجموعة من سمات XML للتكوين السريع دون كود: android:divider (لون أو رسم الفاصل)، android:dividerHeight (السُمك)، android:scrollbars (أشرطة التمرير)، android:fastScrollEnabled (التمرير السريع عبر مقبض)، android:choiceMode (وضع التحديد: singleChoice، multipleChoice). تسمح سمة android:entries بملء القائمة من مصفوفة موارد (string-array) دون محول.

السمةالوصفمثال قيمة
android:dividerلون أو رسم الفاصل@color/gray_light
android:dividerHeightسُمك الفاصل بالبكسل/النقطة1dp
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تنفيذ يدويإلزامي حسب المعمارية
الرسوم المتحركةفقط انتقالات التلاشيDefaultItemAnimator (add, remove, move, change)
التحديثات المفصلةلا (فقط notifyDataSetChanged)notifyItemInserted/Removed/Changed
مستمع النقرonItemClickListener (مضمن)عبر ViewHolder (غير مضمن)
الأداءكافٍ للقوائم حتى 50 عنصراً60 FPS مستقرة لـ 1000+ عنصر

الاستنتاج: للمشاريع الجديدة، استخدم RecyclerView. احتفظ بـ ListView فقط في الكود القديم حيث تكون الترحيل مكلفة بشكل غير مبرر. وفقاً لإرشادات Material Design 3 (2026)، يجب أن تستخدم القوائم من أي درجة تعقيد RecyclerView لتلبية معايير الأداء والإتاحة الحديثة.

الأسئلة الشائعة

هل ListView مهمل أم لا؟

ListView غير مميز بـ @Deprecated في Android SDK API 35، ولكن Google لا يوصي باستخدامه في المشاريع الجديدة (soft-deprecated). يظل المكون عاملاً في جميع إصدارات Android. RecyclerView هو البديل الرسمي منذ 2014. إذا كنت تبدأ مشروعاً جديداً — اختر RecyclerView. إذا كنت تدعم موجوداً — سيستمر ListView في العمل دون تغييرات.

كيف أضيف header/footer إلى ListView؟

يدعم ListView طرق addHeaderView(View, Object, boolean) و addFooterView(View, Object, boolean). يتم إضافة الرأس والتذييل إلى القائمة العامة للعناصر ولكنهما لا يشاركان في المحول — يتم عرضهما دائماً في بداية ونهاية القائمة. مهم: أضف header/footer قبل تعيين المحول (setAdapter())، وإلا لن يتم أخذهما في الاعتبار. بعد إضافة رأس، تتغير معرفات مواقع العناصر بعدد الرؤوس.

لماذا لا يتمرر 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() فقط يعيد رسم العروض ولكنه لا يحدث البيانات. قم دائماً بتغيير البيانات عبر المحول، ليس مباشرة.

كيف أغير ارتفاع عنصر ListView؟

يتم تعيين ارتفاع عنصر ListView في XML تخطيط العنصر. إذا كان التخطيط يستخدم layout_height="wrap_content"، فإن الارتفاع يضبط حسب المحتوى. لارتفاع ثابت لجميع العناصر، استخدم محولاً مخصصاً يضبط LayoutParams في getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). لارتفاعات متنوعة، استخدم RecyclerView مع أنواع عرض مختلفة.

الملخص

  • ListView هو مكون أساسي في SDK Android للقوائم العمودية، متاح منذ 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 تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا