สร้างรายการแบบไดนามิกด้วย RecyclerView   เป็นส่วนหนึ่งของ Android Jetpack

ลองใช้วิธีแบบ Compose
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีทำงานกับเลย์เอาต์ใน Compose

RecyclerView ช่วยให้แสดงชุดข้อมูลขนาดใหญ่ได้อย่างมีประสิทธิภาพและง่ายดาย คุณเป็นผู้ระบุข้อมูลและกำหนดลักษณะของแต่ละรายการ และไลบรารี RecyclerView จะสร้างองค์ประกอบแบบไดนามิกเมื่อจำเป็น

RecyclerView จะรีไซเคิลองค์ประกอบแต่ละรายการตามชื่อของมัน เมื่อรายการเลื่อนออกจากหน้าจอ RecyclerView จะไม่ทำลายมุมมองของรายการนั้น แต่ RecyclerView จะนำมุมมองมาใช้ซ้ำสำหรับรายการใหม่ที่เลื่อนเข้ามาในหน้าจอ RecyclerView ช่วยปรับปรุงประสิทธิภาพและการตอบสนองของแอป รวมถึงลดการใช้พลังงาน

คลาสหลัก

คลาสหลายคลาสจะทำงานร่วมกันเพื่อสร้างรายการแบบไดนามิก

  • RecyclerView คือ ViewGroup ที่มีข้อมูลพร็อพเพอร์ตี้ที่สอดคล้องกับข้อมูลของคุณ เนื่องจากเป็นมุมมอง คุณจึงเพิ่ม RecyclerView ลงในเลย์เอาต์ได้เช่นเดียวกับการเพิ่มองค์ประกอบ UI อื่นๆ

  • แต่ละองค์ประกอบในรายการจะกำหนดโดยออบเจ็กต์ view holder เมื่อสร้าง ViewHolder แล้ว จะไม่มีข้อมูลใดๆ ที่เชื่อมโยงกับ ViewHolder นั้น หลังจากสร้าง View Holder แล้ว RecyclerView จะเชื่อมโยง View Holder กับข้อมูลของตัวเอง คุณกำหนด ViewHolder โดยขยาย RecyclerView.ViewHolder

  • RecyclerView จะขอข้อมูลพร็อพเพอร์ตี้และเชื่อมโยงข้อมูลพร็อพเพอร์ตี้กับข้อมูลของตนโดยเรียกใช้เมธอดใน adapter คุณกำหนดอะแดปเตอร์โดยขยาย RecyclerView.Adapter

  • เครื่องมือจัดการเลย์เอาต์จะจัดเรียงองค์ประกอบแต่ละรายการในรายการ คุณสามารถใช้เครื่องมือจัดการเลย์เอาต์ที่ไลบรารี RecyclerView มีให้ หรือจะกำหนดเองก็ได้ LayoutManager ทั้งหมดจะอิงตามคลาสแอบสแตรกต์ LayoutManager ของไลบรารี

คุณดูวิธีที่องค์ประกอบทั้งหมดทำงานร่วมกันได้ในแอปตัวอย่าง RecyclerView (Kotlin) หรือแอปตัวอย่าง RecyclerView (Java)

ขั้นตอนการติดตั้งใช้งาน RecyclerView

หากคุณจะใช้ RecyclerView คุณต้องทำสิ่งต่อไปนี้ ซึ่งจะอธิบายอย่างละเอียดในส่วนต่อไปนี้

  1. เลือกรูปลักษณ์ของรายการหรือตารางกริด โดยปกติแล้ว คุณสามารถใช้เครื่องมือจัดการเลย์เอาต์มาตรฐานของไลบรารี RecyclerView ได้

  2. ออกแบบลักษณะและลักษณะการทำงานของแต่ละองค์ประกอบในรายการ ขยายคลาส ViewHolder โดยอิงตามการออกแบบนี้ ViewHolder เวอร์ชันของคุณมีฟังก์ชันการทำงานทั้งหมดสำหรับรายการต่างๆ ตัวยึดมุมมองของคุณเป็น Wrapper รอบ View และมุมมองนั้นได้รับการจัดการโดย RecyclerView

  3. กำหนด Adapter ที่เชื่อมโยงข้อมูลของคุณกับข้อมูลพร็อพเพอร์ตี้ ViewHolder

นอกจากนี้ยังมีตัวเลือกการปรับแต่งขั้นสูงที่ช่วยให้คุณปรับแต่ง RecyclerView ให้ตรงกับความต้องการได้

วางแผนเลย์เอาต์

รายการใน RecyclerView จะจัดเรียงตามคลาส LayoutManager ไลบรารี RecyclerView มี LayoutManager 3 รายการ ซึ่งจัดการสถานการณ์เลย์เอาต์ที่พบบ่อยที่สุด ดังนี้

  • LinearLayoutManager จัดเรียงรายการในลิสต์แบบมิติเดียว
  • GridLayoutManager จัดเรียงรายการในตารางกริด 2 มิติ
    • หากตารางกริดจัดเรียงในแนวตั้ง GridLayoutManager จะพยายามทำให้องค์ประกอบทั้งหมดในแต่ละแถวมีความกว้างและความสูงเท่ากัน แต่แถวที่ต่างกันอาจมีความสูงต่างกันได้
    • หากตารางกริดจัดเรียงในแนวนอน GridLayoutManager จะพยายามทำให้องค์ประกอบทั้งหมดในแต่ละคอลัมน์มีความกว้างและความสูงเท่ากัน แต่คอลัมน์ต่างๆ อาจมีความกว้างต่างกันได้
  • StaggeredGridLayoutManager คล้ายกับ GridLayoutManager แต่ไม่จำเป็นต้องให้รายการในแถวมีความสูงเท่ากัน (สำหรับตารางกริดแนวตั้ง) หรือรายการในคอลัมน์เดียวกันมีความกว้างเท่ากัน (สำหรับตารางกริดแนวนอน) ซึ่งส่งผลให้รายการในแถวหรือคอลัมน์อาจไม่ตรงกัน

นอกจากนี้ คุณยังต้องออกแบบเลย์เอาต์ของแต่ละรายการด้วย คุณต้องใช้เลย์เอาต์นี้เมื่อออกแบบตัวยึดมุมมองตามที่อธิบายไว้ในส่วนถัดไป

ติดตั้งใช้งานอะแดปเตอร์และ View Holder

เมื่อกำหนดเลย์เอาต์แล้ว คุณจะต้องใช้ Adapter และ ViewHolder คลาสทั้ง 2 คลาสนี้จะทำงานร่วมกันเพื่อกำหนดวิธีแสดงข้อมูล ViewHolder เป็น Wrapper รอบ View ที่มีเลย์เอาต์สำหรับแต่ละรายการในลิสต์ Adapter จะสร้างออบเจ็กต์ ViewHolder ตามความจำเป็น และตั้งค่าข้อมูลสำหรับมุมมองเหล่านั้นด้วย กระบวนการเชื่อมโยงมุมมองกับข้อมูลเรียกว่าการเชื่อมโยง

เมื่อกำหนดอะแดปเตอร์ คุณจะลบล้างเมธอดหลัก 3 เมธอดต่อไปนี้

  • onCreateViewHolder(): RecyclerView จะเรียกใช้เมธอดนี้เมื่อใดก็ตามที่ต้องการสร้าง ViewHolder ใหม่ เมธอดนี้จะสร้างและเริ่มต้น ViewHolder และ View ที่เชื่อมโยง แต่จะไม่กรอกเนื้อหาของมุมมอง เนื่องจาก ViewHolder ยังไม่ได้เชื่อมโยงกับข้อมูลที่เฉพาะเจาะจง

  • onBindViewHolder(): RecyclerView เรียกใช้เมธอดนี้เพื่อเชื่อมโยง ViewHolder กับข้อมูล เมธอดจะดึงข้อมูลที่เหมาะสมและใช้ข้อมูลเพื่อกรอกเลย์เอาต์ของ ViewHolder ตัวอย่างเช่น หาก RecyclerView แสดงรายการชื่อ เมธอดอาจค้นหาชื่อที่เหมาะสมในรายการและกรอกข้อมูลในวิดเจ็ต TextView ของ View Holder

  • 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 ได้ที่แหล่งข้อมูลต่อไปนี้

แอปตัวอย่าง