สร้างรายการแบบไดนามิกด้วย RecyclerView ส่วนหนึ่งของ Android Jetpack
RecyclerView ช่วยให้แสดงชุดข้อมูลขนาดใหญ่ได้อย่างมีประสิทธิภาพ คุณจะระบุข้อมูลและกำหนดลักษณะของแต่ละรายการ ส่วนไลบรารี RecyclerView จะสร้างองค์ประกอบแบบไดนามิกเมื่อจำเป็น
ดังที่ชื่อบอก RecyclerView จะรีไซเคิลองค์ประกอบแต่ละรายการเหล่านั้น เมื่อ รายการเลื่อนออกจากหน้าจอ RecyclerView จะไม่ทำลายมุมมองของรายการ แต่ RecyclerView จะนำมุมมองกลับมาใช้ซ้ำสำหรับรายการใหม่ที่เลื่อนเข้ามาในหน้าจอ RecyclerView ช่วยปรับปรุงประสิทธิภาพและการตอบสนองของแอป รวมถึงลดการใช้พลังงาน
คลาสที่สำคัญ
โดยมีหลายคลาสที่ทำงานร่วมกันเพื่อสร้างรายการแบบไดนามิก
RecyclerViewคือViewGroupที่มีมุมมอง ที่สอดคล้องกับข้อมูลของคุณ ซึ่งเป็นมุมมองในตัว คุณจึงเพิ่มRecyclerViewลงในเลย์เอาต์ได้เหมือนกับเพิ่มองค์ประกอบ UI อื่นๆองค์ประกอบแต่ละรายการในลิสต์จะกำหนดโดยออบเจ็กต์ view holder เมื่อสร้างตัวยึดมุมมอง จะไม่มีข้อมูลใดๆ ที่เชื่อมโยงอยู่ หลังจากสร้างตัวยึดมุมมองแล้ว
RecyclerViewจะเชื่อมโยงกับข้อมูล คุณ กำหนด View Holder โดยการขยายRecyclerView.ViewHolderRecyclerViewจะขอมุมมองและเชื่อมโยงมุมมองกับข้อมูล โดยการเรียกใช้เมธอดในอะแดปเตอร์ คุณกำหนดอะแดปเตอร์โดยการขยายRecyclerView.Adapterตัวจัดการเลย์เอาต์จะจัดเรียงองค์ประกอบแต่ละรายการในลิสต์ คุณสามารถ ใช้ตัวจัดการเลย์เอาต์ตัวใดตัวหนึ่งที่จัดไว้ให้โดยไลบรารี RecyclerView หรือจะ กำหนดเองก็ได้ LayoutManager ทั้งหมดอิงตามคลาส Abstract ของ
LayoutManagerไลบรารี
คุณดูวิธีประกอบชิ้นส่วนทั้งหมดเข้าด้วยกันได้ในแอปตัวอย่าง RecyclerView (Kotlin) หรือแอปตัวอย่าง RecyclerView (Java)
ขั้นตอนการติดตั้งใช้งาน RecyclerView
หากจะใช้ RecyclerView คุณต้องทำสิ่งต่อไปนี้ ซึ่งจะอธิบายรายละเอียดในส่วนต่อไปนี้
เลือกรูปลักษณ์ของรายการหรือตารางกริด โดยปกติแล้ว คุณสามารถ ใช้เลย์เอาต์แมเนเจอร์มาตรฐานของไลบรารี RecyclerView ได้
ออกแบบลักษณะและการทำงานของแต่ละองค์ประกอบในรายการ ขยายคลาส
ViewHolderตามการออกแบบนี้ViewHolderเวอร์ชันของคุณมีฟังก์ชันทั้งหมดสำหรับรายการในลิสต์ ตัวยึดมุมมองเป็น Wrapper รอบViewและมุมมองนั้นได้รับการจัดการโดยRecyclerViewกําหนด
Adapterที่เชื่อมโยงข้อมูลกับมุมมองViewHolder
นอกจากนี้ยังมีตัวเลือกการปรับแต่งขั้นสูงที่ช่วยให้คุณปรับแต่ง RecyclerView ให้ตรงกับความต้องการของคุณได้
วางแผนเลย์เอาต์
รายการใน RecyclerView จะจัดเรียงตามคลาส
LayoutManager
ไลบรารี RecyclerView มีตัวจัดการเลย์เอาต์ 3 แบบ ซึ่งจัดการ
สถานการณ์เลย์เอาต์ที่พบบ่อยที่สุด
LinearLayoutManagerจัดเรียงรายการในลิสต์แบบ 1 มิติGridLayoutManagerจัดเรียงรายการในตารางแบบ 2 มิติ- หากจัดเรียงตารางแบบแนวตั้ง
GridLayoutManagerจะพยายามทําให้องค์ประกอบทั้งหมดในแต่ละแถวมีความกว้างและความสูงเท่ากัน แต่แถวต่างๆ อาจมีความสูงแตกต่างกันได้ - หากจัดเรียงตารางในแนวนอน
GridLayoutManagerจะพยายามทำให้องค์ประกอบทั้งหมดในแต่ละคอลัมน์มีความกว้างและความสูงเท่ากัน แต่คอลัมน์ต่างๆ อาจมีความกว้างแตกต่างกันได้
- หากจัดเรียงตารางแบบแนวตั้ง
StaggeredGridLayoutManagerคล้ายกับGridLayoutManagerแต่ไม่จำเป็นต้องให้รายการในแถวมีความสูงเท่ากัน (สำหรับตารางกริดแนวตั้ง) หรือรายการในคอลัมน์เดียวกันมีความกว้างเท่ากัน (สำหรับตารางกริดแนวนอน) ผลลัพธ์คือรายการ ในแถวหรือคอลัมน์อาจมีระยะห่างจากกัน
นอกจากนี้ คุณยังต้องออกแบบเลย์เอาต์ของแต่ละรายการด้วย คุณต้องใช้เลย์เอาต์นี้ เมื่อออกแบบตัวยึดมุมมองตามที่อธิบายไว้ในส่วนถัดไป
ติดตั้งใช้งานอะแดปเตอร์และตัวยึดมุมมอง
เมื่อกำหนดเลย์เอาต์แล้ว คุณจะต้องใช้ Adapter และ
ViewHolder คลาสทั้ง 2 นี้จะทำงานร่วมกันเพื่อกำหนดวิธีแสดงข้อมูล ViewHolder เป็น Wrapper รอบ View ที่มีเลย์เอาต์สำหรับแต่ละรายการในลิสต์ Adapter สร้างออบเจ็กต์ ViewHolder
ตามต้องการ และยังตั้งค่าข้อมูลสําหรับมุมมองเหล่านั้นด้วย กระบวนการ
เชื่อมโยงมุมมองกับข้อมูลเรียกว่าการเชื่อมโยง
เมื่อกำหนดอะแดปเตอร์ คุณจะลบล้างเมธอดหลัก 3 รายการ ดังนี้
onCreateViewHolder():RecyclerViewจะเรียกใช้เมธอดนี้ทุกครั้งที่ ต้องสร้างViewHolderใหม่ เมธอดนี้จะสร้างและเริ่มต้นViewHolderและViewที่เกี่ยวข้อง แต่ไม่ได้กรอกเนื้อหาของมุมมอง เนื่องจากยังไม่ได้เชื่อมโยงViewHolderกับข้อมูลที่เฉพาะเจาะจงonBindViewHolder():RecyclerViewเรียกใช้เมธอดนี้เพื่อเชื่อมโยงViewHolderกับข้อมูล เมธอดจะดึงข้อมูลที่เหมาะสมและใช้ข้อมูลนั้นเพื่อกรอกเลย์เอาต์ของ View Holder ตัวอย่างเช่น หาก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() - หากรายการในลิสต์ทับซ้อนกับแถบระบบในตอนแรก ให้ใช้ Insets กับ
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; } );
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 ได้ที่แหล่งข้อมูลต่อไปนี้