需求分析
將課本的章節(jié)以多級(jí)列表的形式顯示。
解決方案思路
1.嵌套多級(jí) RecyclerView,兩級(jí)列表還好說,每多一級(jí)都是一場(chǎng)噩夢(mèng)。
2.ExpandableListView?但是ExpandableListView只支持兩級(jí)沦童,不滿足需求仑濒。
3.一個(gè)RecyclerView或者 ListView 來實(shí)現(xiàn),每一級(jí)節(jié)點(diǎn)的要素有:當(dāng)前節(jié)點(diǎn)id偷遗、父級(jí)節(jié)點(diǎn)id即pid墩瞳,顯示的內(nèi)容。
實(shí)現(xiàn)方案
//id pid name FileNode為實(shí)際用的實(shí)體Bean對(duì)象
mlist.add(new Node("223","0","我也是添加的root節(jié)點(diǎn)",new FileNode()));
ListView需要繼承TreeListViewAdapter
public class SimpleTreeAdapter extends TreeListViewAdapter
{
public SimpleTreeAdapter(ListView mTree, Context context, List<Node> datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
}
public SimpleTreeAdapter(ListView mTree, Context context, List<Node> datas,
int defaultExpandLevel) {
super(mTree, context, datas, defaultExpandLevel);
}
@Override
public View getConvertView(final Node node , int position, View convertView, ViewGroup parent)
{
final ViewHolder viewHolder ;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.cb = (CheckBox) convertView
.findViewById(R.id.cb_select_tree);
viewHolder.label = (TextView) convertView
.findViewById(R.id.id_treenode_label);
viewHolder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setChecked(node,viewHolder.cb.isChecked());
}
});
if (node.isChecked()){
viewHolder.cb.setChecked(true);
}else {
viewHolder.cb.setChecked(false);
}
if (node.getIcon() == -1) {
viewHolder.icon.setVisibility(View.INVISIBLE);
} else {
viewHolder.icon.setVisibility(View.VISIBLE);
viewHolder.icon.setImageResource(node.getIcon());
}
viewHolder.label.setText(node.getName());
return convertView;
}
private final class ViewHolder
{
ImageView icon;
CheckBox cb;
TextView label;
}
}
RecyclerView需繼承TreeRecyclerAdapter
public class SimpleTreeRecyclerAdapter extends TreeRecyclerAdapter {
public SimpleTreeRecyclerAdapter(RecyclerView mTree, Context context, List<Node> datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
}
public SimpleTreeRecyclerAdapter(RecyclerView mTree, Context context, List<Node> datas, int defaultExpandLevel) {
super(mTree, context, datas, defaultExpandLevel);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyHoder(View.inflate(mContext, R.layout.list_item,null));
}
@Override
public void onBindViewHolder(final Node node, RecyclerView.ViewHolder holder, int position) {
final MyHoder viewHolder = (MyHoder) holder;
//todo do something
viewHolder.cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setChecked(node,viewHolder.cb.isChecked());
}
});
if (node.isChecked()){
viewHolder.cb.setChecked(true);
}else {
viewHolder.cb.setChecked(false);
}
if (node.getIcon() == -1) {
viewHolder.icon.setVisibility(View.INVISIBLE);
} else {
viewHolder.icon.setVisibility(View.VISIBLE);
viewHolder.icon.setImageResource(node.getIcon());
}
viewHolder.label.setText(node.getName());
}
class MyHoder extends RecyclerView.ViewHolder{
public CheckBox cb;
public TextView label;
public ImageView icon;
public MyHoder(View itemView) {
super(itemView);
cb = (CheckBox) itemView
.findViewById(R.id.cb_select_tree);
label = (TextView) itemView
.findViewById(R.id.id_treenode_label);
icon = (ImageView) itemView.findViewById(R.id.icon);
}
}
}
初始化 ListView
//第一個(gè)參數(shù) ListView
//第二個(gè)參數(shù) 上下文
//第三個(gè)參數(shù) 數(shù)據(jù)集
//第四個(gè)參數(shù) 默認(rèn)展開層級(jí)數(shù) 0為不展開
//第五個(gè)參數(shù) 展開的圖標(biāo)
//第六個(gè)參數(shù) 閉合的圖標(biāo)
mAdapter = new SimpleTreeAdapter(mTree, ListViewActivity.this,
mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
mTree.setAdapter(mAdapter);
初始化RecyclerView
//第一個(gè)參數(shù) RecyclerView
//第二個(gè)參數(shù) 上下文
//第三個(gè)參數(shù) 數(shù)據(jù)集
//第四個(gè)參數(shù) 默認(rèn)展開層級(jí)數(shù) 0為不展開
//第五個(gè)參數(shù) 展開的圖標(biāo)
//第六個(gè)參數(shù) 閉合的圖標(biāo)
mAdapter = new SimpleTreeRecyclerAdapter(mTree, RecyclerViewActivity.this,
mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
mTree.setAdapter(mAdapter);
添加數(shù)據(jù)鹦肿,可以保持原有選中或者展開狀態(tài)
List<Node> mlist = new ArrayList<>();
mlist.add(new Node("223","0","我也是添加的root節(jié)點(diǎn)",new FileNode()));
mAdapter.addData(0,mlist);
獲取選中內(nèi)容:如果node的isChecked()為true矗烛,即為選中狀態(tài)。
StringBuilder sb = new StringBuilder();
//獲取排序過的nodes
//如果不需要刻意直接用 mDatas既可
final List<Node> allNodes = mAdapter.getAllNodes();
for (int i = 0; i < allNodes.size(); i++) {
if (allNodes.get(i).isChecked()){
sb.append(allNodes.get(i).getName()+",");
}
}
String strNodesName = sb.toString();
if (!TextUtils.isEmpty(strNodesName))
Toast.makeText(this, strNodesName.substring(0, strNodesName.length()-1),Toast.LENGTH_SHORT).show();
控制父子之間聯(lián)動(dòng)的選中與取消狀態(tài)箩溃,只需調(diào)用setChecked方法既可,注意如果在setOnCheckedChangeListener中處理會(huì)有問題:因?yàn)槿绻庸?jié)點(diǎn)/父節(jié)點(diǎn)選中或者取消需要刷新頁面碌嘀,而刷新頁面又會(huì)觸發(fā)viewHolder.cb.setChecked(true/false);的判斷從而又會(huì)進(jìn)入setOnCheckedChangeListener涣旨,會(huì)導(dǎo)致如果父節(jié)點(diǎn)選中某些子節(jié)點(diǎn)取消不了的情況。
//viewHolder.cb 為CheckBox
viewHolder.cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setChecked(node,viewHolder.cb.isChecked());
}
});
簡(jiǎn)單介紹
通過一個(gè)ListView來展示所有數(shù)據(jù)股冗,每一級(jí)內(nèi)容的顯示根據(jù)當(dāng)前展示數(shù)據(jù)的等級(jí)縮進(jìn)一定的padding值霹陡,讓我們看起來有縮進(jìn)效果。
使用過程中感覺不是很舒服的地方在于最終用于顯示在界面實(shí)體Bean并不是我們傳進(jìn)去的數(shù)據(jù)止状,而是經(jīng)過轉(zhuǎn)化并且過濾的數(shù)據(jù)烹棉,這樣最直接的影響就是在我新增數(shù)據(jù)的數(shù)據(jù)之后,拿著Adapter來刷新的時(shí)候怯疤,并沒有任何效果浆洗。因?yàn)槲覀儧]有將后面新加的數(shù)據(jù)進(jìn)行轉(zhuǎn)化。
而我們?nèi)绾文茉诓桓淖冊(cè)袛?shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上集峦,添加我們的新內(nèi)容伏社,并保持原有的選中或者展開正常呢?我的想法是這樣的塔淤,如果可以直接給它傳入轉(zhuǎn)化后的Node節(jié)點(diǎn)類型數(shù)據(jù)就好了摘昌,我想到了繼承,讓實(shí)體類去繼承基類Node高蜂,但一旦繼承Node則意味著實(shí)體類就不能再繼承其他類了聪黎,感覺不是很靈活,而且也影響了實(shí)體類本身的結(jié)構(gòu)备恤。后來想到了包裝設(shè)計(jì)模式的一些東西稿饰,那我就在實(shí)體類外再包上一層,也就是將實(shí)體類傳給Node烘跺,最終我們使用的還是Node湘纵,但也可以用node.bean很輕松的取出實(shí)體類做其他操作,并且實(shí)體類本身的結(jié)構(gòu)并沒有被破壞滤淳。
在此基礎(chǔ)上梧喷,因?yàn)槲覀兊腘ode不需要轉(zhuǎn)化重新創(chuàng)建,那么它就可以保存一些狀態(tài)比如展開、選中等等铺敌,而在新加入數(shù)據(jù)時(shí)只需標(biāo)記下新加入的數(shù)據(jù)汇歹,只需對(duì)新加入的數(shù)據(jù)進(jìn)行初始化狀態(tài),已有老數(shù)據(jù)不進(jìn)行狀態(tài)改變:
if (node.isNewAdd && defaultExpandLeval >= currentLevel) {
node.setExpand(true);
}
項(xiàng)目地址:https://github.com/zhangke3016/MultilevelTreeList
原文連接:http://www.reibang.com/p/090904d2b689