Android使用ListView实现一个高性能无限层级显示的树形控件:
最近公司的Android项目里有一个地方需要选择某公司的所有部门,因为手机屏幕有限所以并不能像网页那样显示树状结构,但是如果只是用列表依次显示所有的部门又会让用户很难找到想要找的部门(即使加上搜索功能也很难表现出层级关系),由于系统控件ExpandableListView 只能显示两级,加上数据集的组织比较麻烦,所以就使用ListView来实现如下的树形展示效果。至于为什么使用listview最大的好处就是它自带控件复用功能,我们不用去处理这个复杂的问题。最终效果如下:
分析:
因为要展示的是一个树形结构,所以每一条记录必须拥有一个指向父亲节点的字段。为了体现出层级结构,其实就是增加缩进就可以了。当然说起来很简单,但是做起来的时候会有很多地方值得注意,比如说如何处理展开和收缩,以及跨级展开,收缩,如何得到当前是第几层从而处理缩进等等,再比如说如果数据量比较大的话性能怎么样等等一系列问题。
接下来我们就以层级显示一个公司的所有部门为需求来实现一下,其实只要具有树形结构我们都可以这样做。
实现思路以及使用方法:
首先我们要定义一个抽象类,其中包含必需的字段和方法:
/**
* Created by HQOCSHheqing on 2016/8/2.
*
* @description 节点抽象类(泛型T主要是考虑到ID和parentID有可能是int型也有可能是String型
* 即这里可以传入Integer或者String,具体什么类型由子类指定
,因为这两种类型比较是否相等的方式不同:一个是用 “==”,一个是用 equals() 函数)
*/
public abstract class Node<T> {
private int _level = -1;//当前节点的层级,初始值-1 后面会讲到
private List<Node> _childrenList = new ArrayList<>();//所有的孩子节点
private Node _parent;//父亲节点
private int _icon;//图标资源ID
private boolean isExpand = false;//当前状态是否展开
public abstract T get_id();//得到当前节点ID
public abstract T get_parentId();//得到当前节点的父ID
public abstract String get_label();//要显示的内容
public abstract boolean parent(Node dest);//判断当前节点是否是dest的父亲节点
public abstract boolean child(Node dest);//判断当前节点是否是dest的孩子节点
public int get_level() {
if (_level == -1){//如果是 -1 的话就递归获取
//因为是树形结构,所以此处想要得到当前节点的层级
//,必须递归调用,但是递归效率低下,如果每次都递归获取会严重影响性能,所以我们把第一次
//得到的结果保存起来避免每次递归获取
int level = _parent == null ? 1 : _parent.get_level()+1;
_level = level;
return _level;
}
return _level;
}
public void set_level(int _level) {
this._level = _level;
}
public List<Node> get_childrenList() {
return _childrenList;
}
public void set_childrenList(List<Node> _childrenList) {
this._childrenList = _childrenList;
}
public Node get_parent() {
return _parent;
}
public void set_parent(Node _parent) {
this._parent = _parent;
}
public int get_icon() {
return _icon;
}
public void set_icon(int _icon) {
this._icon = _icon;
}
public boolean isExpand() {
return isExpand;
}
public void setIsExpand(boolean isExpand) {
this.isExpand = isExpand;
if (isExpand){
_icon = R.mipmap.collapse;
}else{
_icon = R.mipmap.expand;
}
}
public boolean isRoot(){
return _parent == null;
}
public boolean isLeaf(){
return _childrenList.size() <= 0;
}
}
将我们要树状显示的实体继承此抽象类,我们一部门ID以及parentid为integer型为例:
/**
* Created by HQOCSHheqing on 2016/8/2.
*
* @description 部门类(继承Node),此处的泛型Integer是因为ID和parentID都为int
* ,如果为String传入泛型Strin