Android RecycleView多种布局实现(工厂模式)

RecycleView是个很常用的控件,很多APP中都可以看到它的身影,同时它也是个很难用的控件,主要就难在多种布局的实现。

在《第一行代码—Android》这本书里边有个RecycleView实现的聊天界面布局,左右两种布局写在了同一个文件中,如果是发送来的消息,就隐藏右侧布局,反之隐藏左侧布局,这种方式对于比较简单的、只有两种Item的界面是可行的,假如我们的Item有多种布局,那么这种方式就显得很笨重。对于多种布局,我们可以使用工厂模式来实现。

Github:https://github.com/imcloudfloating/DesignApp

 

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>

 

Android RecycleView多种布局实现(工厂模式)

全文结束