RecyclerView 的基本使用

bailang426發表於2020-12-19

1.新增依賴

   implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha02'

2.在layout佈局檔案中宣告

activity_main.xml的程式碼如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

3.在當前類中獲取RecyclerView控制元件,並設定介面卡

因為資料是無法直接傳遞給RecyclerView的,所以需要藉助介面卡來完成。 在設定Adapter之前要初始化資料來源,然後通過Adapter的構造方法將資料傳遞給介面卡, 當資料發生變化時我們只需要更新這個資料來源就可以了。


public class MainActivity extends AppCompatActivity {
   private ArrayList<Fruit> fruits = new ArrayList<>();

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        RecyclerView rv = findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(RecyclerView.VERTICAL);
        rv.setLayoutManager(layoutManager);

        //建立資料來源
        initData();
        FruitAdapter adapter = new FruitAdapter(fruits);
        rv.setAdapter(adapter);


    }
    
 }

RecyclerView必須要設定 layoutManager ,通過設定不同的 layoutManager 實現不同形式的列表。

常見的有:

  • LinearLayoutManager 縱向/橫向列表
  • GridLayoutManager 網格佈局
  • StaggeredGridLayoutManager 瀑布流佈局

4. Adapter 的編碼

RecyclerView已經為我們提供了一個介面卡,我們只用繼承它,並且重寫它的三個方法 onCreateViewHolderonBindViewHoldergetItemCount就可以了 。

  • onCreateViewHolder主要是建立ViewHolder例項的, 在這個方法中可以載入子條目的佈局檔案, 設定子條目的點選事件
  • onBindViewHolder 主要是為子條目的控制元件進行賦值。 會在每個子條目滾動到螢幕內的時候執行。
  • getItemCount()返回的是條目數量,直接返回資料來源的長度即可。

疑問: 那麼onCreatViewHolder什麼時候執行呢? (待理解)

我們定義的ViewHolder只需要繼承Recycler的ViewHolder即可。複用holder的邏輯RecyclerView已經幫我們封裝好了,所以不用像ListView那樣還需要我們自己實現。

ViewHolder 建構函式的引數 itemView 其實就是 RecyclerView 子條目佈局fruit_item(程式碼見下方)的根佈局, 它就是RecyclerView列表中的子條目。 然後使用子條目fruit_item的findViewById()獲取條目中的子View。

具體程式碼如下:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    private static final String TAG = "FruitAdapter";

    private final ArrayList<Fruit> mFruits;

    public FruitAdapter(ArrayList<Fruit> fruits) {
        this.mFruits = fruits;
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        ImageView ivFruit;
        TextView tvName;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            ivFruit = itemView.findViewById(R.id.iv_fruit);
            tvName = itemView.findViewById(R.id.tv_name);
        }
    }


    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit = mFruits.get(position);
        holder.ivFruit.setImageResource(fruit.getImageId());
        holder.tvName.setText(fruit.getName());
    }


    @Override
    public int getItemCount() {
        return mFruits.size();
    }
}


RecyclerView條目的佈局fruit_item.xml的佈局如下:


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        xmlns:tool="http://schemas.android.com/tools"
        android:orientation="horizontal">
    
        <ImageView
            android:id="@+id/iv_fruit"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            />
    
    
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tool:text="蘋果"
            android:layout_gravity="center_vertical"
            />
    
    </LinearLayout>

5.設定網格列表和瀑布流

我們可以通過 LayoutManager 來設定佈局的型別。

LinearLayoutManager 為列表型別的佈局, 可以通過setOrientation()來設定列表的方向。


     LinearLayoutManager layoutManager = new LinearLayoutManager(this);
      //VERTICAL 豎直列表   HORIZONTAL:水平列表
     layoutManager.setOrientation(RecyclerView.VERTICAL);

除了設定列表,還可以設定網格列表和瀑布流列表。
設定網格列表程式碼如下:

        //引數二: 網格的列數
        GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
        rv.setLayoutManager(layoutManager);

設定瀑布流

  // 引數一:如果瀑布流的方向是vertical, 該引數指定了佈局的列數. 如果瀑布流的方向是horizontal, 該引數指定了佈局的行數
  //引數二: 瀑布流的方向
  StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
  rv.setLayoutManager(layoutManager);
        

6.為RecyclerView設定點選事件

RecyclerView並沒有像ListView一樣為我們提供類似setOnItemClickListener的條目點選事件, 而是需要我們自己給子項具體的View去註冊點選事件,所以實現起來比ListView要複雜一些。

那麼為什麼RecyclerView在各個方面的設計都要優於ListView, 偏偏在點選事件上卻"沒有處理的非常好"呢? 其實不是這樣的, ListView的點選事件並不人性化, setOnItemClickListener方法註冊的是子條目的點選事件, 但是如果點選的是子條目裡具體的按鈕呢? 雖然ListView也能實現,但是相對就比較麻煩了。 所以,RecyclerView乾脆直接放棄了子條目的點選事件,所以的點選事件都由具體的View去註冊。點選事件的實現方法如下:

首先, 在ViewHolder中新增fruitView變數來儲存子項最外佈局,

   static class ViewHolder extends RecyclerView.ViewHolder {

    View fruitView;
        ImageView ivFruit;
        TextView tvName;

        ViewHolder(@NonNull View itemView) {
            super(itemView);
            fruitView = itemView;
            ivFruit = itemView.findViewById(R.id.iv_fruit);
            tvName = itemView.findViewById(R.id.tv_name);
        }
    }

然後在onCreateViewHolder()中註冊條目或者條目中子view的點選事件。


public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    
    ...
    
   @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
        final ViewHolder viewHolder = new ViewHolder(view);

        //因為fruitView為子條目的最外層佈局,所以該點選事件就是整個條目的點選事件
        viewHolder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (parent instanceof RecyclerView) {
                    int position = ((RecyclerView) parent).getChildAdapterPosition(view);
                    onRecyclerItemClickListener.onRecyclerItemClick(position);
                }
            }
        });
        
        
                //子項中子view的點選事件
                viewHolder.ivFruit.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        
                    }
                });

        return viewHolder;
    }
    
    
    
        private OnRecyclerItemClickListener onRecyclerItemClickListener;
    
        interface  OnRecyclerItemClickListener {
            void onRecyclerItemClick(int position);
        }
        @Override
        public int getItemCount() {
            return mFruits.size();
        }
    
        void setOnRecyclerItemClickListener(OnRecyclerItemClickListener onRecyclerItemClickListener) {
            this.onRecyclerItemClickListener = onRecyclerItemClickListener;
        }
        
        ...

    }

參考:郭霖《第一行程式碼》

相關文章