需求分析
将课本的章节以多级列表的形式显示。
解决方案思路
1.嵌套多级 RecyclerView,两级列表还好说,每多一级都是一场噩梦。
2.ExpandableListView?但是ExpandableListView只支持两级,不满足需求。
3.一个RecyclerView或者 ListView 来实现,每一级节点的要素有:当前节点id、父级节点id即pid,显示的内容。
实现方案
//id pid name FileNode为实际用的实体Bean对象
mlist.add(new Node("223","0","我也是添加的root节点",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
//第一个参数 ListView
//第二个参数 上下文
//第三个参数 数据集
//第四个参数 默认展开层级数 0为不展开
//第五个参数 展开的图标
//第六个参数 闭合的图标
mAdapter = new SimpleTreeAdapter(mTree, ListViewActivity.this,
mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
mTree.setAdapter(mAdapter);
初始化RecyclerView
//第一个参数 RecyclerView
//第二个参数 上下文
//第三个参数 数据集
//第四个参数 默认展开层级数 0为不展开
//第五个参数 展开的图标
//第六个参数 闭合的图标
mAdapter = new SimpleTreeRecyclerAdapter(mTree, RecyclerViewActivity.this,
mDatas, 1,R.mipmap.tree_ex,R.mipmap.tree_ec);
mTree.setAdapter(mAdapter);
添加数据,可以保持原有选中或者展开状态
List<Node> mlist = new ArrayList<>();
mlist.add(new Node("223","0","我也是添加的root节点",new FileNode()));
mAdapter.addData(0,mlist);
获取选中内容:如果node的isChecked()为true,即为选中状态。
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();
控制父子之间联动的选中与取消状态,只需调用setChecked方法既可,注意如果在setOnCheckedChangeListener中处理会有问题:因为如果要子节点/父节点选中或者取消需要刷新页面,而刷新页面又会触发viewHolder.cb.setChecked(true/false);的判断从而又会进入setOnCheckedChangeListener,会导致如果父节点选中某些子节点取消不了的情况。
//viewHolder.cb 为CheckBox
viewHolder.cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setChecked(node,viewHolder.cb.isChecked());
}
});
简单介绍
通过一个ListView来展示所有数据,每一级内容的显示根据当前展示数据的等级缩进一定的padding值,让我们看起来有缩进效果。
使用过程中感觉不是很舒服的地方在于最终用于显示在界面实体Bean并不是我们传进去的数据,而是经过转化并且过滤的数据,这样最直接的影响就是在我新增数据的数据之后,拿着Adapter来刷新的时候,并没有任何效果。因为我们没有将后面新加的数据进行转化。
而我们如何能在不改变原有数据结构的基础上,添加我们的新内容,并保持原有的选中或者展开正常呢?我的想法是这样的,如果可以直接给它传入转化后的Node节点类型数据就好了,我想到了继承,让实体类去继承基类Node,但一旦继承Node则意味着实体类就不能再继承其他类了,感觉不是很灵活,而且也影响了实体类本身的结构。后来想到了包装设计模式的一些东西,那我就在实体类外再包上一层,也就是将实体类传给Node,最终我们使用的还是Node,但也可以用node.bean很轻松的取出实体类做其他操作,并且实体类本身的结构并没有被破坏。
在此基础上,因为我们的Node不需要转化重新创建,那么它就可以保存一些状态比如展开、选中等等,而在新加入数据时只需标记下新加入的数据,只需对新加入的数据进行初始化状态,已有老数据不进行状态改变:
if (node.isNewAdd && defaultExpandLeval >= currentLevel) {
node.setExpand(true);
}
项目地址:https://github.com/zhangke3016/MultilevelTreeList
原文连接:https://www.jianshu.com/p/090904d2b689











网友评论