Android 控制元件之 RecyclerView(一)—— 載入檢視和佈局選擇
本文目錄
一、概述
RecyclerView 是在 Android5.0 之後,Google 推出的一個新控制元件,是作為 ListView、GridView 的替代者出現的。它具有比上述兩者更高的靈活性,在功能上也更加強大。Google 官方對它的定義是:
A flexible view for providing a limited window into a large data set.
向一個有限的視窗(window)提供大量資料集的靈活檢視(view)。
先來看看 RecyclerView 能實現哪些效果:
由於 RecylerView 定義在了 support 庫中,如果我們想要使用 RecyclerView 控制元件,需要向專案中的 build.gradle新增程式碼 implementation ‘com.android.support:recyclerview-v7:27.1.1’ ,如下所示:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:recyclerview-v7:27.1.1'
}
新增完之後一定要記得點一下右上角的 Sync Now 進行同步,否則可能會出現錯誤。
二、列表檢視的處理
1. item 的佈局檔案
首先,在 activity_main 中新增 RecyclerView 控制元件,程式碼如下:
<?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">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
其次,在 layout 資料夾下新建一個 Layout Resource file,命名為 item_recycler_view,它是 RecyclerView 中 item(即子項)的佈局檔案,我們需要的 item 佈局為圖片(ImageView)和文字(TextView)的組合,所以程式碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_item"
android:layout_width="80dp"
android:layout_height="80dp" />
<TextView
android:id="@+id/tv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:textSize="15sp" />
</LinearLayout>
2. 構造 Adapter 類
既然展示的是關於食物的圖片和文字的檢視,那麼我們就新建一個 Food 類來存取我們每個食物(Food)的例項,程式碼如下:
public class Food {
// 食物的圖片id
private int imageId;
// 食物的名字
private String name;
// 建構函式
public Food(String name, int imageId){
this.imageId = imageId;
this.name = name;
}
// 獲取食物的圖片id
public int getImageId() {
return imageId;
}
// 獲取食物的名字
public String getName() {
return name;
}
}
接著,為我們的 RecyclerView 定義一個 Adapter 類。和 ListView 一樣,RecyclerView 也需要使用 Adapter 介面卡,現在我們新建一個 MyAdapter 類,繼承自 RecyclerView.Adapter,其中泛型引數 VH 表示的就是 ViewHolder ,這裡我們將泛型型別指定為 MyAdapter.ViewHolder,它是 MyAdapter 中我們自己定義的一個 的內部類,繼承自 Recycler.ViewHolder。
我們還需要在 MyAdapter 類中覆寫三個方法 :onCreateViewHolder()、onBindViewHolder() 和 getItemCount(),新建好的程式碼如下:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
// 儲存 Food 型別資料的資料來源
private List<Food> mFoodList;
// 構造方法用於傳入資料
public MyAdapter(List<Food> list){
mFoodList = list;
}
// 建立 ViewHolder的例項,在這裡載入 item_recycler_view
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
return null;
}
// 繫結 ViewHolder,用於對每個 item 進行賦值操作
@Override
public void onBindViewHolder(ViewHolder holder, int position){
}
// 獲取 item 的數量
@Override
public int getItemCount(){
return 0;
}
// 自定義的 ViewHolder 類,繼承自 RecyclerView.ViewHolder
static class ViewHolder extends RecyclerView.ViewHolder{
View view;
ViewHolder(View view){
super(view);
this.view = view;
}
}
}
分析一下這段程式碼,mFoodList 是用於儲存 Food 型別資料的資料來源,它通過構造方法獲取資料。接下來看第一個需要覆寫的方法 onCreateViewHolder(),從字面上理解,顧名思義,就是需要我們在這個方法中建立 ViewHolder 例項。在這個方法中,我們將之前寫好的佈局檔案 item_recycler_view 載入進來,並建立一個 ViewHolder 例項返回。程式碼如下:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
// 傳入子項(item)的佈局檔案
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_recycler_view, parent, false);
// 建立 ViewHolder 例項
ViewHolder holder = new ViewHolder(view);
return holder;
}
接下來是第二個覆寫方法 onBindViewHolder(),該方法用於對 RecyclerView 子項的資料進行賦值,我們可以通過第二個引數 position 來獲取當前項的 Food 例項,然後把 Food 例項中的資料傳入 holder 的 ImageView 和 TextView 中。程式碼如下:
@Override
public void onBindViewHolder(ViewHolder holder, int position){
// 獲取當前項的 Food 例項
Food food = mFoodList.get(position);
TextView textView = holder.view.findViewById(R.id.tv_item);
ImageView imageView = holder.view.findViewById(R.id.iv_item);
textView.setText(food.getName());
imageView.setImageResource(food.getImageId());
}
最後一個覆寫方法 getItemCount() 就很簡單了,因為是獲取 item 的個數,所以直接返回 mFoodList 的長度即可。
@Override
public int getItemCount(){
return mFoodList.size();
}
完整的 MyAdapter 類程式碼如下所示:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
// 儲存 Food 型別資料的資料來源
private List<Food> mFoodList;
// 構造方法用於傳入資料
public MyAdapter(List<Food> list){
mFoodList = list;
}
// 建立 ViewHolder的例項,在這裡載入 item_recycler_view
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
// 傳入子項(item)的佈局檔案
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_recycler_view, parent, false);
// 建立 ViewHolder 例項
ViewHolder holder = new ViewHolder(view);
return holder;
}
// 繫結 ViewHolder,用於對每個 item 進行賦值操作
@Override
public void onBindViewHolder(ViewHolder holder, int position){
// 獲取當前項的 Food 例項
Food food = mFoodList.get(position);
TextView textView = holder.view.findViewById(R.id.tv_item);
ImageView imageView = holder.view.findViewById(R.id.iv_item);
textView.setText(food.getName());
imageView.setImageResource(food.getImageId());
}
// 獲取 item 的數量
@Override
public int getItemCount(){
return mFoodList.size();
}
// 自定義的 ViewHolder 類,繼承自 RecyclerView.ViewHolder
static class ViewHolder extends RecyclerView.ViewHolder{
View view;
ViewHolder(View view){
super(view);
this.view = view;
}
}
}
3. 佈局管理器
在寫好 MyAdapter 類之後,就是在 MainActivity 中使用 RecyclerView 了。我們到目前為止還剩下一個問題沒解決,那就是既然提到了 RecyclerView 是作為 ListView 和 GridView 的替代者出現的,那麼它是如何管理它的佈局的呢?
答案就是呼叫 RecyclerView 例項的 setLayoutManager() 方法。setLayoutManager()方法接受一個 LayoutManager 型別的引數,通過這個引數來實現 RecyclerView 的各種各樣的佈局,前面所展示的佈局都是在這個方法中進行設定的。下面就來介紹幾個佈局管理器型別:
1)LinearLayoutManager
如果我們要實現像 ListView 那樣的垂直線性佈局,就得使用 LinearLayoutManager 。程式碼如下所示:
// 獲取 RecyclerView 的例項
mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);
// 線性佈局管理器
LinearLayoutManager manager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(manager);
先構造 LinearLayoutManager 的例項 manager,它的預設 orientation 為 VERTICAL,與我們的需求正好一致,所以構造好之後直接將 manager 傳入 setLayoutManager() 即可。最終效果如前面垂直線性佈局所示。
而當我們需要實現橫向線性佈局時,只需要在第二句和第三句之間新增setOrientation()方法即可:
// 線性佈局管理器
LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(manager);
效果如前面水平線性佈局所示。
2)GridLayoutManager
如果我們要實現像 GridView 那樣的網格佈局,就得使用 GridLayoutManager 。程式碼如下所示:
// 網格佈局管理器
GridLayoutManager manager = new GridLayoutManager(MainActivity.this, 3);
mRecyclerView.setLayoutManager(manager);
GridLayoutManager 的建構函式的第二個引數型別是 int,表示列數,這裡我們指定為 3,即顯示三列的資料。效果如前面網格佈局所示。
3)StaggeredGridLayoutManager
如果我們想實現瀑布流佈局的效果的話,就得使用 StaggeredGridLayoutManager,程式碼如下所示:
// 瀑布流佈局管理器
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(manager);
StaggeredGridLayoutManager 的構造方法第一個引數型別為int,表示列數,這裡我們同樣指定為3。第二個引數表示佈局的方向,這裡我們設定為垂直方向。實現效果如前面瀑布流佈局所示。
4. 完整的 MainActivity 程式碼
這裡我們以垂直線性佈局為例,直接上程式碼:
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private List<Food> foodList;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化食物的資料
initData();
mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);
// 垂直線性佈局
LinearLayoutManager manager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(manager);
// 將 foodList 傳入 adapter 中
adapter = new MyAdapter(MainActivity.this, foodList);
mRecyclerView.setAdapter(adapter);
}
// 這裡為了方便展示隨便初始化了 20 個食物的資料
private void initData(){
foodList = new ArrayList<>();
for(int i = 0; i < 5; i++) {
Food food1 = new Food("Cake", R.drawable.cake);
Food food2 = new Food("Biscuit", R.drawable.biscuit);
Food food3 = new Food("Bread", R.drawable.bread);
Food food4 = new Food("Hamburger", R.drawable.hamburger);
foodList.add(food1);
foodList.add(food2);
foodList.add(food3);
foodList.add(food4);
}
}
}
到這裡相信很多人都會看的比較明白了,在 MainActivity 中我們首先初始化了我們要展示的食物的資料,然後將佈局設定為線性(預設為垂直佈局)。然後我們會初始化adpater,它是我們自定義的 MyAdapter 類的例項,我們將初始好後的 foodList 作為引數傳給它。最後,它會被作為引數傳給 setAdapter() 方法。
至此,我們的程式碼就全部完成了!執行一下,效果如下圖所示:
如果我們需要設定簡單的分割線的話,可以新增一句程式碼:
mRecyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,
DividerItemDecoration.VERTICAL));
效果如下圖所示:
為了節省篇幅,其他的佈局程式碼這裡就不貼出來了,方法也很簡單,無非是在setLayoutManager() 方法中傳入不同的佈局器引數,然後對 item_recycler_view 或資料進行簡單的修改即可。
三、總結
對於 RecyclerView 的使用,可分為 4 步:
Step 1:向 build.gradle 中新增相應的依賴
Step 2:為 item 新增一個佈局檔案
Step 3:新建一個繼承自 RecyclerView.Adapter 的 Adapter 類,並覆寫 onCreateViewHolder()、onBindViewHolder() 和 getItemCount() 這三個方法。
Step 4:在 MainActivity 中,獲取 RecyclerView 的例項,然後通過 setLayoutManager() 進行佈局設定,通過 setAdapter() 設定介面卡,然後就大功告成了!
最後,本篇部落格是我寫的第一篇部落格,如果有寫的不清楚或者不清晰的地方,望見諒!如果對於我的部落格有任何想法建議的話,歡迎評論或私信!