Skip to main content

Android RecycleView最佳实践

Android RecycleView最佳实践

原创 大王 1周前

收录于话题

#android1

#函数式编程1

#客户端架构1

RecycleView是Android开发过程中经常用的一种控件. 相信Android小伙伴在使用过程经历过如下的折磨

  1. 每个使用RecycleView的地方都需要使用定义对应的Adapter.
  2. 每个不同模块都需要定义不同的ViewHolder.
  3. 每个不同模块都需要指定不同的布局文件.
  4. 每个不同模块都需要绑定数据到视图.
  5. 每个不同模块都需要指定宽度信息.
  6. 每次添加一个模块需要重复上面2到5的过程.

在大型项目项目每次修改的时候面对如此多的地方和每个地方的多达数十个的switch或者if-else语句, 相信大家内心都很crazy. 这里提供一个方案(GridRender)来解决这样的问题.

GridRender方案目标

为了解放业务开发同学,GridRender方案的主要目标如下:

  1. 消除创建同质化的Adapter和ViewHolder
  2. 模块的处理集中在同一个类
  3. 保证业务模块核心逻辑稳定
  4. 新增模块不影响原有的模块

GridRender架构

  1. 提出RenderModel概念,一个RenderModel代表一个模块.
  2. GridRender内部处理实现Adapter和ViewHolder
  3. GridRender交互的数据为List
  4. 原来的Present层由生成ViewHolder转化为生成RenderModel
  5. RenderModel提供模块所需要的所有信息.
  6. 布局文件
  7. 布局宽度
  8. 事件响应

GridRender使用流程

  1. 调用Present的buildRenderModels将实体数据转化为RenderModel
  2. 调用GridRender的setRenderModels将数据提供给Render
  3. GridRender内部会在合适的时机调用RenderModel提供的方法完成真正的渲染.

实战

需要构建的界面如下

-----[图片] (resource/875DAF5487FB8FACFAA2E30BB90CDE54.gif)

示意图界面具有如下特征

  1. 界面由不同的模块组成.
  2. 模块由标题(Header),内容,底部组成(Footer), 其中模块Header和模块Footer是可选的
  3. 模块内容部分存在多种样式, 上图下文,左图右文, 多列

具体支持的模块如下:

模块名称一行列数模块头部模块底部左图右文模块1有有大图模块1有有通栏模块1无无一行三列模块3有有一行四列模块4有有

示例代码

  1. 创建GridRender并关联RecycleView
public class HomeActivity extends Activity{
GridRender gridRender;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int screenWidth = getResources().getDisplayMetrics().widthPixels;
double ratio = screenWidth / (double) (ModuleStyles.ALL_COLUMN);
RecyclerView recyclerView = mBinding.rvMultiView;
// 创建GridRender
this.gridRender = GridRender.grid(recyclerView, ratio, ModuleStyles.ALL_COLUMN);
}
}

  1. 从实体数据创建renderModels,并设置到render
public class HomeActivity extends Activity{
// 数据加载成功
private void dataLoaded(List\<Item\> moduleData){
List\<GridRenderModel\<?\>\> renderModels = Present.buildRenderModels(moduleData, builders);
gridRender.set(renderModels, true);
}
}

  1. 定义相应的RenderModel
// 左图右文模块
public class LeftImgRightTextRenderModel extends GridRenderModel\<ItemModel\> {
protected LeftImgRightTextRenderModel(ItemModel bean) {
super(bean);
}
// 模块对应的资源
@Override
public int layoutResId() {
return R.layout.layout_item_img_txt;
}
@Override
public ViewBinding createBinding(RecyclerViewRender.ViewHolder viewHolder) {
return LayoutItemImgTxtBinding.bind(viewHolder.itemView);
}
// 绑定数据到视图
@Override
public void bindTo(RecyclerViewRender.ViewHolder viewHolder) {
LayoutItemImgTxtBinding binding = viewHolder.getBinding(LayoutItemImgTxtBinding.class);
Context context = viewHolder.itemView.getContext();
ItemModel itemModel = getModel();
ImageUtil.loadImage(context, itemModel.cover, binding.ivCover);
binding.tvTitle.setText(itemModel.title);
binding.tvIntro.setText(itemModel.intro);
binding.rlContent.setOnClickListener(view -\> {
ToastUtil.showShortToast(context, "图文模块点击 index= " + itemModel.index);
});
}
}
// 一行三列模块
public class ThreeColumnsRenderModel extends GridRenderModel\<ItemModel\> {
protected ThreeColumnsRenderModel(ItemModel bean, int position) {
super(bean, position, 3, ModuleStyles.ALL_COLUMN, 13, 10, 20, GridRenderModel.multipleEdgeSizeHelper);
}
@Override
public int layoutResId() {
return R.layout.layout_item_three_columns;
}
@Override
public ViewBinding createBinding(RecyclerViewRender.ViewHolder viewHolder) {
return LayoutItemThreeColumnsBinding.bind(viewHolder.itemView);
}
@Override
public void bindTo(RecyclerViewRender.ViewHolder viewHolder) {
LayoutItemThreeColumnsBinding binding = viewHolder.getBinding(LayoutItemThreeColumnsBinding.class);
Context context = viewHolder.itemView.getContext();
ItemModel itemModel = getModel();
ImageUtil.loadImage(context, itemModel.cover, binding.ivCover);
binding.tvTitle.setText(itemModel.title);
binding.tvIntro.setText(itemModel.intro);
binding.rlThreeItem.setOnClickListener(view -\> {
ToastUtil.showShortToast(context, "三图模块点击 index= " + itemModel.index);
});
binding.rlThreeItem.setPadding(
(int) (multipleEdgeSizeHelper.designPaddingStart(position, total, width, spacing, edge, 20) \* ratio),
binding.rlThreeItem.getPaddingTop(),
(int) (multipleEdgeSizeHelper.designPaddingEnd(position, total, width, spacing, edge) \* ratio),
binding.rlThreeItem.getPaddingBottom());
}
}

总结

可以看到使用了GridRender方案之后,我们业务同学可以把更多的精力聚焦在单个模块. 同时增加和减少模块时需要修改的地方仅为Present.buildRenderModels和xxRenderModel两个地方. GridRender方案消除了原始方案的样板代码, 并提高了模块的内聚性, 更便于代码维护, 提高开发效率.