إنشاء قوائم ديناميكية باستخدام RecyclerView   جزء من Android Jetpack.

تجربة طريقة Compose
‫Jetpack Compose هي مجموعة أدوات واجهة المستخدم المقترَحة لنظام التشغيل Android. تعرَّف على كيفية استخدام التنسيقات في Compose.

تسهّل RecyclerView عرض مجموعات كبيرة من البيانات بكفاءة. أنت تقدّم البيانات وتحدّد مظهر كل عنصر، وتنشئ مكتبة RecyclerView العناصر ديناميكيًا عند الحاجة إليها.

كما يشير الاسم، فإن RecyclerView يعيد تدوير هذه العناصر الفردية. عندما يختفي عنصر من الشاشة، لا يتلف RecyclerView طريقة عرضه. بدلاً من ذلك، يعيد RecyclerView استخدام العرض للعناصر الجديدة التي تم الانتقال إليها على الشاشة. يُحسِّن RecyclerView الأداء واستجابة تطبيقك، ويقلِّل من استهلاك الطاقة.

الفئات الرئيسية

تعمل عدة فئات معًا لإنشاء قائمتك الديناميكية.

  • RecyclerView هو ViewGroup الذي يحتوي على طرق العرض المقابلة لبياناتك. وهو عرض بحد ذاته، لذا يمكنك إضافة RecyclerView إلى التصميم بالطريقة نفسها التي تضيف بها أي عنصر في واجهة المستخدم.

  • يتم تحديد كل عنصر فردي في القائمة من خلال عنصر حامل العرض. عند إنشاء حامل العرض، لا يحتوي على أي بيانات مرتبطة به. بعد إنشاء حامل العرض، يربطه RecyclerView ببياناته. يمكنك تحديد حامل العرض من خلال توسيع RecyclerView.ViewHolder.

  • يطلب RecyclerView طرق العرض ويربطها ببياناتها من خلال استدعاء الطرق في المحوّل. يمكنك تحديد المحوّل من خلال توسيع RecyclerView.Adapter.

  • يرتّب مدير التنسيق العناصر الفردية في قائمتك. يمكنك استخدام أحد مدراء التنسيق الذين توفّرهم مكتبة RecyclerView، أو يمكنك تحديد مدير التنسيق الخاص بك. تستند جميع فئات إدارة التنسيق إلى الفئة المجرّدة LayoutManager في المكتبة.

يمكنك الاطّلاع على كيفية تجميع كل الأجزاء معًا في نموذج تطبيق RecyclerView (Kotlin) أو نموذج تطبيق RecyclerView (Java).

خطوات تنفيذ RecyclerView

إذا كنت ستستخدم RecyclerView، هناك بعض الأمور التي عليك تنفيذها، وهي موضّحة بالتفصيل في الأقسام التالية.

  1. حدِّد شكل القائمة أو الشبكة. عادةً، يمكنك استخدام أحد مدراء التنسيق العاديين في مكتبة RecyclerView.

  2. صمِّم مظهر كل عنصر في القائمة وسلوكه. استنادًا إلى هذا التصميم، يمكنك توسيع فئة ViewHolder. يوفّر إصدارك من ViewHolder جميع الوظائف لعناصر القائمة. حامل العرض هو برنامج تضمين حول View، وتتم إدارة هذا العرض من خلال RecyclerView.

  3. حدِّد Adapter الذي يربط بياناتك بمشاهدات ViewHolder.

تتوفّر أيضًا خيارات تخصيص متقدّمة تتيح لك تخصيص RecyclerView وفقًا لاحتياجاتك الدقيقة.

التخطيط للتنسيق

يتم ترتيب العناصر في RecyclerView حسب فئة LayoutManager. توفّر مكتبة RecyclerView ثلاثة مدراء للتنسيق، يتعاملون مع حالات التنسيق الأكثر شيوعًا:

  • LinearLayoutManager ترتّب العناصر في قائمة أحادية البُعد.
  • GridLayoutManager يرتّب العناصر في شبكة ثنائية الأبعاد:
    • إذا تم ترتيب الشبكة عموديًا، يحاول GridLayoutManager جعل جميع العناصر في كل صف لها العرض والارتفاع نفسهما، ولكن يمكن أن يكون للصفوف المختلفة ارتفاعات مختلفة.
    • إذا تم ترتيب الشبكة أفقيًا، يحاول GridLayoutManager جعل جميع العناصر في كل عمود لها العرض والارتفاع نفسهما، ولكن يمكن أن يكون للأعمدة المختلفة عروض مختلفة.
  • StaggeredGridLayoutManager يشبه GridLayoutManager، ولكنه لا يتطلب أن يكون للعناصر في الصف نفسه الارتفاع نفسه (للشبكات العمودية) أو أن يكون للعناصر في العمود نفسه العرض نفسه (للشبكات الأفقية). والنتيجة هي أن العناصر الموجودة في صف أو عمود يمكن أن ينتهي بها الأمر إلى الإزاحة عن بعضها البعض.

تحتاج أيضًا إلى تصميم تخطيط العناصر الفردية. ستحتاج إلى هذا التنسيق عند تصميم حامل العرض، كما هو موضّح في القسم التالي.

تنفيذ المحوّل وحامل العرض

بعد تحديد التنسيق، عليك تنفيذ Adapter وViewHolder. تعمل هاتان الفئتان معًا لتحديد كيفية عرض بياناتك. ViewHolder هو برنامج تضمين حول View يحتوي على تخطيط لعنصر فردي في القائمة. ينشئ Adapter كائنات ViewHolder حسب الحاجة ويضبط أيضًا البيانات لهذه العروض. تُعرف عملية ربط طرق العرض ببياناتها باسم الربط.

عند تحديد المحوّل، يمكنك إلغاء ثلاث طرق رئيسية:

  • onCreateViewHolder(): يستدعي RecyclerView هذه الطريقة كلما احتاج إلى إنشاء ViewHolder جديد. تنشئ هذه الطريقة ViewHolder وView المرتبط به وتهيئهما، ولكنها لا تملأ محتوى العرض، لأنّه لم يتم ربط ViewHolder ببيانات معيّنة بعد.

  • ‫onBindViewHolder(): يستدعي "RecyclerView" هذه الطريقة لربط ViewHolder بالبيانات. تجلب الطريقة البيانات المناسبة وتستخدمها لملء تنسيق حامل العرض. على سبيل المثال، إذا كان RecyclerView يعرض قائمة بالأسماء، قد تعثر الطريقة على الاسم المناسب في القائمة وتملأ عنصر واجهة المستخدم TextView الخاص بحامل العرض.

  • getItemCount(): يستدعي RecyclerView هذه الطريقة للحصول على حجم مجموعة البيانات. على سبيل المثال، في تطبيق دفتر العناوين، قد يكون هذا هو إجمالي عدد العناوين. يستخدم RecyclerView هذه الدالة لتحديد متى لا يتوفّر المزيد من العناصر التي يمكن عرضها.

في ما يلي مثال نموذجي لمحوِّل بسيط يتضمّن ViewHolder متداخلة تعرض قائمة بالبيانات. في هذه الحالة، يعرض RecyclerView قائمة بسيطة من العناصر النصية. يتم تمرير مصفوفة من السلاسل التي تحتوي على النص لعناصر ViewHolder إلى المحوِّل.

Kotlin

class CustomAdapter(private val dataSet: Array<String>) :
        RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView

        init {
            // Define click listener for the ViewHolder's View
            textView = view.findViewById(R.id.textView)
        }
    }

    // Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        // Create a new view, which defines the UI of the list item
        val view = LayoutInflater.from(viewGroup.context)
                .inflate(R.layout.text_row_item, viewGroup, false)

        return ViewHolder(view)
    }

    // Replace the contents of a view (invoked by the layout manager)
    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.textView.text = dataSet[position]
    }

    // Return the size of your dataset (invoked by the layout manager)
    override fun getItemCount() = dataSet.size

}

Java

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {

    private String[] localDataSet;

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;

        public ViewHolder(View view) {
            super(view);
            // Define click listener for the ViewHolder's View

            textView = (TextView) view.findViewById(R.id.textView);
        }

        public TextView getTextView() {
            return textView;
        }
    }

    /**
     * Initialize the dataset of the Adapter
     *
     * @param dataSet String[] containing the data to populate views to be used
     * by RecyclerView
     */
    public CustomAdapter(String[] dataSet) {
        localDataSet = dataSet;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view, which defines the UI of the list item
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.text_row_item, viewGroup, false);

        return new ViewHolder(view);
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.getTextView().setText(localDataSet[position]);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return localDataSet.length;
    }
}

يتم تحديد تنسيق كل عنصر عرض في ملف تنسيق XML، كالمعتاد. في هذه الحالة، يحتوي التطبيق على ملف text_row_item.xml على النحو التالي:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_height"
    android:layout_marginLeft="@dimen/margin_medium"
    android:layout_marginRight="@dimen/margin_medium"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/element_text"/>
</FrameLayout>

الخطوات التالية

يوضّح مقتطف الرمز التالي كيفية استخدام RecyclerView.

Kotlin

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dataset = arrayOf("January", "February", "March")
        val customAdapter = CustomAdapter(dataset)

        val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = customAdapter

    }

}

Java

RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.layoutManager = new LinearLayoutManager(this)
recyclerView.setAdapter(customAdapter);

توفّر المكتبة أيضًا العديد من الطرق لتخصيص عملية التنفيذ. لمزيد من المعلومات، يُرجى الاطّلاع على التخصيص المتقدّم لـ RecyclerView.

تفعيل ميزة "العرض حتى حافة الشاشة"

اتّبِع الخطوات التالية لتفعيل ميزة "العرض حتى حافة الشاشة" على جهاز RecyclerView:

  • يمكنك إعداد ميزة "العرض حتى حافة الشاشة" المتوافقة مع الأنظمة القديمة من خلال استدعاء enableEdgeToEdge().
  • إذا كانت عناصر القائمة تتداخل في البداية مع أشرطة النظام، يمكنك تطبيق الأجزاء الداخلية على RecyclerView. يمكنك إجراء ذلك من خلال ضبط android:fitsSystemWindows على true أو باستخدام ViewCompat.setOnApplyWindowInsetsListener.
  • يمكنك السماح بعرض عناصر القائمة تحت أشرطة النظام أثناء الانتقال للأعلى أو للأسفل من خلال ضبط android:clipToPadding على false في RecyclerView.

يعرض الفيديو التالي جهاز RecyclerView مع إيقاف ميزة "العرض على كامل الشاشة" (على اليمين) وتفعيلها (على اليسار):

مثال على رمز الإدراج:

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(
  findViewById(R.id.my_recycler_view)
  ) { v, insets ->
      val innerPadding = insets.getInsets(
          WindowInsetsCompat.Type.systemBars()
                  or WindowInsetsCompat.Type.displayCutout()
          // If using EditText, also add
          // "or WindowInsetsCompat.Type.ime()" to
          // maintain focus when opening the IME
      )
      v.setPadding(
          innerPadding.left,
          innerPadding.top,
          innerPadding.right,
          innerPadding.bottom)
      insets
  }
  

Java

ViewCompat.setOnApplyWindowInsetsListener(
  activity.findViewById(R.id.my_recycler_view),
  (v, insets) -> {
      Insets innerPadding = insets.getInsets(
              WindowInsetsCompat.Type.systemBars() |
                      WindowInsetsCompat.Type.displayCutout()
              // If using EditText, also add
              // "| WindowInsetsCompat.Type.ime()" to
              // maintain focus when opening the IME
      );
      v.setPadding(
              innerPadding.left,
              innerPadding.top,
              innerPadding.right,
              innerPadding.bottom
      );
      return insets;
  }
);
  

ملف XML الخاص بـ RecyclerView:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:clipToPadding="false"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

مراجع إضافية

لمزيد من المعلومات حول الاختبار على Android، يُرجى الاطّلاع على المراجع التالية.

نماذج التطبيقات