有兴趣和疑问的同学可以加入学习小组QQ群: 193765960做进一步的讨论。
分享知识,传递价值,收获快乐,与诸君共勉。
1. 背景介绍
在日常的开发中,我们经常会遇到异常复杂的界面,尤其是订单详情页,商品详情页这样的界面。
如何对复杂的界面进行解耦是个很好的话题。本文作者就基于自己项目中订单列表和订单详情的实例讲解一种行之有效的方案:利用树状菜单将复杂界面拆解以达到业务逻辑解耦的目的。
2. 订单列表实例
订单列表页如下图:关键点是订单中展示的商品种类不定,不同的订单商品数不是固定的。
大家可以思考一下这种界面如何实现,大家的第一反应可能是嵌套列表的实现方案,当时作者本人第一反应也是这样。但是作者个人一向对嵌套列表类的东西本能的比较抵触,所以开始思考其他的方案。
2.1 设计原理
作者曾经开发过电商类的类目界面,一级类目--(展开)--> 二级类目--(展开)-->三级类目--...。当时使用到了树状菜单(级联菜单)这样的一个东西,当时想到可以将商品部分作为类似的二级菜单来加载展示。至于每个订单item最底部的订单状态和订单金额的部分,经过考虑作为了和商品同一层级的视图进行展示(没有必要作为商品item的一部分)。
这样基本的视图就被切割为了三种:
- 1,订单头视图:作为根视图(一级菜单)
- 2,商品信息视图:作为根视图的子视图
- 3,订单尾视图:作为和商品视图同一级别的视图添加到根视图。
可能有点同学感觉这样没必要,太麻烦。其实这样做有个非常重要的好处是:界面解耦后可以灵活组合扩展。
- 任何一部分的视图的修改,都不会在UI和业务逻辑上影响其他视图。
- 添加任何的视图,只要在相应的节点添加子节点,非常灵活。
- 各视图的业务和操作逻辑在各自内部完成,尽量实现了业务的解耦。
2.2 代码结构
代码组织结构如下图:
2.3 代码实现(伪代码)
2.3.1OrderListActivity
- 布局:RecyclerView(订单类表部分的布局为简单的RecyclerView控件)
- 关键伪代码:
public class OrderListActivity extends Activity{
private RecyclerView recyclerview;
private RecyclerView.LayoutManager layoutmanager;
private TreeRecyclerAdapter treeRecyclerAdapter;
private List<TreeItem> treeItemList;
......
public void showOrderList(OrderListDTO data) {
// OrderListTreeItem为我们自定义的一级菜单
treeItemList.addAll(ItemHelperFactory.createTreeItemList(data.getResultList(), OrderListTreeItem.class, null));
//设置adapter的显示样式
treeRecyclerAdapter.setType(TreeRecyclerViewType.SHOW_ALL);
treeRecyclerAdapter.setDatas(treeItemList);
recyclerview.setAdapter(treeRecyclerAdapter);
}
}
是不是感觉到很诧异:没错就是这么简单,activity中只负责将数据获取到后设置给adapter。只需绑定一级菜单:treeItemList.addAll(ItemHelperFactory.createTreeItemList(data.getResultList(), OrderListTreeItem.class, null));
注意:
TreeItem和TreeRecyclerAdapter 都是树状菜单的内部控件,跟业务是完全解耦的。
2.3.2 OrderListTreeItem
public class OrderListTreeItem extends TreeItemGroup<OrderSearchResultDto> {
private OrderListTreeItemFooter footer;
private Context mContext;
@Override
public List<TreeItem> initChildsList(OrderSearchResultDto data) {
//创建子菜单:商品菜单
List<TreeItem> childs = ItemHelperFactory.createTreeItemList(data.getProdList(), OrderProductTreeItem.class, this);
//创建子菜单:订单状态和金额尾视图
footer = new OrderListTreeItemFooter();
footer.setData(data);
footer.setParentItem(this);
//加载子视图
if(null==childs){
childs = new ArrayList<>();
}
childs.add(footer);
return childs;
}
@Override
public int initLayoutId() {
//设置本级菜单视图的资源id
return R.layout.item_order_order_list;
}
@Override
public void onBindViewHolder(ViewHolder holder) {
//对本级视图的视图元素设置数据和各种业务逻辑,用法类似recyclerview的adapter中onBindViewHolder
}
@Override
public boolean isCanExpand() {
//有商品子菜单则展开,没有商品子菜单则不用展开
return !(data.getProductList()==null ||data.getProductList().size()==0);
}
2.3.3 OrderProductTreeItem
public class OrderProductTreeItem extends TreeItem<OrderProduct> {
private Context mContext;
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
//todo
}
};
@Override
public int initLayoutId() {
return R.layout.order_list_item_product;
}
@Override
public void onBindViewHolder(ViewHolder holder) {
//todo
}
}
2.3.4 OrderListTreeItemFooter
public class OrderListTreeItemFooterextends TreeItem<OrderSearchResultDto> {
private Context mContext;
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
//todo
}
};
@Override
public int initLayoutId() {
return R.layout.order_list_item_footer;
}
@Override
public void onBindViewHolder(ViewHolder holder) {
//todo
}
}
到此,我们就完成了订单列表页的所有视图和业务逻辑。是不是很简单呢?每个类都尽量做到了业务单一,相互解耦。
注:
本文中所使用的树状菜单控件在网上有类似的方案,读者可以自己百度查找,也可以加QQ群找作者索要。