Android中RecyclerView用法,一步一步教你如何使用RecyclerView以及帶你走過編碼中可能會出現的坑~
- 2020 年 8 月 24 日
- 筆記
- Android, RecycleView
首先,要明白RecyclerView是做什麼的?其次是為什麼要用RecyclerView?這裡牽扯到RecyclerView和ListView的區別,這裡不廢話,大家自行百度即可!
以下示例我用的Android API 29 ,啟用了AndroidX。
第一步,添加依賴
創建一個新的工程,在app/build.gradle
中的dependencies閉包
添加以下內容:
implementation 'com.android.support:appcompat-v7:29.0.0'
注意這裡的『29.0.0』要和你的 compileSdkVersion 版本號一致。版本號是28,就改為28。
別的不需要改,以下是此時的項目目錄以及gradle文件內容
第二步,創建RecyclerView布局
在你的布局文件中添加RecyclerView,切記要為RecyclerView添加id。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="//schemas.android.com/apk/res/android" xmlns:app="//schemas.android.com/apk/res-auto" xmlns:tools="//schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_card" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
第三步,為RecyclerView中的item創建一個統一的布局文件
在layout下創建一個名為layout_rv_card的布局文件,文件名命名要和你的實際業務相匹配方便後期查找。
在此布局文件中,可以添加任何你想展示的視圖,在這裡我們先創建兩個TextView為大家展示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="//schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:orientation="horizontal"> <TextView android:id="@+id/tv_id" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" /> <TextView android:id="@+id/tv_state" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" /> </LinearLayout>
別忘了添加id。
第四步,創建Adapter
Adapter是連接後端數據和前端顯示的適配器介面,是數據和UI(View)之間一個重要的紐帶。
如果之前沒有接觸過,先跟著做,做完之後就會理解了。
我們在項目目錄下新建一個Java類,取名為CardAdapter。
然後在CardAdapter中我們創建一個內部類MyViewHolder繼承RecyclerView.ViewHolder。
為什麼要創建內部類?
答:就是為了方便而已,創建在外部也是可以,但是如果項目大了之後,類太多不好區分,而且基本上因為布局的不同ViewHolder也基本不同,不會復用,所以創建在內部即可。
為什麼要使用ViewHolder?
答:ViewHolder的主要任務:容納View視圖。前邊我們提到Adapter連接了後端數據和前端顯示,viewHolder的作用就是提供前端的視圖文件。
接下來,我們在ViewHolder綁定視圖文件,同在activity中類似,itemView顧名思義即列表中每一項的視圖,每一項都要綁定。
import android.view.View; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; /** * @Author: Messi Mei * @Date: 2020/8/24 13:46 * @Email: [email protected] **/ public class CardAdapter { class MyViewHolder extends RecyclerView.ViewHolder { private TextView tvId,tvState; public MyViewHolder(@NonNull View itemView) { super(itemView); tvId = itemView.findViewById(R.id.tv_id); tvState = itemView.findViewById(R.id.tv_state); } } }
第五步,完成Adapter
在我們創建的CardAdapter類繼承我們創建的內部類MyViewHolder,並重寫提供的三個方法:
onCreateViewHolder(),onBindViewHolder(),getItemCount()
import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; /** * @Author: Messi Mei * @Date: 2020/8/24 13:46 * @Email: [email protected] **/ public class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder> { @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return null; } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { } @Override public int getItemCount() { return 0; } class MyViewHolder extends RecyclerView.ViewHolder { private TextView tvId,tvState; public MyViewHolder(@NonNull View itemView) { super(itemView); tvId = itemView.findViewById(R.id.tv_id); tvState = itemView.findViewById(R.id.tv_state); } } }
5.1重寫onCreateViewHolder方法,返回我們的內部類MyViewHolder ,此處為將我們的item布局文件和adapter綁定。
@NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.layout_rv_card,parent,false); return new MyViewHolder(view); }
也可以寫為
@NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_rv_card,parent,false); return new MyViewHolder(view); }
一樣的。
5.2為每一項視圖,添加數據。
此處holder為你每一項的MyViewHolder對象,position 定位,可以理解為list下標。
@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { holder.tvId.setText(position); holder.tvState.setText(position+position); }
5.3返回列表大小
@Override public int getItemCount() { return 0; }
第六步,創建數據介面
創建一個List列表(當然也可以是JsonArray),以此為例,我們創一個Card實體類,為List添加該類泛型。
6.1接下來在CardAdapter創建一個帶該list的構造器
List<Card> list; public CardAdapter(List<Card> list) { this.list = list; }
6.2在getItemCount方法中返回該列表大小
@Override public int getItemCount() { return list == null ? 0 : list.size(); }
6.3在onBindViewHolder方法中綁定真實數據
@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { holder.tvId.setText(list.get(position).getId()); holder.tvState.setText(list.get(position).getState()); }
以下為整體程式碼(文末有我在使用RecycleView中對Adapter的改進程式碼,如有需要可以參考):
import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.List; /** @Author: Messi Mei @Date: 2020/8/24 13:46 @Email: [email protected] */ public class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder> { private List<Card> list; public CardAdapter(List<Card> list) { this.list = list; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_rv_card, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { holder.tvId.setText(list.get(position).getId()); holder.tvState.setText(list.get(position).getState()); } @Override public int getItemCount() { return list == null ? 0 : list.size(); } static class MyViewHolder extends RecyclerView.ViewHolder { private TextView tvId, tvState; MyViewHolder(@NonNull View itemView) { super(itemView); tvId = itemView.findViewById(R.id.tv_id); tvState = itemView.findViewById(R.id.tv_state); } } }
第七步,使用RecycleView
在Activity中創建RecycleView對象並綁定視圖,創建Adapter對象。
在initData中初始化數據。
package com.mz.myapplication; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private CardAdapter cardAdapter; private List<Card> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); } private void initData() { list = new ArrayList<>(); for (int i = 0; i < 50; i++) { Card card = new Card(); card.setId(i+1+""); if (i%2 == 1){ card.setState("在線"); }else{ card.setState("離線"); } list.add(card); } } private void initView() { recyclerView = findViewById(R.id.rv_card); cardAdapter = new CardAdapter(list); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(cardAdapter); } }
至此,運行即可。
如果你想要更複雜的列表布局,在布局文件中加入相應的視圖即可,修改完後記得在ViewHolder中加入、綁定相應的視圖,在Adapter中的onBindViewHolder為其添加、綁定數據。
而我更推薦以下這種方式,用set方法去設置Adapter中的數據,更加靈活。
package com.mz.myapplication; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; /** @Author: Messi Mei @Date: 2020/8/24 13:46 @Email: [email protected] */ public class Card2Adapter extends RecyclerView.Adapter<Card2Adapter.MyViewHolder> { private List<Card> list = new ArrayList<>(); public void setList(List<Card> list) { this.list = list;
//一定要記得加 notifyDataSetChanged(); } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_rv_card, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { holder.tvId.setText(list.get(position).getId()); holder.tvState.setText(list.get(position).getState()); } @Override public int getItemCount() { return list == null ? 0 : list.size(); } static class MyViewHolder extends RecyclerView.ViewHolder { private TextView tvId, tvState; MyViewHolder(@NonNull View itemView) { super(itemView); tvId = itemView.findViewById(R.id.tv_id); tvState = itemView.findViewById(R.id.tv_state); } } }
package com.mz.myapplication; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.os.Bundle; import java.util.ArrayList; import java.util.List; public class Main2Activity extends AppCompatActivity { private RecyclerView recyclerView; private Card2Adapter card2Adapter; private List<Card> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); initView(); initData(); } private void initData() { list = new ArrayList<>(); for (int i = 0; i < 50; i++) { Card card = new Card(); card.setId(i+1+""); if (i%2 == 1){ card.setState("在線"); }else{ card.setState("離線"); } list.add(card); } card2Adapter.setList(list); } private void initView() { recyclerView = findViewById(R.id.rv_card); card2Adapter = new Card2Adapter(); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(card2Adapter); } }
這樣你可以在任意位置修改列表中的數據。