1. 引言
??上兩章簡(jiǎn)單講了下ListView的一些基本用法。ListView由于其強(qiáng)大的功能,在Android 開發(fā)的過程中貢獻(xiàn)卓越。但是由于它自身的部分缺點(diǎn),不使用一些技巧來(lái)提高它的運(yùn)行效率的話,它的性能體驗(yàn)會(huì)非常的差。還有就是ListView的擴(kuò)展性不夠完美,我們?cè)趯?shí)現(xiàn)數(shù)據(jù)縱向排列的同時(shí),還需要數(shù)據(jù)的橫向排列。
?RecyclerView可以說是ListView的增強(qiáng)版,不僅可以輕松實(shí)現(xiàn)和ListView同樣的效果,還會(huì)優(yōu)化ListView存在的各種問題。目前官方更推薦RecyclerView,相信未來(lái)也會(huì)有更多的程序逐漸從ListView轉(zhuǎn)向Recyclerview。
2. RecyclerView的基本用法
??RecyclerView也屬于新增的控件,所以為了能夠使用RecyclerView,首先得在項(xiàng)目的build.gradle的dependencies中添加相應(yīng)的依賴庫(kù)。
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile 'com.android.support:recyclerview-v7:25.3.1'
}
添加完依賴庫(kù)之后,Sync Now來(lái)進(jìn)行同步,修改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="com.demo.recyclerviewdemo.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
在你添加完了RecyclerView的依賴包之后,添加依賴控件的,記得顯示完整的包名。為了和之前的ListView的項(xiàng)目有一個(gè)對(duì)比,這里用同樣的圖和布局模式。
1.item_list.xml ,展示子項(xiàng)目的布局
<?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="100dp"
android:gravity="center_vertical"
android:padding="10dp">
<ImageView
android:id="@+id/iv_head"
android:layout_width="80dp"
android:layout_height="match_parent"
android:src="@mipmap/ic_launcher"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/tv_student_name"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:text="學(xué)生姓名"
android:layout_gravity="center"
android:gravity="center"/>
</LinearLayout>
2.Student.java 學(xué)生的實(shí)體類
public class Student {
private String name;
private int imageId;
public Student(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
}
- 自定義一個(gè)適配器,讓這個(gè)適配器繼承RecyclerView.Adapter ,并將泛型指定為StudentAdapter.ViewHolder,其中ViewHolder是我們自己定義的一個(gè)內(nèi)部類。
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
private List<Student> mStudentList;
public StudentAdapter(List<Student> mStudentList) {
this.mStudentList = mStudentList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false);
ViewHolder holder=new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Student student=mStudentList.get(position);
holder.studentImage.setImageResource(student.getImageId());
holder.studentname.setText(student.getName());
}
@Override
public int getItemCount() {
return mStudentList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder{
private ImageView studentImage;
private TextView studentname;
public ViewHolder(View itemView) {
super(itemView);
studentImage= (ImageView) itemView.findViewById(R.id.iv_head);
studentname= (TextView) itemView.findViewById(R.id.tv_student_name);
}
}
}
??可能剛開始看的時(shí)候有點(diǎn)暈,哪里好理解了?我發(fā)四沒有騙你,你等我慢慢給你分析下。首先我們定義了一個(gè)內(nèi)部類 ViewHolder 繼承RecyclerView.ViewHolder,然后ViewHolder下面會(huì)有一個(gè)構(gòu)造函數(shù)ViewHolder(itemView)需要讓你實(shí)現(xiàn),你發(fā)現(xiàn)沒有,你在構(gòu)造函數(shù)傳的這個(gè)View參數(shù),其實(shí)就是RecyclerView中的子項(xiàng)的最外層布局。所以我們就可以通過findviewByid的方法來(lái)獲取布局中實(shí)例。
??然后我們接下來(lái)繼續(xù)分析,StudentAdapter 中也有一個(gè)構(gòu)造函數(shù),這個(gè)方法是用于將要展示的數(shù)據(jù)源傳進(jìn)來(lái),并賦值給全局變量,后續(xù)的操作都是圍著這個(gè)操作進(jìn)行的。
??繼續(xù)往下看,由于StudentAdapter 是繼承RecyclerView.Adapter的,所以必須重寫 onCreateViewHolder(), onBindViewHolder(),getItemCount()三個(gè)方法。 onCreateViewHolder()這個(gè)方法是創(chuàng)建ViewHolder 實(shí)例,然后將item_list.xml這個(gè)子布局也裝載進(jìn)去,并把加載出來(lái)的布局傳入到構(gòu)造函數(shù)當(dāng)中,最后返回ViewHolder的實(shí)例。onBindViewHolder()這個(gè)方法是對(duì)RecyclerView的子項(xiàng)的數(shù)據(jù)進(jìn)行賦值,會(huì)在子項(xiàng)被滾動(dòng)到屏幕中的時(shí)候被執(zhí)行。我們通過get(position)的方法得到當(dāng)前項(xiàng)的Student的實(shí)例。getItemCount(),告訴RecyclerView,這個(gè)集合有多少個(gè)子項(xiàng)。直接返回?cái)?shù)據(jù)的長(zhǎng)度。
4.開始使用RecyclerView了。
public class MainActivity extends AppCompatActivity {
private List<Student> studentList=new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initStudent();//初始化學(xué)生數(shù)據(jù)
RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
StudentAdapter adapter=new StudentAdapter(studentList);
recyclerView.setAdapter(adapter);
}
private void initStudent() {
for (int i = 0; i < 2; i++) {
Student student1=new Student("學(xué)生一",R.mipmap.a1);
studentList.add(student1);
Student student2=new Student("學(xué)生二",R.mipmap.a2);
studentList.add(student2);
Student student3=new Student("學(xué)生三",R.mipmap.a3);
studentList.add(student3);
Student student4=new Student("學(xué)生四",R.mipmap.a4);
studentList.add(student4);
Student student5=new Student("學(xué)生五",R.mipmap.a5);
studentList.add(student5);
Student student6=new Student("學(xué)生六",R.mipmap.a6);
studentList.add(student6);
Student student7=new Student("學(xué)生七",R.mipmap.a7);
studentList.add(student7);
Student student8=new Student("學(xué)生八",R.mipmap.a8);
studentList.add(student8);
Student student9=new Student("學(xué)生九",R.mipmap.a9);
studentList.add(student9);
Student student10=new Student("學(xué)生十",R.mipmap.a10);
studentList.add(student10);
Student student11=new Student("學(xué)生十一",R.mipmap.a11);
studentList.add(student11);
Student student12=new Student("學(xué)生十二",R.mipmap.a12);
studentList.add(student12);
Student student13=new Student("學(xué)生十三",R.mipmap.a13);
studentList.add(student13);
}
}
}
?可以看到我們使用同樣的辦法initStudent()初始化所有的學(xué)生對(duì)象,接著在OnCreate()方法中線獲取到RecyclerView的實(shí)例。然后創(chuàng)建一個(gè)LinearLayoutManager對(duì)象,將它設(shè)置到RecyclerView當(dāng)中,這里使用LinearLayoutManager是線性布局的意思??梢詫?shí)現(xiàn)和ListView類似的效果。然后是創(chuàng)建Studentadapter的實(shí)例,將studentList實(shí)例數(shù)據(jù)傳到StudentAdapter的構(gòu)造函數(shù)當(dāng)中。最后調(diào)用setAdapter()方法完成適配器的設(shè)置。這樣RecyclerView和數(shù)據(jù)之間的關(guān)聯(lián)就建立完成了。效果如下圖所示:

3.實(shí)現(xiàn)橫向滾動(dòng)
?之前說了ListView不能設(shè)置橫向排列,RecyclerView可以很好的彌補(bǔ)這個(gè)不足,使用方法很簡(jiǎn)單,在MainActivity加入一行代碼就可以了。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initStudent();//初始化學(xué)生數(shù)據(jù)
RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(linearLayoutManager);
StudentAdapter adapter=new StudentAdapter(studentList);
recyclerView.setAdapter(adapter);
}
發(fā)現(xiàn)沒有,就多了一行代碼 linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);設(shè)置布局的橫向排列。默認(rèn)是縱向排列的。顯示效果如圖所示:

為啥子ListView不能橫向滑動(dòng),而RecyclerView可以很輕松的實(shí)現(xiàn)呢?這主要的得益于RecyclerView出色的設(shè)計(jì)。ListView的布局排列是由自身去管理的,而RecyclerView將這個(gè)工作給了LayoutManager,LayoutManager中制定了一套可擴(kuò)展的布局排列接口,子類按照這個(gè)借口的規(guī)范來(lái)實(shí)現(xiàn),這樣就可以制定出各種不同的排列的方式的布局。
4.瀑布流布局
RecyclerView除了提供了LinearLayoutManager線性布局排列以外,還提供了GridLayoutManager(網(wǎng)格布局)和StaggeredGridLayoutManager(瀑布流布局)。
這里稍稍修改了下item_list的布局:
<?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"
android:orientation="vertical"
android:padding="5dp"
>
<ImageView
android:id="@+id/iv_head"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:scaleType="centerCrop"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="@+id/tv_student_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="學(xué)生姓名"
android:layout_marginTop="10dp"/>
</LinearLayout>
代碼也更改了一下,仔細(xì)看:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initStudent();//初始化學(xué)生數(shù)據(jù)
RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
StaggeredGridLayoutManager staggeredGridLayoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
StudentAdapter adapter=new StudentAdapter(studentList);
recyclerView.setAdapter(adapter);
}
對(duì)比下前面的發(fā)現(xiàn)就改變了一句是不是:
StaggeredGridLayoutManager staggeredGridLayoutManager=new StaggeredGridLayoutManager (3, StaggeredGridLayoutManager.VERTICAL);
StaggeredGridLayoutManager 的參數(shù):
- 第一個(gè)參數(shù),是設(shè)置多少列,我這里設(shè)為3列
- 第二個(gè)參數(shù) ,是指定布局的排列方向
實(shí)現(xiàn)的效果如下圖所示:

5.網(wǎng)格布局
在經(jīng)過了這個(gè)多的布局之后,下面的的網(wǎng)格布局應(yīng)該也猜到了,也是修改其中的LayoutManager。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initStudent();//初始化學(xué)生數(shù)據(jù)
RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
GridLayoutManager gridLayoutManager=new GridLayoutManager(this,4);
gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
StudentAdapter adapter=new StudentAdapter(studentList);
recyclerView.setAdapter(adapter);
}
GridLayoutManager的兩個(gè)參數(shù):
1、第一個(gè)參數(shù),是設(shè)置上下文對(duì)象
2、第二個(gè)參數(shù)是設(shè)置排列的行數(shù)
還有最后設(shè)置好排列的方向

6.RecyclerView的點(diǎn)擊事件
??正如ListView那樣,RecyclerView也必須滿足點(diǎn)擊這個(gè)硬性要求的。但是與ListView不同的是,RecyclerView并沒有提供類似setOnItemClickLisenter()這樣的注冊(cè)監(jiān)聽器的方法,而是需要我們自己給子項(xiàng)具體的view去注冊(cè)點(diǎn)擊事件。相比較實(shí)現(xiàn)起來(lái),可能會(huì)稍微復(fù)雜一點(diǎn)。
??大兄弟,看到這里你是不是又有點(diǎn)暈了???都說了是比ListView更優(yōu)秀,那搞個(gè)點(diǎn)擊事件怎么實(shí)現(xiàn)起來(lái)還復(fù)雜了呢?...... 大哥,我真的沒騙你,拳頭放下來(lái)。因?yàn)長(zhǎng)istView的點(diǎn)擊處理,是點(diǎn)擊一整個(gè)子項(xiàng)目,然后出發(fā)點(diǎn)擊事件的是不,哪如果我只想子項(xiàng)目里面的某一個(gè)控件出發(fā)自己的點(diǎn)擊事件呢?ListView并沒有給出人性化的處理,處理起來(lái)比較麻煩。所以RecyclerView就放棄了子項(xiàng)的點(diǎn)擊監(jiān)聽事件的監(jiān)聽器。所有的點(diǎn)擊事件都由具體的View來(lái)處理,這樣是不是沒有這個(gè)煩惱了啊。
??這里講一下它的點(diǎn)擊事件,需要修改StudentAdapter的代碼。我們先是修改了ViewHolder,在ViewHolder 添加studentView 變量來(lái)保存子項(xiàng)最外層的布局的實(shí)例。然后在onCreateViewHolder()方法中注冊(cè)點(diǎn)擊事件就可以了。這里分別為最外層的布局和ImageViewd都注冊(cè)了點(diǎn)擊事件。(意思是,它不僅給每個(gè)item都設(shè)置了點(diǎn)擊事件,連item里面的imageview也設(shè)置了點(diǎn)擊事件)。通過holder 獲取子項(xiàng)的view,然后設(shè)置點(diǎn)擊事件。通過holder獲取子項(xiàng)的view的position。這樣就拿到了子項(xiàng)的Student的數(shù)據(jù)。設(shè)置一個(gè)點(diǎn)擊事件,來(lái)顯示具體的數(shù)據(jù)值。在點(diǎn)擊文字的時(shí)候,我們沒有給文字設(shè)置點(diǎn)擊事件,但是給studentView設(shè)置了點(diǎn)擊事件,所以還是被最外層的布局捕獲到,彈出Toast。
ublic class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
private List<Student> mStudentList;
public StudentAdapter(List<Student> mStudentList) {
this.mStudentList = mStudentList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false);
final ViewHolder holder=new ViewHolder(view);
holder.studentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Student student = mStudentList.get(position);
Toast.makeText(v.getContext(), "你點(diǎn)擊了"+student.getName(), Toast.LENGTH_SHORT).show();
}
});
holder.studentImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Student student = mStudentList.get(position);
Toast.makeText(v.getContext(), "你點(diǎn)擊了"+student.getName(), Toast.LENGTH_SHORT).show();
}
});
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Student student=mStudentList.get(position);
holder.studentImage.setImageResource(student.getImageId());
holder.studentname.setText(student.getName());
}
@Override
public int getItemCount() {
return mStudentList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder{
private ImageView studentImage;
private TextView studentname;
View studentView;
public ViewHolder(View itemView) {
super(itemView);
studentView=itemView;
studentImage= (ImageView) itemView.findViewById(R.id.iv_head);
studentname= (TextView) itemView.findViewById(R.id.tv_student_name);
}
}
}
效果如圖:

項(xiàng)目代碼github地址:https://github.com/wangxin3119/recyclerview