ایجاد فهرست‌های پویا با 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 با داده‌ها فراخوانی می‌کند. The method fetches the appropriate data and uses the data to fill in the view holder's layout. برای مثال، اگر RecyclerView فهرستی از نام‌ها را نمایش دهد، این روش ممکن است نام مناسب را در فهرست پیدا کند و ابزاره TextView نگهدارنده نما را تکمیل کند.

  • getItemCount(): RecyclerView این روش را برای دریافت اندازه مجموعه داده فراخوانی می‌کند. برای مثال، در برنامه دفترچه نشانی، این مقدار ممکن است تعداد کل نشانی‌ها باشد. RecyclerView از این برای تعیین زمانی استفاده می‌کند که دیگر موردی برای نمایش وجود ندارد.

در اینجا نمونه‌ای معمول از یک آداپتور ساده با ViewHolder تودرتو آورده شده است که فهرستی از داده‌ها را نمایش می‌دهد. در این مورد، RecyclerView فهرست ساده‌ای از عناصر نوشتاری را نمایش می‌دهد. آداپتور آرایه‌ای از رشته‌های حاوی نوشتار را برای عناصر ViewHolder ارسال می‌کند.

کاتلین

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

}

جاوا

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 استفاده کنید.

کاتلین

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

    }

}

جاوا

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 را با نمایش لبه‌به‌لبه غیرفعال (سمت راست) و فعال (سمت چپ) نشان می‌دهد:

کد درج نمونه:

کاتلین

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
  }
  

جاوا

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، به منابع زیر مراجعه کنید.

برنامه‌های نمونه