上一篇博客RecyclerView的基本使用讲述了Recycler最基本的使用方法,这篇博客主要围绕如何在一个RecyclerView呈现不同的Item。
要实现在RecyclerView呈现复合Item,主要要在RecyclerView.adapter中的以下三个方法做处理:
getItemViewType()
onCreateViewHolder()
onBindViewHolder()
具体实现
首先,要呈现出不同Item,那么说明Item会有多个XML文件和对应的不同数据源。简便起见,假设我们的项目中有两种不同的Item,一种是单纯的Image,另外一种则是由多个TextView组成。
数据源定义如下:
private ArrayList<Object> getSampleArrayList() {
ArrayList<Object> items = new ArrayList<>();
items.add(new User("Dany Targaryen", "Valyria"));
items.add(new User("Rob Stark", "Winterfell"));
items.add("image");
items.add(new User("Jon Snow", "Castle Black"));
items.add("image");
items.add(new User("Tyrion Lanister", "King's Landing"));
return items;
}
可以看到,有User和单纯的String两种数据。这两种数据对应的布局文件也不同,那么必然导致需要使用不同的ViewHolder:
ViewHolder1.java
public class ViewHolder1 extends RecyclerView.ViewHolder {
private TextView label1, label2;
public ViewHolder1(View v) {
super(v);
label1 = (TextView) v.findViewById(R.id.text1);
label2 = (TextView) v.findViewById(R.id.text2);
}
public TextView getLabel1() {
return label1;
}
public void setLabel1(TextView label1) {
this.label1 = label1;
}
public TextView getLabel2() {
return label2;
}
public void setLabel2(TextView label2) {
this.label2 = label2;
}
}
然后是它的布局文件:
layout_viewholder1.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/llContainer"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"/>
<TextView
android:id="@+id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical" />
</LinearLayout>
ViewHolder2.java
public class ViewHolder2 extends RecyclerView.ViewHolder {
private ImageView ivExample;
public ViewHolder2(View v) {
super(v);
ivExample = (ImageView) v.findViewById(R.id.ivExample);
}
public ImageView getImageView() {
return ivExample;
}
public void setImageView(ImageView ivExample) {
this.ivExample = ivExample;
}
}
layout_viewholder2.xml
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ivExample"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
接下来是我们在例子中使用的图片资源:
创建ComplexRecyclerViewAdapter
public class ComplexRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// The items to display in your RecyclerView
private List<Object> items;
private final int USER = 0, IMAGE = 1;
// Provide a suitable constructor (depends on the kind of dataset)
public ComplexRecyclerViewAdapter(List<Object> items) {
this.items = items;
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return this.items.size();
}
@Override
public int getItemViewType(int position) {
//More to come
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
//More to come
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
//More to come
}
}
这里给出了必须要覆盖的函数,并没有给出实现细节,下面一个一个讨论:
首先是getItemViewType
,这里我们需要根据数据源的类型,判定Item的View类型:
@Override
public int getItemViewType(int position) {
if (items.get(position) instanceof User) {
return USER;
} else if (items.get(position) instanceof String) {
return IMAGE;
}
return -1;
}
接下来,我们需要根据getItemViewType
获得的View类型来实例化所需的ViewHolder:
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
RecyclerView.ViewHolder viewHolder;
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
switch (viewType) {
case USER:
View v1 = inflater.inflate(R.layout.layout_viewholder1, viewGroup, false);
viewHolder = new ViewHolder1(v1);
break;
case IMAGE:
View v2 = inflater.inflate(R.layout.layout_viewholder2, viewGroup, false);
viewHolder = new ViewHolder2(v2);
break;
default:
View v = inflater.inflate(android.R.layout.simple_list_item_1, viewGroup, false);
viewHolder = new RecyclerViewSimpleTextViewHolder(v);
break;
}
return viewHolder;
}
最后重写onBindViewHolder,将数据源中的内容呈现到ViewHolder的控件中去。这里需要根据viewHolder的不同来加载不同的数据。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
switch (viewHolder.getItemViewType()) {
case USER:
ViewHolder1 vh1 = (ViewHolder1) viewHolder;
configureViewHolder1(vh1, position);
break;
case IMAGE:
ViewHolder2 vh2 = (ViewHolder2) viewHolder;
configureViewHolder2(vh2);
break;
default:
RecyclerViewSimpleTextViewHolder vh = (RecyclerViewSimpleTextViewHolder) viewHolder;
configureDefaultViewHolder(vh, position);
break;
}
}
private void configureDefaultViewHolder(RecyclerViewSimpleTextViewHolder vh, int position) {
vh.getLabel().setText((CharSequence) items.get(position));
}
private void configureViewHolder1(ViewHolder1 vh1, int position) {
User user = (User) items.get(position);
if (user != null) {
vh1.getLabel1().setText("Name: " + user.name);
vh1.getLabel2().setText("Hometown: " + user.hometown);
}
}
private void configureViewHolder2(ViewHolder2 vh2) {
vh2.getImageView().setImageResource(R.drawable.sample_golden_gate);
}
到这里,就已经大功告成了,只需要在Activity或者Fragment中,对RecyclerView进行一些准备工作即可。
效果如下: