إنشاء قوائم ديناميكية باستخدام 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 customization.

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

اتّبِع الخطوات التالية لتفعيل ميزة "العرض حتى حافة الشاشة" لـ 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، يُرجى الاطّلاع على المراجع التالية.

أمثلة على التطبيقات