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()方法来设置数据;
public class MultiListAdapter extends RecyclerView.Adapter<ItemHolder> {
private List<Item> mDataList;
public MultiListAdapter(List<Item> dataList) {
mDataList = dataList;
}
@NonNull
@Override
public ItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
return ItemHolderFactory.getItemHolder(viewGroup, type);
}
@Override
public void onBindViewHolder(@NonNull ItemHolder viewHolder, int i) {
//设置 Holder 数据
viewHolder.setData(mDataList.get(i));
}
@Override
public int getItemViewType(int position) {
return mDataList.get(position).getType();
}
@Override
public int getItemCount() {
return mDataList.size();
}
public void setSpanCount(GridLayoutManager layoutManager) {
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int i) {
int type = getItemViewType(i);
switch (type) {
default:
case ItemHolderFactory.ITEM_LARGE:
return 3;
case ItemHolderFactory.ITEM_SMALL:
return 2;
case ItemHolderFactory.ITEM_TITLE_BAR:
return 6;
}
}
});
}
}
4.ItemHolder抽象类:
setData方法用来设置Item布局的数据
public abstract class ItemHolder extends RecyclerView.ViewHolder {
public ItemHolder(View item) {
super(item);
}
public abstract void setData(Item itemData);
}
5.LargeItemHolder类:
另外两个类似
public class LargeItemHolder extends ItemHolder {
private ImageView mItemImage;
private TextView mTitle;
private TextView mSubTitle;
public LargeItemHolder(View item) {
super(item);
mItemImage = item.findViewById(R.id.item_image);
mTitle = item.findViewById(R.id.item_title);
mSubTitle = item.findViewById(R.id.item_sub_title);
}
@Override
public void setData(Item itemData) {
ItemLarge item = (ItemLarge) itemData;
mItemImage.setImageBitmap(item.getImage());
mTitle.setText(item.getTitle());
mSubTitle.setText(item.getSubTitle());
}
}
6.Item接口:
public interface Item {
int getType();
}
7.ItemLarge类(一个图片、一个标题和一个副标题):
另外两个类似
public class ItemLarge implements Item {
private Bitmap mImage;
private String mTitle;
private String mSubTitle;
public ItemLarge(Bitmap bitmap, String title, String subTitle) {
mImage = bitmap;
mTitle = title;
mSubTitle = subTitle;
}
public Bitmap getImage() {
return mImage;
}
public String getTitle() {
return mTitle;
}
public String getSubTitle() {
return mSubTitle;
}
@Override
public int getType() {
return ItemHolderFactory.ITEM_LARGE;
}
}
8.工厂类ItemHolderFactory:
三个常量表示不同的布局类型,通过getItemHolder来创建ViewHolder。
public class ItemHolderFactory {
public static final int ITEM_LARGE = 0;
public static final int ITEM_SMALL = 1;
public static final int ITEM_TITLE_BAR = 2;
@IntDef({
ITEM_LARGE,
ITEM_SMALL,
ITEM_TITLE_BAR
})
@interface ItemType {}
static ItemHolder getItemHolder(ViewGroup parent, @ItemType int type) {
switch (type) {
default:
case ITEM_LARGE:
return new LargeItemHolder(LayoutInflater
.from(parent.getContext()).inflate(R.layout.item_large, parent, false));
case ITEM_SMALL:
return new SmallItemHolder(LayoutInflater
.from(parent.getContext()).inflate(R.layout.item_small, parent, false));
case ITEM_TITLE_BAR:
return new TitleBarItemHolder(LayoutInflater
.from(parent.getContext()).inflate(R.layout.item_title_bar, parent, false));
}
}
}
9.ListActivity类:
public class ListActivity extends AppCompatActivity {
List<Item> itemList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
initData();
GridLayoutManager layoutManager = new GridLayoutManager(this, 6);
MultiListAdapter adapter = new MultiListAdapter(itemList);
adapter.setSpanCount(layoutManager);
RecyclerView recyclerView = findViewById(R.id.recycle_view);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
private void initData() {
//添加数据
itemList.add(new ItemTitleBar("Hot New", null));
itemList.add(new ItemLarge(
BitmapFactory.decodeResource(getResources(), R.drawable.img_1),
"One More Light",
"Linkin Park"));
itemList.add(new ItemLarge(
BitmapFactory.decodeResource(getResources(), R.drawable.img_2),
"Let Go ",
"Avril Lavigne"));
itemList.add(new ItemTitleBar("Recommended", null));
itemList.add(new ItemSmall(
BitmapFactory.decodeResource(getResources(), R.drawable.img_3),
"Bridge to Terabithia"));
itemList.add(new ItemSmall(
BitmapFactory.decodeResource(getResources(), R.drawable.img_4),
"Life Is Beautiful"));
itemList.add(new ItemSmall(
BitmapFactory.decodeResource(getResources(), R.drawable.img_5),
"A Violent Flame"));
itemList.add(new ItemTitleBar("Top Rated", null));
itemList.add(new ItemLarge(
BitmapFactory.decodeResource(getResources(), R.drawable.img_6),
"Furious 7: Original Motion Picture Soundtrack",
"Various Artists"));
itemList.add(new ItemLarge(
BitmapFactory.decodeResource(getResources(), R.drawable.img_7),
"Halo 5: Guardians (Original Soundtrack)",
"Kazuma Jinnouchi"));
}
}
10.布局文件(item_large.xml):
layout_width用match_parent是为了Item在网格中居中,此处match_parent相当于宽度为Item所占的列数。
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:background="#ffffff"
android:elevation="2dp"> <ImageView
android:contentDescription="@id/item_title"
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="170dp"
android:scaleType="centerCrop"
tools:src="@drawable/img_7" /> <TextView
android:id="@+id/item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:lines="1"
android:ellipsize="end"
android:textColor="#000000"
android:textSize="18sp"
tools:text="Item Title" /> <TextView
android:id="@+id/item_sub_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:lines="1"
android:ellipsize="end"
android:textSize="14sp"
tools:text="Sub Title" /> </LinearLayout>
Android RecycleView多种布局实现(工厂模式)的更多相关文章
- Android设计模式(五岁以下儿童)--简单工厂模式
1.面试的时候问这个问题: 在ListView 的item小程序.很多不同的显示风格.或者是,为了更好地维护,不同的样式,应该怎么做? 我一下就想到的是工厂的模式,利用project,编写ViewFa ...
- 抽象工厂模式(Java与Kotlin版)
前文推送 设计模式 简单工厂模式(Java与Kotlin版) 工厂方法模式(Java与Kotlin版) Kotlin基础知识 Kotlin入门第一课:从对比Java开始 Kotlin入门第二课:集合操 ...
- .Net简单工厂模式,工厂模式,抽象工厂模式实例
1.定义 简单工厂模式:是由一个工厂对象决定创建出哪一种产品类的实例.简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现. 工厂模式:定义一个用于创建对象的接口, ...
- Android ListView Adapter的getItemViewType和getViewTypeCount多种布局
<Android ListView Adapter的getItemViewType和getViewTypeCount多种布局> 在Android的ListView中.假设在一个Lis ...
- 《Android源码设计模式》--抽象工厂模式
No1: 4种MediaPlayer Factory分别会生成不同的MediaPlayer基类:StagefrightPlayer.NuPlayerDriver.MidiFile和TestPlayer ...
- Android开发之ListView添加多种布局效果演示
在这个案例中展示的新闻列表,使用到ListView控件,然后在适配器中添加多种布局效果,这里通过重写BaseAdapter类中的 getViewType()和getItemViewType()来做判断 ...
- Android KK后为何工厂模式下无法adb 无法重新启动机器 ?
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- Android设计模式(十二)--抽象工厂模式
问题: 抽象工厂模式,是一个,狠恶心的模式,那么这个模式在Android有没实用到过呢? 1.定义: 抽象工厂模式:为创建一组相关或者是相互依赖的对象提供一个接口,而不须要指定他们的详细类. 2.使用 ...
- android多种布局的列表实现
最近有一个列表效果,需要一个列表有多种布局,最终效果如下: 这个我也问了同事以及开发群里的朋友,居然都没得到最优的实现方式的回答,看来这种复杂列表的需求还是比较少的,我自己也走了一些弯路,把我几个实现 ...
随机推荐
- Redis 再牛逼,也得设置密码!!
Redis 你再牛逼也得设置密码啊,不然会有安全漏洞,造成一些隐患. 还有,比如像出现下面这样的错,需要设置密码,或者关闭保护模式,所以还是设置密码比较安全.不然只能本地操作,不能远程连接. DENI ...
- IdentityServer4源码颁发token分析及性能优化
IdentityServer4源码地址 IdentityModel源码地址 以下的流程用ResourceOwnerPassword类型获取token作为介绍 分两种获取形式说明 token请求地址为默 ...
- 【ABP框架系列学习】介绍篇(1)
0.引言 该系列博文主要在[官方文档]及[tkbSimplest]ABP框架理论研究系列博文的基础上进行总结的,或许大家会质问,别人都已经翻译过了,这不是多此一举吗?原因如下: 1.[tkbSim ...
- SHOW INDEX 你用过吗???
mysql中 show 包含了很多指令,例如show table status, show innodb 等等等, 今天来讲讲mysql中SHOW INDEX FROM tableName 本例中用 ...
- MySQL(3)---MySQL优化
MySQL优化 一.单表.双表.三表优化 1.单表 首先结论就是,range类型查询字段后面的索引全都无效 (1)建表 create table if not exists article( i ...
- [java]类初始化挺有意思的题目
public class Base { private String baseName = "base"; public Base() { callName(); } public ...
- win32程序之窗口程序,以及消息机制
win32程序值窗口程序,以及消息机制 一丶简介 通过上一讲.我们了解了窗口其实是绘制出来的.而且是不断绘制的过程. 所以窗口的本质是绘制. 但是我们现在看到的窗口程序.都可以点击关闭按钮. 使用鼠标 ...
- k8s之external-etcd集群管理
一.概述 kubernetes使用etcd作为数据中心,使用kubeadm部署kubernetes的时候默认会自己部署一个etcd,当然也可以将kubeadm部署的单点的etcd做成集群,但是比较麻烦 ...
- 序言:我为什么学Perl
曾经,我熟练操作grep.awk.sed,甚至自认对sed尚算精通,我一度爱上了写脚本.但是随着写脚本的次数多了,需求复杂了,我深深的感受到shell的无奈. 例如,我多次遇到过类似下面这种恶心的需求 ...
- ARM 汇编指令 ADR 与 LDR 使用
简介 这两个都是伪指令:ADR 是小范围的地址读取伪指令,LDR 是大范围的读取地址伪指令.可实际使用的区别是: ADR 是将基于 PC 相对偏移的地址值或基于寄存器相对地址值读取的伪指令,而 LDR ...