RecyclerView — ما هو، ViewHolder و Adapter في Android

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

RecyclerView هو مكون من Android Jetpack لعرض مجموعات كبيرة من البيانات بكفاءة على شكل قوائم وشبكات. على عكس ListView، يعيد RecyclerView استخدام ViewHolders ويدير التخطيط عبر LayoutManager. وفقًا لجوجل (Android Developers، 2026)، يعالج RecyclerView ما يصل إلى 60 إطارًا في الثانية عند التمرير في قائمة تضم أكثر من 1000 عنصر بفضل تجميع ViewHolders وخوارزمية الفروق DiffUtil. اقرأ المزيد عن بنية تطبيقات Android في مقالتنا حول ListView.

أهم النقاط

  • RecyclerView هو مكون حديث للقوائم في Android، حل محل ListView في Android 5.0 (API 21).
  • ViewHolder هو نمط لتخزين مراجع View مؤقتًا، مما يسرع إعادة استخدام الخلايا أثناء التمرير.
  • Adapter هو حلقة الوصل بين البيانات و RecyclerView، المسؤول عن إنشاء ViewHolders وربط البيانات.
  • LayoutManager يحدد موضع العناصر: قائمة رأسية/أفقية (LinearLayoutManager)، شبكة (GridLayoutManager)، متعرجة (StaggeredGridLayoutManager).
  • DiffUtil هي أداة لحساب الفرق بين القوائم القديمة والجديدة، مما يقلل من إعادة رسم العناصر.

ما هو RecyclerView؟

RecyclerView هو مكون من مكتبة Android Jetpack، تم تقديمه في Google I/O 2014 كجزء من Android Support Library v7. إنه مصمم لعرض القوائم والشبكات الديناميكية بأداء عالٍ. ظهر RecyclerView كبديل لـ ListView ويحل المشكلة الرئيسية لسابقه — عدم فرض إعادة استخدام ViewHolder.

بنية RecyclerView مبنية على نمط Model-View-Adapter: يتم تخزين البيانات في نموذج (List، LiveData، Flow)، وتتم إدارة العرض بواسطة Adapter، ويتحكم LayoutManager في التخطيط. كل مكون مسؤول عن مجاله الخاص، مما يجعل النظام مرنًا وقابلًا للتوسيع.

وفقًا لاستطلاع Android Developers (2025)، يُستخدم RecyclerView في 78% من تطبيقات Android في أفضل 100 على Google Play. يظل أداء التمرير لقائمة تضم أكثر من 1000 عنصر مستقرًا عند 60 إطارًا في الثانية على الأجهزة متوسطة المدى بفضل ثلاث آليات: تجميع ViewHolders، وحساب التحديثات القائم على الفروق، والتحميل غير المتزامن للبيانات.

متى تستخدم RecyclerView

RecyclerView مثالي لـ: قوائم بعدد غير معروف من العناصر (التغذية، الدردشات، السجلات)، شبكات بأعمدة N (المعارض، الكتالوجات)، تخطيطات متعرجة (لوحات Pinterest)، وقوائم مع رسوم متحركة للإضافة/الإزالة. للشاشات البسيطة التي تحتوي على 2–5 عناصر، LinearLayout أو ScrollView أبسط وأكثر كفاءة — RecyclerView يضيف عبئًا إضافيًا.

إعداد الاعتماد

لاستخدام RecyclerView، أضف الاعتماد إلى build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. الحد الأدنى لإصدار API هو 14 (Android 4.0). بدءًا من AndroidX RecyclerView 1.3.0، تتوفر وحدات recyclerview-selection لتحديد العناصر و recyclerview-swipe لإيماءات السحب.

كيف يعمل RecyclerView: ثلاثة مكونات رئيسية

RecyclerView مقسم إلى ثلاث طبقات مستقلة: Adapter يدير البيانات و ViewHolders، LayoutManager يتعامل مع تحديد موضع العناصر، و ItemAnimator يتحكم في الرسوم المتحركة. تسمح هذه البنية باستبدال أي طبقة دون تعديل الطبقات الأخرى — على سبيل المثال، التبديل من LinearLayoutManager إلى GridLayoutManager دون تغيير كود Adapter.

المكونالمسؤولياتالتنفيذ
Adapterإنشاء ViewHolders، ربط البيانات بـ View، الإخطار بالتغييراتRecyclerView.Adapter<VH>
ViewHolderتخزين مراجع View مؤقتًا، تجنب findViewById()RecyclerView.ViewHolder
LayoutManagerتحديد موضع العناصر، تحديد اتجاه التمريرLinearLayoutManager، GridLayoutManager، StaggeredGridLayoutManager
ItemAnimatorالرسوم المتحركة لإضافة وإزالة ونقل العناصرDefaultItemAnimator
ItemDecorationرسم الفواصل والمسافات والخلفيات بين العناصرRecyclerView.ItemDecoration

دورة العمل: (1) يقوم Adapter بإنشاء ViewHolder عبر onCreateViewHolder()، (2) يحدد LayoutManager موضع View الجديد على الشاشة، (3) يربط Adapter البيانات عبر onBindViewHolder()، (4) أثناء التمرير، تدخل ViewHolders التي تغادر الشاشة إلى المجمع (RecycledViewPool) لإعادة الاستخدام. بدلاً من إنشاء Views جديدة، يتم إعادة استخدام الموجودة — هذه هي آلية الأداء الرئيسية.

تنفيذ ViewHolder في RecyclerView

ViewHolder هو فئة تخزن مراجع Views (TextView، ImageView، إلخ) داخل عنصر القائمة. بدون ViewHolder، كل تمرير قد يشغل findViewById() — واحدة من أغلى عمليات واجهة Android. يلغي ViewHolder هذه الاستدعاء بتخزين المراجع مؤقتًا بعد الربط الأولي.

kotlin
// نموذج البيانات
data class Article(
    title: String,
    summary: String,
    author: String,
    imageUrl: String?
)

// ViewHolder — يخزن مراجع View
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val titleText = itemView.findViewById<TextView>(R.id.tvTitle)
    private val summaryText = itemView.findViewById<TextView>(R.id.tvSummary)
    private val authorText = itemView.findViewById<TextView>(R.id.tvAuthor)

    fun bind(article: Article) {
        titleText.text = article.title
        summaryText.text = article.summary
        authorText.text = article.author
    }
}

قاعدة عملية: يجب تعريف كل View في تخطيط العنصر كخاصية ViewHolder مع findViewById() مرة واحدة بالضبط — في المُنشئ. لا تستدعي طريقة bind() findViewById()، بل تقوم فقط بتعيين النص والألوان والمستمعين. هذا الأسلوب يسرع تمرير القائمة بنسبة 30–50% وفقًا لجوجل (Android Performance Patterns، 2025).

أنواع LayoutManager: اختيار ترتيب العناصر

LayoutManager يحدد كيفية وضع العناصر داخل RecyclerView. يوفر Android ثلاث تطبيقات افتراضية: LinearLayoutManager و GridLayoutManager و StaggeredGridLayoutManager. يؤثر اختيار LayoutManager على إدراك البيانات وأداء التمرير.

LayoutManagerالترتيبمتى يستخدمطريقة الإعداد
LinearLayoutManagerقائمة (رأسية أو أفقية)التغذية، الدردشات، الإشعارات، قوائم المنتجاتLinearLayoutManager(context)
GridLayoutManagerشبكة بأعمدة Nالمعارض، الكتالوجات، الأيقونات، شبكات الصورGridLayoutManager(context, spanCount)
StaggeredGridLayoutManagerشبكة متعرجة (ارتفاعات مختلفة للعناصر)لوحات Pinterest، الملاحظات، معارض masonryStaggeredGridLayoutManager(spanCount, orientation)
kotlin
// إعداد LinearLayoutManager في Fragment
class ArticleListFragment : Fragment() {
    private lateinit var binding: FragmentArticleListBinding

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val recyclerView = binding.recyclerView
        recyclerView.layoutManager = LinearLayoutManager(requireContext())
        recyclerView.adapter = ArticleAdapter(articlesList)

        // تحسين: حجم ثابت + مجمع
        recyclerView.setHasFixedSize(true)
        recyclerView.setItemViewCacheSize(20)

        // فاصل بين العناصر
        val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
        recyclerView.addItemDecoration(divider)
    }
}

الأداء: LinearLayoutManager مع التمرير الرأسي هو الخيار الأسرع لأن العناصر تُرتب تسلسليًا ويحسب LayoutManager المواضع في O(1). يتطلب GridLayoutManager حسابات إضافية لتقسيم الأعمدة لكنه يبقى فعالاً مع spanCount ثابت. StaggeredGridLayoutManager هو الأبطأ من بين الثلاثة لأنه يحتاج لحساب موضع العناصر في التتالي مع مراعاة ارتفاعات العناصر المجاورة. استخدمه فقط عندما تكون ارتفاعات الخلايا المختلفة مطلوبة بصريًا.

Adapter و DiffUtil: تحديث فعال للبيانات

Adapter في RecyclViewfinder يطبق نمط Adapter من GoF: يحول البيانات (List<T>) إلى ViewHolders معروضة على الشاشة. عندما تتغير البيانات، يمكن لـ Adapter إخطار RecyclerView عبر notifyDataSetChanged() و notifyItemInserted() وطرق مماثلة. المشكلة هي أن notifyDataSetChanged() يعيد رسم جميع العناصر المرئية، مما يسبب التقطيع (jank).

kotlin
// Adapter مع DiffUtil للتحديثات الفعالة
class ArticleAdapter : RecyclerView.Adapter<ArticleViewHolder>() {

    private var articles: List<Article> = emptyList()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_article, parent, false)
        return ArticleViewHolder(view)
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        holder.bind(articles[position])
    }

    override fun getItemCount(): Int = articles.size

    // تحديث القائمة عبر DiffUtil
    fun updateArticles(newArticles: List<Article>) {
        val diffCallback = ArticleDiffCallback(articles, newArticles)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        articles = newArticles
        diffResult.dispatchUpdatesTo(this)
    }
}

// Callback لـ DiffUtil
class ArticleDiffCallback(
    private val oldList: List<Article>,
    private val newList: List<Article>
) : DiffUtil.Callback() {

    override fun getOldListSize() = oldList.size
    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos].title == newList[newPos].title
    }

    override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos] == newList[newPos]
    }
}

DiffUtil يستخدم خوارزمية الفروق لـ Eugene W. Myers، التي تحسب الحد الأدنى من العمليات (إدراج، إزالة، نقل، تغيير) لتحويل القائمة القديمة إلى الجديدة. لقائمة من 100 عنصر، يعمل DiffUtil في 1–5 مللي ثانية (Google، 2025). للقوائم الكبيرة (10000+)، استخدم AsyncListDiffer أو PagingDataAdapter من مكتبة Paging — يقومون بالحسابات في خيط خلفية.

RecyclerView مقابل ListView: مقارنة

ListView هو سلف RecyclerView، تم تقديمه في Android 1.0 (API 1). يعرض القوائم أيضًا لكنه لا يفرض ViewHolder، ولا يدعم LayoutManager، ولا يمكنه تحريك التغييرات. تم تصميم RecyclerView كبديل لـ ListView بهندسة أكثر صرامة وأداء أفضل.

المعيارRecyclerViewListView
ViewHolderإلزامي (هندسة)موصى به لكن غير مطلوب
LayoutManagerفصل: قائمة، شبكة، متعرجةقائمة رأسية فقط
رسوم التغييرات المتحركةDefaultItemAnimator مدمجلا دعم مدمج
الإخطار بالتغييراتدقيق: notifyItemInserted/RemovednotifyDataSetChanged() فقط للقائمة بأكملها
DiffUtilدعم عبر DiffUtil.Callbackلا يوجد مكافئ
الفواصلItemDecoration (مخصص)android:divider (مدمج)
الحد الأدنى للإصدارAPI 14 (Android 4.0) عبر AndroidXAPI 1 (جميع الإصدارات)

توصية جوجل (Android Developers، 2026): استخدم RecyclerView لجميع المشاريع الجديدة. يمكن الاحتفاظ بـ ListView في قواعد الأكواد القديمة حيث لا تبرر الترحيل التكلفة. يوفر RecyclerView أداء أفضل مع 100+ عنصر، ويدعم التمرير الأفقي والشبكات بدون حلول إضافية. في Android 15، تم وضع علامة رسمية على ListView كقديم جزئيًا.

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

RecyclerView لا يعرض العناصر — ماذا أفعل؟

تحقق من ثلاثة شروط: (1) تم تعيين Adapter عبر recyclerView.adapter = adapter، (2) تم تعيين LayoutManager عبر recyclerView.layoutManager = LinearLayoutManager(context)، (3) قائمة البيانات ليست فارغة وتم إخطار adapter (notifyDataSetChanged()). خطأ شائع هو تعيين البيانات بعد استدعاء notifyDataSetChanged(). يجب تعيين البيانات قبل الإخطار. تحقق من حجم القائمة: يجب أن يكون adapter.itemCount أكبر من 0.

لماذا يتم تمرير RecyclerView ببطء؟

يحدث البطء أثناء التمرير (jank) عندما ينفذ onBindViewHolder() عمليات ثقيلة: تحميل الصور دون تخزين مؤقت، findViewById() داخل bind، حسابات معقدة. الحلول: (1) استخدم Glide أو Coil لتحميل الصور غير المتزامن، (2) انقل جميع استدعاءات findViewById() إلى مُنشئ ViewHolder، (3) لحسابات الفروق، استخدم AsyncListDiffer. فعّل إنشاء ملفات تعريف GPU في Android Studio (Profile GPU Rendering) للعثور على الإطارات البطيئة.

كيفية إنشاء RecyclerView أفقي؟

مرر الاتجاه إلى LayoutManager: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). المعامل الثاني هو reverseLayout (false = من اليسار إلى اليمين). لشبكة أفقية، استخدم GridLayoutManager مع اتجاه أفقي أو LinearSnapHelper مخصص للتمرير الصفحي. القوائم الأفقية مناسبة تمامًا للدوّارات (carousels) للمنتجات والصور والفئات.

كيفية إضافة click listener لعنصر RecyclerView؟

RecyclerView لا يحتوي على onClickListener مدمج (على عكس ListView مع onItemClickListener). قم بتنفيذه في ViewHolder: مرر لامدا إلى المُنشئ وعيّن itemView.setOnClickListener. مثال: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). استدعِ onItemClick(article) داخل bind(). بديل هو استخدام RecyclerView.addOnItemTouchListener() لمعالجة الإيماءات.

ما هو RecycledViewPool وكيفية تكوينه؟

RecycledViewPool هو مجمع داخلي لـ ViewHolders التي أصبحت غير مرئية أثناء التمرير. بدلاً من تدميرها، يتم الاحتفاظ بها في المجمع وإعادة استخدامها. افتراضيًا، يخزن المجمع 5 ViewHolders من كل نوع. قم بزيادة الحجم للتخطيطات المعقدة: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). إذا كان RecyclerView يحتوي على أنواع متعددة من View (viewType)، يحتفظ المجمع بمكدسات منفصلة لكل نوع. مشاركة RecycledViewPool بين RecyclerViews مختلفة توفر الذاكرة في الشاشات ذات القوائم المتداخلة.

الخلاصة

  • RecyclerView هو مكون حديث من Android Jetpack للقوائم والشبكات بأداء يصل إلى 60 إطارًا في الثانية.
  • ViewHolder هو نمط إلزامي لتخزين Views مؤقتًا، يلغي findViewById() عند كل تمرير.
  • Adapter يدير البيانات وينشئ ViewHolders عبر onCreateViewHolder و onBindViewHolder.
  • LayoutManager — ثلاث تطبيقات: LinearLayoutManager (قائمة)، GridLayoutManager (شبكة)، StaggeredGridLayoutManager (متعرجة).
  • DiffUtil — خوارزمية لتحديثات القائمة الدنيا: إعادة رسم دقيقة بدلاً من التحديث الكامل.
  • يُستخدم RecyclerView في 78% من تطبيقات Android في أفضل 100 وهو موصى به رسميًا من جوجل بدلاً من ListView.
  • يتم تحقيق أداء RecyclerView من خلال تجميع ViewHolders والإخطارات الدقيقة والحساب غير المتزامن للفروق.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

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

اقرأ أيضًا