RecycleView是个很常用的控件,很多APP中都可以看到它的身影,同时它也是个很难用的控件,主要就难在多种布局的实现。
在《第一行代码—Android》这本书里边有个RecycleView实现的聊天界面布局,左右两种布局写在了同一个文件中,如果是发送来的消息,就隐藏右侧布局,反之隐藏左侧布局,这种方式对于比较简单的、只有两种Item的界面是可行的,假如我们的Item有多种布局,那么这种方式就显得很笨重。对于多种布局,我们可以使用工厂模式来实现。
1.首先看看效果(GIF一直上传失败,只好传JPG了):

这里的LayoutManager使用GridLayoutManager,设置为6列,然后在Adapter类中根据不同的类型来设置所占列数,具体见Adapter类的setSpanCount方法。
2.然后是类图:

3.Adapter类:
适配器的代码很短,设置数据和绑定View的代码都写在了ItemHolder的子类里面;
List<Item>储存三种类型的Item数据,如果需要增加新的类型,只要实现Item接口就可以了;
在onBindViewHolder方法中调用ItemHolder的setData()方法来设置数据;
- 1 public class MultiListAdapter extends RecyclerView.Adapter<ItemHolder> {
- 2
- 3 private List<Item> mDataList;
- 4
- 5 public MultiListAdapter(List<Item> dataList) {
- 6 mDataList = dataList;
- 7 }
- 8
- 9 @NonNull
- 10 @Override
- 11 public ItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
- 12 return ItemHolderFactory.getItemHolder(viewGroup, type);
- 13 }
- 14
- 15 @Override
- 16 public void onBindViewHolder(@NonNull ItemHolder viewHolder, int i) {
- 17 //设置 Holder 数据
- 18 viewHolder.setData(mDataList.get(i));
- 19 }
- 20
- 21 @Override
- 22 public int getItemViewType(int position) {
- 23 return mDataList.get(position).getType();
- 24 }
- 25
- 26 @Override
- 27 public int getItemCount() {
- 28 return mDataList.size();
- 29 }
- 30
- 31 public void setSpanCount(GridLayoutManager layoutManager) {
- 32 layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
- 33 @Override
- 34 public int getSpanSize(int i) {
- 35 int type = getItemViewType(i);
- 36 switch (type) {
- 37 default:
- 38 case ItemHolderFactory.ITEM_LARGE:
- 39 return 3;
- 40 case ItemHolderFactory.ITEM_SMALL:
- 41 return 2;
- 42 case ItemHolderFactory.ITEM_TITLE_BAR:
- 43 return 6;
- 44 }
- 45 }
- 46 });
- 47 }
- 48 }
4.ItemHolder抽象类:
setData方法用来设置Item布局的数据
- 1 public abstract class ItemHolder extends RecyclerView.ViewHolder {
- 2 public ItemHolder(View item) {
- 3 super(item);
- 4 }
- 5
- 6 public abstract void setData(Item itemData);
- 7 }
5.LargeItemHolder类:
另外两个类似
- 1 public class LargeItemHolder extends ItemHolder {
- 2
- 3 private ImageView mItemImage;
- 4 private TextView mTitle;
- 5 private TextView mSubTitle;
- 6
- 7 public LargeItemHolder(View item) {
- 8 super(item);
- 9 mItemImage = item.findViewById(R.id.item_image);
- 10 mTitle = item.findViewById(R.id.item_title);
- 11 mSubTitle = item.findViewById(R.id.item_sub_title);
- 12 }
- 13
- 14 @Override
- 15 public void setData(Item itemData) {
- 16 ItemLarge item = (ItemLarge) itemData;
- 17 mItemImage.setImageBitmap(item.getImage());
- 18 mTitle.setText(item.getTitle());
- 19 mSubTitle.setText(item.getSubTitle());
- 20 }
- 21 }
6.Item接口:
- 1 public interface Item {
- 2 int getType();
- 3 }
7.ItemLarge类(一个图片、一个标题和一个副标题):
另外两个类似
- 1 public class ItemLarge implements Item {
- 2
- 3 private Bitmap mImage;
- 4 private String mTitle;
- 5 private String mSubTitle;
- 6
- 7 public ItemLarge(Bitmap bitmap, String title, String subTitle) {
- 8 mImage = bitmap;
- 9 mTitle = title;
- 10 mSubTitle = subTitle;
- 11 }
- 12
- 13 public Bitmap getImage() {
- 14 return mImage;
- 15 }
- 16
- 17 public String getTitle() {
- 18 return mTitle;
- 19 }
- 20
- 21 public String getSubTitle() {
- 22 return mSubTitle;
- 23 }
- 24
- 25 @Override
- 26 public int getType() {
- 27 return ItemHolderFactory.ITEM_LARGE;
- 28 }
- 29 }
8.工厂类ItemHolderFactory:
三个常量表示不同的布局类型,通过getItemHolder来创建ViewHolder。
- 1 public class ItemHolderFactory {
- 2
- 3 public static final int ITEM_LARGE = 0;
- 4 public static final int ITEM_SMALL = 1;
- 5 public static final int ITEM_TITLE_BAR = 2;
- 6
- 7 @IntDef({
- 8 ITEM_LARGE,
- 9 ITEM_SMALL,
- 10 ITEM_TITLE_BAR
- 11 })
- 12 @interface ItemType {}
- 13
- 14 static ItemHolder getItemHolder(ViewGroup parent, @ItemType int type) {
- 15 switch (type) {
- 16 default:
- 17 case ITEM_LARGE:
- 18 return new LargeItemHolder(LayoutInflater
- 19 .from(parent.getContext()).inflate(R.layout.item_large, parent, false));
- 20 case ITEM_SMALL:
- 21 return new SmallItemHolder(LayoutInflater
- 22 .from(parent.getContext()).inflate(R.layout.item_small, parent, false));
- 23 case ITEM_TITLE_BAR:
- 24 return new TitleBarItemHolder(LayoutInflater
- 25 .from(parent.getContext()).inflate(R.layout.item_title_bar, parent, false));
- 26 }
- 27 }
- 28 }
9.ListActivity类:
- 1 public class ListActivity extends AppCompatActivity {
- 2
- 3 List<Item> itemList = new ArrayList<>();
- 4
- 5 @Override
- 6 protected void onCreate(Bundle savedInstanceState) {
- 7 super.onCreate(savedInstanceState);
- 8 setContentView(R.layout.activity_list);
- 9
- 10 initData();
- 11
- 12 GridLayoutManager layoutManager = new GridLayoutManager(this, 6);
- 13 MultiListAdapter adapter = new MultiListAdapter(itemList);
- 14 adapter.setSpanCount(layoutManager);
- 15
- 16 RecyclerView recyclerView = findViewById(R.id.recycle_view);
- 17 recyclerView.setLayoutManager(layoutManager);
- 18 recyclerView.setAdapter(adapter);
- 19 }
- 20
- 21 private void initData() {
- 22 //添加数据
- 23 itemList.add(new ItemTitleBar("Hot New", null));
- 24 itemList.add(new ItemLarge(
- 25 BitmapFactory.decodeResource(getResources(), R.drawable.img_1),
- 26 "One More Light",
- 27 "Linkin Park"));
- 28 itemList.add(new ItemLarge(
- 29 BitmapFactory.decodeResource(getResources(), R.drawable.img_2),
- 30 "Let Go ",
- 31 "Avril Lavigne"));
- 32 itemList.add(new ItemTitleBar("Recommended", null));
- 33 itemList.add(new ItemSmall(
- 34 BitmapFactory.decodeResource(getResources(), R.drawable.img_3),
- 35 "Bridge to Terabithia"));
- 36 itemList.add(new ItemSmall(
- 37 BitmapFactory.decodeResource(getResources(), R.drawable.img_4),
- 38 "Life Is Beautiful"));
- 39 itemList.add(new ItemSmall(
- 40 BitmapFactory.decodeResource(getResources(), R.drawable.img_5),
- 41 "A Violent Flame"));
- 42 itemList.add(new ItemTitleBar("Top Rated", null));
- 43 itemList.add(new ItemLarge(
- 44 BitmapFactory.decodeResource(getResources(), R.drawable.img_6),
- 45 "Furious 7: Original Motion Picture Soundtrack",
- 46 "Various Artists"));
- 47 itemList.add(new ItemLarge(
- 48 BitmapFactory.decodeResource(getResources(), R.drawable.img_7),
- 49 "Halo 5: Guardians (Original Soundtrack)",
- 50 "Kazuma Jinnouchi"));
- 51 }
- 52 }
10.布局文件(item_large.xml):
layout_width用match_parent是为了Item在网格中居中,此处match_parent相当于宽度为Item所占的列数。
- 1 <?xml version="1.0" encoding="utf-8"?>
- 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- 3 xmlns:tools="http://schemas.android.com/tools"
- 4 android:orientation="vertical"
- 5 android:layout_width="match_parent"
- 6 android:layout_height="wrap_content"
- 7 android:layout_margin="4dp"
- 8 android:background="#ffffff"
- 9 android:elevation="2dp">
- 10
- 11 <ImageView
- 12 android:contentDescription="@id/item_title"
- 13 android:id="@+id/item_image"
- 14 android:layout_width="match_parent"
- 15 android:layout_height="170dp"
- 16 android:scaleType="centerCrop"
- 17 tools:src="@drawable/img_7" />
- 18
- 19 <TextView
- 20 android:id="@+id/item_title"
- 21 android:layout_width="match_parent"
- 22 android:layout_height="wrap_content"
- 23 android:layout_marginTop="8dp"
- 24 android:paddingStart="8dp"
- 25 android:paddingEnd="8dp"
- 26 android:lines="1"
- 27 android:ellipsize="end"
- 28 android:textColor="#000000"
- 29 android:textSize="18sp"
- 30 tools:text="Item Title" />
- 31
- 32 <TextView
- 33 android:id="@+id/item_sub_title"
- 34 android:layout_width="match_parent"
- 35 android:layout_height="wrap_content"
- 36 android:layout_marginBottom="8dp"
- 37 android:paddingStart="8dp"
- 38 android:paddingEnd="8dp"
- 39 android:lines="1"
- 40 android:ellipsize="end"
- 41 android:textSize="14sp"
- 42 tools:text="Sub Title" />
- 43
- 44 </LinearLayout>