欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现树形层级ListView

程序员文章站 2024-02-20 23:07:58
直接贴代码,代码中有相应注释: 主界面activity,布局就只一个listview: public class mainactivity extends...

直接贴代码,代码中有相应注释:

主界面activity,布局就只一个listview:

public class mainactivity extends activity {
private listview mlistview;
private treelistviewadapter<testbean> madapter;
private list<testbean> mdatas = new arraylist<testbean>();

@override
protected void oncreate(bundle savedinstancestate) {
// todo auto-generated method stub
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
this.mlistview = (listview) findviewbyid(r.id.listview);
inittestdatas();
try {
madapter = new treelistviewadapter<testbean>(mlistview, this, mdatas, 0);
}
catch (exception e) {
e.printstacktrace();
}
this.mlistview.setadapter(madapter);
madapter.setmtreelistener(new treeviewonitemclick() {


@override
public void ontreeitemclick(int position, node node) {
toast.maketext(mainactivity.this, "你点击的是:" + node.getname(), toast.length_short).show();
}
});
this.mlistview.setonitemlongclicklistener(new onitemlongclicklistener() {


@override
public boolean onitemlongclick(adapterview<?> arg0, view arg1, final int arg2, long arg3) {
final edittext edt = new edittext(mainactivity.this);
new alertdialog.builder(mainactivity.this).settitle("insert").setview(edt).setpositivebutton("submit", new onclicklistener() {

@override
public void onclick(dialoginterface dialog, int which) {
if (textutils.isempty(edt.gettext().tostring())) {
toast.maketext(mainactivity.this, "请填写添加内容", toast.length_short).show();
}
else {
madapter.insertnodedata(arg2, edt.gettext().tostring());
}
}
}).setnegativebutton("cancel", null).create().show();
return true;
}
});
}


private void inittestdatas() {
testbean bean = null;
bean = new testbean(1, 0, "文件目录1");
mdatas.add(bean);
bean = new testbean(2, 0, "文件目录2");
mdatas.add(bean);
bean = new testbean(3, 0, "文件目录3");
mdatas.add(bean);
bean = new testbean(4, 1, "文件目录4");
mdatas.add(bean);
bean = new testbean(5, 1, "文件目录5");
mdatas.add(bean);
bean = new testbean(6, 2, "文件目录6");
mdatas.add(bean);
bean = new testbean(7, 2, "文件目录7");
mdatas.add(bean);
bean = new testbean(8, 3, "文件目录8");
mdatas.add(bean);
bean = new testbean(9, 3, "文件目录9");
mdatas.add(bean);
bean = new testbean(10, 0, "文件目录10");
mdatas.add(bean);
}
}

数据适配器基类:

**
 * 树形listview的数据适配器类
 * @description:
 * @author ldm
 * @date 2015-10-9 上午9:47:01
 */
public abstract class treeviewbaseadapter<t> extends baseadapter {
protected context context;
protected list<t> datas;
protected list<node> mallnodes;
protected list<node> mvisiblenodes;
protected layoutinflater minflater;
protected listview treelv;
protected treeviewonitemclick mtreelistener;


public treeviewbaseadapter(listview treelv, context context, list<t> datas, int defaultexpandlevel) throws illegalaccessexception, illegalargumentexception {
this.context = context;
this.treelv = treelv;
minflater = layoutinflater.from(context);
mallnodes = treehelpertools.getsortednodes(datas, defaultexpandlevel);
mvisiblenodes = treehelpertools.filtervisiblenodes(mallnodes);
this.treelv.setonitemclicklistener(new onitemclicklistener() {


@override
public void onitemclick(adapterview<?> arg0, view arg1, int position, long arg3) {
expandorcollapse(position);
if(mtreelistener!=null){
mtreelistener.ontreeitemclick(position, mvisiblenodes.get(position));
}
}
});
}


public void setmtreelistener(treeviewonitemclick mtreelistener) {
this.mtreelistener = mtreelistener;
}


/**
* 设置listview点击item节点时,是否应该展开
* @description:
* @author ldm
* @date 2015-10-10 上午9:05:08
*/
protected void expandorcollapse(int position) {
node n=mvisiblenodes.get(position);
if(n!=null){
if(n.isleaf()){
return;
}
n.setexpand(!n.isexpand());
mvisiblenodes=treehelpertools.filtervisiblenodes(mallnodes);
notifydatasetchanged();
}
}


@override
public int getcount() {
// todo auto-generated method stub
return mvisiblenodes.size();
}


@override
public object getitem(int position) {
// todo auto-generated method stub
return mvisiblenodes.get(position);
}


@override
public long getitemid(int position) {
// todo auto-generated method stub
return position;
}


@override
public view getview(int position, view convertview, viewgroup parent) {
node node=mvisiblenodes.get(position);
convertview = getcontentview(node,position, convertview, parent);
return convertview;
}


public abstract view getcontentview(node node,int position, view convertview, viewgroup parent);
public interface treeviewonitemclick{
void ontreeitemclick(int position,node node);
}
}

我们使用的adapter

 public class treelistviewadapter<t> extends treeviewbaseadapter<t> {


public treelistviewadapter(listview treelv, context context, list<t> datas, int defaultexpandlevel) throws illegalaccessexception, illegalargumentexception {
super(treelv, context, datas, defaultexpandlevel);
}


@override
public view getcontentview(node node, int position, view convertview, viewgroup parent) {
viewholder holder = null;
if (convertview == null) {
holder = new viewholder();
convertview = minflater.inflate(r.layout.tree_listview_item, parent, false);
holder.mitemiv = (imageview) convertview.findviewbyid(r.id.mitemiv);
holder.mitemtv = (textview) convertview.findviewbyid(r.id.mitemtv);
convertview.settag(holder);
}
else {
holder = (viewholder) convertview.gettag();
}
holder.mitemiv.setpadding(node.getlevel()*30, 3, 3, 3);
if (node.geticon() == -1) {
holder.mitemiv.setvisibility(view.invisible);
}
else {
holder.mitemiv.setvisibility(view.visible);
holder.mitemiv.setimageresource(node.geticon());
}
holder.mitemtv.settext(node.getname());
return convertview;
}


private static class viewholder {
imageview mitemiv;
textview mitemtv;
}


/**
* 动态插入数据
* @description:
* @author ldm
* @date 2015-10-10 上午10:08:03
*/
public void insertnodedata(int position,string label) {
node node=mvisiblenodes.get(position);
int indexof=mallnodes.indexof(node);
node insertnode=new node(-1, node.getid(), label);
insertnode.setparent(node);
node.getchildren().add(insertnode);
mallnodes.add(indexof+1,insertnode);
mvisiblenodes=treehelpertools.filtervisiblenodes(mvisiblenodes);
notifydatasetchanged();
}
}

数据处理的工具类:

public class treedatashelpertools {
/**
* 将用户提供的数据转化成树层级上可用数据
* @description:
* @date 2015-10-9 下午4:07:24
*/
public static <t> list<node> datas2nodes(list<t> datas) throws illegalaccessexception, illegalargumentexception {
list<node> nodes = new arraylist<node>();
node node = null;
for (t t : datas) {
int id = -1;
int pid = -1;
string label = "";
// node = new node();
class clazz = t.getclass();
field[] fields = clazz.getdeclaredfields();// 反射获取类中的字段
for (field field : fields) {
if (field.getannotation(treenodeid.class) != null) {// 根据字段上的注解来获取对应的值
field.setaccessible(true);// 在java的反射使用中,如果字段是私有的,那么必须要对这个字段设置才能正常使用,否则报错
id = field.getint(t);
}
if (field.getannotation(treenodepid.class) != null) {
field.setaccessible(true);
pid = field.getint(t);
}
if (field.getannotation(treenodelabel.class) != null) {
field.setaccessible(true);
label = (string) field.get(t);
}
}
node = new node(id, pid, label);
nodes.add(node);


}
// 设置nodes中的父子节点关系
for (int i = 0; i < nodes.size(); i++) {
node n = nodes.get(i);
for (int j = i + 1; j < nodes.size(); j++) {
node m = nodes.get(j);
if (m.getpid() == n.getid()) {// 如果m的父节点pid==n的id,则m是父节点,n是子节点
n.getchildren().add(m);
m.setparent(n);
}
else if (m.getid() == n.getpid()) {
m.getchildren().add(n);
n.setparent(m);
}
}
}
// 设置节点图片
for (node n : nodes) {
setnodeicon(n);
}
return nodes;
}


/**
* 为我们的node数据设置对应的图标
* @description:
* @date 2015-10-9 下午4:46:29
*/
private static void setnodeicon(node n) {
if (n.getchildren().size() > 0 && n.isexpand()) {// 如果有子节点且展开状态
n.seticon(r.drawable.icon_unable);
}
else if (n.getchildren().size() > 0 && !n.isexpand()) {
n.seticon(r.drawable.icon_enable);
}
else {
n.seticon(-1);
}
}


public static <t> list<node> getsortednodes(list<t> datas, int defaultexpandlevel) throws illegalaccessexception, illegalargumentexception {
list<node> result = new arraylist<node>();
list<node> nodes = datas2nodes(datas);
// 首先获取根节点数据
list<node> rootnodes = getrootnodes(nodes);
for (node node : rootnodes) {
addnode(result, node, defaultexpandlevel, 1);
}
return result;


}



/**
* 获取数据中的所有根节点数据
* @description:
* @date 2015-10-9 下午5:00:32
*/
private static list<node> getrootnodes(list<node> nodes) {
list<node> root = new arraylist<node>();
for (node node : nodes) {
if (node.isroot()) {
root.add(node);
}
}
return root;
}


/**
* 获取到可见的节点数据
* @description:
* @date 2015-10-9 下午5:12:58
*/
public static list<node> filtervisiblenodes(list<node> mallnodes) {
list<node> nodes = new arraylist<node>();
for (node node : mallnodes) {
if (node.isroot() || node.isparentexpand()) {
setnodeicon(node);
nodes.add(node);
}
}
return nodes;
}


/**
* 把一个节点的所有子节点都放入result中
* @description:
* @date 2015-10-9 下午5:05:57
*/
private static void addnode(list<node> result, node node, int defaultexpandlevel, int currentlevel) {
result.add(node);
if (defaultexpandlevel >= currentlevel) {
node.setexpand(true);
}
if (node.isleaf()) { return; }
for (int i = 0; i < node.getchildren().size(); i++) {
addnode(result, node.getchildren().get(i), defaultexpandlevel, currentlevel + 1);
}
}
}

数据实体bean:

public class testbean {
@treenodeid
private int id;//添加对应的注解
@treenodepid
private int pid;
@treenodelabel
private string label;
private string desc;


public testbean(int id, int pid, string label) {
super();
this.id = id;
this.pid = pid;
this.label = label;
}


public testbean() {
// todo auto-generated constructor stub
}


public int getid() {
return id;
}


public void setid(int id) {
this.id = id;
}


public int getpid() {
return pid;
}


public void setpid(int pid) {
this.pid = pid;
}


public string getlabel() {
return label;
}


public void setlabel(string label) {
this.label = label;
}


public string getdesc() {
return desc;
}


public void setdesc(string desc) {
this.desc = desc;
}

}

数据展示中的node类,我们可以通过反射+注解把任意实体bean如testbean映射成我们想要的node

public class node {
private int id;// 所在节点id
private int pid = 0;// 父节点的id
private string name;// 对应的内容
private int level;// 所在listview中树层级
private boolean isexpand = false;// 所在节点是否展开
private int icon;// 图标icon
private node parent;// 父节点node
private list<node> children = new arraylist<node>();// 对应的子节点数据集
public node() {
}

public node(int id, int pid, string name) {
this.id = id;
this.pid = pid;
this.name = name;
}


public int getid() {
return id;
}


public void setid(int id) {
this.id = id;
}


public int getpid() {
return pid;
}


public void setpid(int pid) {
this.pid = pid;
}


public string getname() {
return name;
}


public void setname(string name) {
this.name = name;
}

/**
* 当前节点所在的层级
* @description:
* @date 2015-10-9 下午4:02:29
*/
public int getlevel() {
return parent == null ? 0 : parent.getlevel() + 1;
}


public void setlevel(int level) {
this.level = level;
}


public boolean isexpand() {
return isexpand;
}


public void setexpand(boolean isexpand) {
this.isexpand = isexpand;
if (!isexpand && children.size() > 0) {// 如果当前节点没有展开,则其子节点展开状态也是:没展开
for (node node : children) {
node.setexpand(false);
}
}
}


public int geticon() {
return icon;
}


public void seticon(int icon) {
this.icon = icon;
}


public node getparent() {
return parent;
}


public void setparent(node parent) {
this.parent = parent;
}


public list<node> getchildren() {
return children;
}


public void setchildren(list<node> children) {
this.children = children;
}


/**
* 判断当前节点有没有子节点
* @description:
* @author ldm
* @date 2015-10-9 下午3:59:42
*/
public boolean isleaf() {
return children.size() == 0;
}


/**
* 是不是根节点
* @description:
* @author ldm
* @date 2015-10-9 下午3:58:15
*/
public boolean isroot() {
return parent == null;
}


/**
* 当前节点所在父节点是否展开
* @description:
* @author ldm
* @date 2015-10-9 下午3:58:34
*/
public boolean isparentexpand() {
if (parent == null) {
return false;
}
else {
return parent.isexpand;
}
}
}

用到的注解类:

@target(elementtype.field)//定义注解的作用目标:字段、枚举的常量
@retention(retentionpolicy.runtime)//注解策略: 注解会在class字节码文件中存在,在运行时可以通过反射获取到
public @interface treenodeid {
}
@target(elementtype.field)//定义注解的作用目标:字段、枚举的常量
@retention(retentionpolicy.runtime)//注解策略: 注解会在class字节码文件中存在,在运行时可以通过反射获取到
public @interface treenodelabel {
}
``
@target(elementtype.field)//定义注解的作用目标:字段、枚举的常量 
@retention(retentionpolicy.runtime)//注解策略: 注解会在class字节码文件中存在,在运行时可以通过反射获取到 
public @interface treenodepid { 
}

以上就是android实现树形层级listview的相关代码,希望对大家的学习有所帮助。