使用 RecyclerView 建立動態清單   Android Jetpack 的一部分。

試用 Compose
Jetpack Compose 是 Android 推薦的 UI 工具包。瞭解如何在 Compose 中處理版面配置。

藉由 RecyclerView,您可以輕鬆有效地顯示大型資料集。由您提供資料並定義每個項目的外觀,而 RecyclerView 程式庫則會在需要時動態建立元素。

顧名思義,RecyclerView 就是回收個別元素。當向下捲動螢幕而未顯示項目時,RecyclerView 並不會刪除其檢視畫面。只不過,RecyclerView 會重複使用捲動螢幕後顯示新項目的檢視畫面。RecyclerView 可改善效能和應用程式的回應速度,並降低耗電量。

主要類別

有數種不同的類別可建立動態清單。

  • RecyclerViewViewGroup,包含對應至資料的檢視表。其本身是檢視畫面,因此您可以在版面配置中新增 RecyclerView,方法和加入其他 UI 元素相同。

  • 清單中的每個個別元素都是由「檢視容器」物件定義。建立檢視容器時,沒有任何相關聯的資料。建立檢視容器後,RecyclerView 就會將該資料繫結至其資料。您可以藉由擴充 RecyclerView.ViewHolder 來定義檢視容器。

  • RecyclerView 會呼叫轉接程式中的方法,要求檢視畫面,並將檢視畫面繫結至其資料。擴充 RecyclerView.Adapter 即可定義轉接程式。

  • 版面配置管理員會排列清單中的個別元素。您可以使用 RecyclerView 程式庫提供的其中一個版面配置管理員,也可以定義自己的版面配置。版面配置管理員都是以程式庫的 LayoutManager 抽象類別為基礎。

您可以在 RecyclerView 範例應用程式 (Kotlin)RecyclerView 範例應用程式 (Java) 中查看所有元件如何配合在一起。

實作 RecyclerView 的步驟

如要使用 RecyclerView,您必須執行下列幾個步驟。我們將在後續章節中詳細說明這些細節。

  1. 決定清單或格線的外觀。一般情況下,您可以使用其中一個 RecyclerView 程式庫的標準版面配置管理工具。

  2. 設計清單中的每個元素外觀和行為。根據這個設計,擴充 ViewHolder 類別。您的 ViewHolder 版本提供清單項目的所有功能。檢視容器是 View 的包裝函式,而該檢視畫面由 RecyclerView 管理。

  3. 定義將資料與 ViewHolder 檢視畫面相關聯的 Adapter

您也可以使用進階自訂選項,依據自己的需求自訂 RecyclerView。

規劃版面配置

RecyclerView 中的項目是由 LayoutManager 類別排列。RecyclerView 程式庫提供三個版面配置管理工具,可用於處理最常見的版面配置情境:

  • LinearLayoutManager 會在一個維度清單中排列項目。
  • GridLayoutManager 會將項目排列成二維格線:
    • 如果網格是垂直排列的,GridLayoutManager 會嘗試讓每個資料列中的所有元素具有相同的寬度和高度,但不同的資料列高度不同。
    • 如果格線已水平排列,GridLayoutManager 會嘗試讓每個資料欄的所有元素具有相同的寬度和高度,但不同資料欄寬度不同。
  • StaggeredGridLayoutManagerGridLayoutManager 類似,但不要求一列中的項目高度 (適用於垂直網格) 或同一欄中的項目寬度相同 (適用於水平格線)。因此,資料列或資料欄中的項目可能會相互偏移。

此外,您也必須設計個別項目的版面配置。設計檢視區塊容器時,需要使用這個版面配置,詳情請見下一節。

實作轉接程式和檢視容器

決定版面配置後,請實作 AdapterViewHolder。這兩個類別會一起運作,以定義資料的顯示方式。ViewHolderView 的包裝函式,其中包含清單中個別項目的版面配置。Adapter 會視需要建立 ViewHolder 物件,並設定這些檢視畫面的資料。將檢視畫面與資料建立關聯的程序稱為「繫結」

定義轉接程式時,您會覆寫三種主要方法:

  • onCreateViewHolder():每當需要建立新的 ViewHolder 時,RecyclerView 都會呼叫這個方法。這個方法會建立及初始化 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 自訂功能」。

其他資源

如要進一步瞭解如何在 Android 裝置上測試,請參閱下列資源。

範例應用程式