有興趣和疑問的同學(xué)可以加入學(xué)習(xí)小組QQ群: 193765960做進一步的討論。
分享知識,傳遞價值,收獲快樂,與諸君共勉。
1. 背景介紹
在日常的開發(fā)中,我們經(jīng)常會遇到異常復(fù)雜的界面,尤其是訂單詳情頁,商品詳情頁這樣的界面。
如何對復(fù)雜的界面進行解耦是個很好的話題。本文作者就基于自己項目中訂單列表和訂單詳情的實例講解一種行之有效的方案:利用樹狀菜單將復(fù)雜界面拆解以達到業(yè)務(wù)邏輯解耦的目的。
2. 訂單列表實例
訂單列表頁如下圖:關(guān)鍵點是訂單中展示的商品種類不定,不同的訂單商品數(shù)不是固定的。
大家可以思考一下這種界面如何實現(xiàn),大家的第一反應(yīng)可能是嵌套列表的實現(xiàn)方案,當(dāng)時作者本人第一反應(yīng)也是這樣。但是作者個人一向?qū)η短琢斜眍惖臇|西本能的比較抵觸,所以開始思考其他的方案。

2.1 設(shè)計原理
作者曾經(jīng)開發(fā)過電商類的類目界面,一級類目--(展開)--> 二級類目--(展開)-->三級類目--...。當(dāng)時使用到了樹狀菜單(級聯(lián)菜單)這樣的一個東西,當(dāng)時想到可以將商品部分作為類似的二級菜單來加載展示。至于每個訂單item最底部的訂單狀態(tài)和訂單金額的部分,經(jīng)過考慮作為了和商品同一層級的視圖進行展示(沒有必要作為商品item的一部分)。
這樣基本的視圖就被切割為了三種:

- 1,訂單頭視圖:作為根視圖(一級菜單)
- 2,商品信息視圖:作為根視圖的子視圖
- 3,訂單尾視圖:作為和商品視圖同一級別的視圖添加到根視圖。
可能有點同學(xué)感覺這樣沒必要,太麻煩。其實這樣做有個非常重要的好處是:界面解耦后可以靈活組合擴展。
- 任何一部分的視圖的修改,都不會在UI和業(yè)務(wù)邏輯上影響其他視圖。
- 添加任何的視圖,只要在相應(yīng)的節(jié)點添加子節(jié)點,非常靈活。
- 各視圖的業(yè)務(wù)和操作邏輯在各自內(nèi)部完成,盡量實現(xiàn)了業(yè)務(wù)的解耦。
2.2 代碼結(jié)構(gòu)
代碼組織結(jié)構(gòu)如下圖:

2.3 代碼實現(xiàn)(偽代碼)
2.3.1OrderListActivity
- 布局:RecyclerView(訂單類表部分的布局為簡單的RecyclerView控件)
- 關(guān)鍵偽代碼:
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));
//設(shè)置adapter的顯示樣式
treeRecyclerAdapter.setType(TreeRecyclerViewType.SHOW_ALL);
treeRecyclerAdapter.setDatas(treeItemList);
recyclerview.setAdapter(treeRecyclerAdapter);
}
}
是不是感覺到很詫異:沒錯就是這么簡單,activity中只負責(zé)將數(shù)據(jù)獲取到后設(shè)置給adapter。只需綁定一級菜單:treeItemList.addAll(ItemHelperFactory.createTreeItemList(data.getResultList(), OrderListTreeItem.class, null));
注意:
TreeItem和TreeRecyclerAdapter 都是樹狀菜單的內(nèi)部控件,跟業(yè)務(wù)是完全解耦的。
2.3.2 OrderListTreeItem
public class OrderListTreeItem extends TreeItemGroup<OrderSearchResultDto> {
private OrderListTreeItemFooter footer;
private Context mContext;
@Override
public List<TreeItem> initChildsList(OrderSearchResultDto data) {
//創(chuàng)建子菜單:商品菜單
List<TreeItem> childs = ItemHelperFactory.createTreeItemList(data.getProdList(), OrderProductTreeItem.class, this);
//創(chuàng)建子菜單:訂單狀態(tài)和金額尾視圖
footer = new OrderListTreeItemFooter();
footer.setData(data);
footer.setParentItem(this);
//加載子視圖
if(null==childs){
childs = new ArrayList<>();
}
childs.add(footer);
return childs;
}
@Override
public int initLayoutId() {
//設(shè)置本級菜單視圖的資源id
return R.layout.item_order_order_list;
}
@Override
public void onBindViewHolder(ViewHolder holder) {
//對本級視圖的視圖元素設(shè)置數(shù)據(jù)和各種業(yè)務(wù)邏輯,用法類似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
}
}
到此,我們就完成了訂單列表頁的所有視圖和業(yè)務(wù)邏輯。是不是很簡單呢?每個類都盡量做到了業(yè)務(wù)單一,相互解耦。
注:
本文中所使用的樹狀菜單控件在網(wǎng)上有類似的方案,讀者可以自己百度查找,也可以加QQ群找作者索要。