Android UI 之实现多级树形列表TreeView示例
程序员文章站
2024-02-10 18:33:40
所谓treeview就是在windows中常见的多级列表树,在android中系统只默认提供了listview和expandablelistview两种列表,最多只支持到二...
所谓treeview就是在windows中常见的多级列表树,在android中系统只默认提供了listview和expandablelistview两种列表,最多只支持到二级列表的实现,所以如果想要实现三级和更多层次的列表,就需要我们自己来做一些处理了。
其实这个效果很久以前就有人想办法实现了,但是实现的效果有一些问题,我的实现思路主要也是来自于网络,但是在其基础上修正了逻辑上的一些错误,做了一些优化。
先来看一下效果:
然后大体说一下思路:
其实这里实现的多级列表只是一个视觉效果,我们看到的分级效果是由于每行的缩进不同造成的。比如在上面的效果中,山东省和广东省是级别最高的层次,山东省下的青岛市作为山东省的子项,我们增加他的左缩进,这样看起来就有了层次感了。其他的层次也是同理。
也就是说,我们只用了一个listview,工作的重点就在于不断变化listview显示的数据,根据用户的操作,将数据修改为用户想要看到的数据内容,并根据每个数据项的不同,在显示效果上做不同的缩进处理,最终呈现出一个treeview的效果。
具体的实现思路参考下面的项目结构和具体代码:
element.java:
package com.example.androidtreeviewdemo.treeview; /** * element类 * @author carrey * */ public class element { /** 文字内容 */ private string contenttext; /** 在tree中的层级 */ private int level; /** 元素的id */ private int id; /** 父元素的id */ private int parendid; /** 是否有子元素 */ private boolean haschildren; /** item是否展开 */ private boolean isexpanded; /** 表示该节点没有父元素,也就是level为0的节点 */ public static final int no_parent = -1; /** 表示该元素位于最顶层的层级 */ public static final int top_level = 0; public element(string contenttext, int level, int id, int parendid, boolean haschildren, boolean isexpanded) { super(); this.contenttext = contenttext; this.level = level; this.id = id; this.parendid = parendid; this.haschildren = haschildren; this.isexpanded = isexpanded; } public boolean isexpanded() { return isexpanded; } public void setexpanded(boolean isexpanded) { this.isexpanded = isexpanded; } public string getcontenttext() { return contenttext; } public void setcontenttext(string contenttext) { this.contenttext = contenttext; } public int getlevel() { return level; } public void setlevel(int level) { this.level = level; } public int getid() { return id; } public void setid(int id) { this.id = id; } public int getparendid() { return parendid; } public void setparendid(int parendid) { this.parendid = parendid; } public boolean ishaschildren() { return haschildren; } public void sethaschildren(boolean haschildren) { this.haschildren = haschildren; } }
treeviewadapter.java:
package com.example.androidtreeviewdemo.treeview; import java.util.arraylist; import com.example.androidtreeviewdemo.r; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.imageview; import android.widget.textview; /** * treeviewadapter * @author carrey * */ public class treeviewadapter extends baseadapter { /** 元素数据源 */ private arraylist<element> elementsdata; /** 树中元素 */ private arraylist<element> elements; /** layoutinflater */ private layoutinflater inflater; /** item的行首缩进基数 */ private int indentionbase; public treeviewadapter(arraylist<element> elements, arraylist<element> elementsdata, layoutinflater inflater) { this.elements = elements; this.elementsdata = elementsdata; this.inflater = inflater; indentionbase = 50; } public arraylist<element> getelements() { return elements; } public arraylist<element> getelementsdata() { return elementsdata; } @override public int getcount() { return elements.size(); } @override public object getitem(int position) { return elements.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { viewholder holder = null; if (convertview == null) { holder = new viewholder(); convertview = inflater.inflate(r.layout.treeview_item, null); holder.disclosureimg = (imageview) convertview.findviewbyid(r.id.disclosureimg); holder.contenttext = (textview) convertview.findviewbyid(r.id.contenttext); convertview.settag(holder); } else { holder = (viewholder) convertview.gettag(); } element element = elements.get(position); int level = element.getlevel(); holder.disclosureimg.setpadding( indentionbase * (level + 1), holder.disclosureimg.getpaddingtop(), holder.disclosureimg.getpaddingright(), holder.disclosureimg.getpaddingbottom()); holder.contenttext.settext(element.getcontenttext()); if (element.ishaschildren() && !element.isexpanded()) { holder.disclosureimg.setimageresource(r.drawable.close); //这里要主动设置一下icon可见,因为convertview有可能是重用了"设置了不可见"的view,下同。 holder.disclosureimg.setvisibility(view.visible); } else if (element.ishaschildren() && element.isexpanded()) { holder.disclosureimg.setimageresource(r.drawable.open); holder.disclosureimg.setvisibility(view.visible); } else if (!element.ishaschildren()) { holder.disclosureimg.setimageresource(r.drawable.close); holder.disclosureimg.setvisibility(view.invisible); } return convertview; } /** * 优化holder * @author carrey * */ static class viewholder{ imageview disclosureimg; textview contenttext; } }
treeviewitemclicklistener.java:
package com.example.androidtreeviewdemo.treeview; import java.util.arraylist; import android.view.view; import android.widget.adapterview; import android.widget.adapterview.onitemclicklistener; /** * treeview item点击事件 * @author carrey * */ public class treeviewitemclicklistener implements onitemclicklistener { /** adapter */ private treeviewadapter treeviewadapter; public treeviewitemclicklistener(treeviewadapter treeviewadapter) { this.treeviewadapter = treeviewadapter; } @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { //点击的item代表的元素 element element = (element) treeviewadapter.getitem(position); //树中的元素 arraylist<element> elements = treeviewadapter.getelements(); //元素的数据源 arraylist<element> elementsdata = treeviewadapter.getelementsdata(); //点击没有子项的item直接返回 if (!element.ishaschildren()) { return; } if (element.isexpanded()) { element.setexpanded(false); //删除节点内部对应子节点数据,包括子节点的子节点... arraylist<element> elementstodel = new arraylist<element>(); for (int i = position + 1; i < elements.size(); i++) { if (element.getlevel() >= elements.get(i).getlevel()) break; elementstodel.add(elements.get(i)); } elements.removeall(elementstodel); treeviewadapter.notifydatasetchanged(); } else { element.setexpanded(true); //从数据源中提取子节点数据添加进树,注意这里只是添加了下一级子节点,为了简化逻辑 int i = 1;//注意这里的计数器放在for外面才能保证计数有效 for (element e : elementsdata) { if (e.getparendid() == element.getid()) { e.setexpanded(false); elements.add(position + i, e); i ++; } } treeviewadapter.notifydatasetchanged(); } } }
mainactivity.java:
package com.example.androidtreeviewdemo; import java.util.arraylist; import com.example.androidtreeviewdemo.treeview.element; import com.example.androidtreeviewdemo.treeview.treeviewadapter; import com.example.androidtreeviewdemo.treeview.treeviewitemclicklistener; import android.os.bundle; import android.app.activity; import android.content.context; import android.view.layoutinflater; import android.view.menu; import android.widget.listview; public class mainactivity extends activity { /** 树中的元素集合 */ private arraylist<element> elements; /** 数据源元素集合 */ private arraylist<element> elementsdata; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); layoutinflater inflater = (layoutinflater) getsystemservice(context.layout_inflater_service); init(); listview treeview = (listview) findviewbyid(r.id.treeview); treeviewadapter treeviewadapter = new treeviewadapter( elements, elementsdata, inflater); treeviewitemclicklistener treeviewitemclicklistener = new treeviewitemclicklistener(treeviewadapter); treeview.setadapter(treeviewadapter); treeview.setonitemclicklistener(treeviewitemclicklistener); } private void init() { elements = new arraylist<element>(); elementsdata = new arraylist<element>(); //添加节点 -- 节点名称,节点level,节点id,父节点id,是否有子节点,是否展开 //添加最外层节点 element e1 = new element("山东省", element.top_level, 0, element.no_parent, true, false); //添加第一层节点 element e2 = new element("青岛市", element.top_level + 1, 1, e1.getid(), true, false); //添加第二层节点 element e3 = new element("市南区", element.top_level + 2, 2, e2.getid(), true, false); //添加第三层节点 element e4 = new element("香港中路", element.top_level + 3, 3, e3.getid(), false, false); //添加第一层节点 element e5 = new element("烟台市", element.top_level + 1, 4, e1.getid(), true, false); //添加第二层节点 element e6 = new element("芝罘区", element.top_level + 2, 5, e5.getid(), true, false); //添加第三层节点 element e7 = new element("凤凰台街道", element.top_level + 3, 6, e6.getid(), false, false); //添加第一层节点 element e8 = new element("威海市", element.top_level + 1, 7, e1.getid(), false, false); //添加最外层节点 element e9 = new element("广东省", element.top_level, 8, element.no_parent, true, false); //添加第一层节点 element e10 = new element("深圳市", element.top_level + 1, 9, e9.getid(), true, false); //添加第二层节点 element e11 = new element("南山区", element.top_level + 2, 10, e10.getid(), true, false); //添加第三层节点 element e12 = new element("深南大道", element.top_level + 3, 11, e11.getid(), true, false); //添加第四层节点 element e13 = new element("10000号", element.top_level + 4, 12, e12.getid(), false, false); //添加初始树元素 elements.add(e1); elements.add(e9); //创建数据源 elementsdata.add(e1); elementsdata.add(e2); elementsdata.add(e3); elementsdata.add(e4); elementsdata.add(e5); elementsdata.add(e6); elementsdata.add(e7); elementsdata.add(e8); elementsdata.add(e9); elementsdata.add(e10); elementsdata.add(e11); elementsdata.add(e12); elementsdata.add(e13); } @override public boolean oncreateoptionsmenu(menu menu) { // inflate the menu; this adds items to the action bar if it is present. getmenuinflater().inflate(r.menu.activity_main, menu); return true; } }
treeview_item.xml:
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <imageview android:id="@+id/disclosureimg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centervertical="true" android:layout_alignparentleft="true"/> <textview android:id="@+id/contenttext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centervertical="true" android:layout_torightof="@id/disclosureimg"/> </relativelayout>
activity_main.xml:
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".mainactivity" > <listview android:id="@+id/treeview" android:layout_width="match_parent" android:layout_height="match_parent"/> </relativelayout>
下载地址:androidtreeviewdemo_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。