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

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

تسهّل أداة 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 مع إيقاف ميزة &quot;العرض من الحافة إلى الحافة&quot; (على اليمين) وتفعيلها (على اليسار):

مثال على رمز الإطار الداخلي:

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;
  }
);
  

ملف RecyclerView XML:

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

مراجع إضافية

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

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