Android UI 之实现多级列表TreeView
所谓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>
转载请注明:http://blog.csdn.net/carrey1989/article/details/10227165
Android UI 之实现多级列表TreeView的更多相关文章
- Android UI组件----ListView列表控件详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...
- Android UI(五)云通讯录项目之联系人列表,带侧滑选择,带搜索框
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节.交流QQ群:[编程之美 365234583]h ...
- 基于bootstrap的jQuery多级列表树插件 treeview
http://www.cnblogs.com/mfc-itblog/p/5233453.html http://www.htmleaf.com/jQuery/Menu-Navigation/20150 ...
- 基于bootstrup treeview多级列表树插件
<!doctype html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...
- GitHub上受欢迎的Android UI Library
GitHub上受欢迎的Android UI Library 内容 抽屉菜单 ListView WebView SwitchButton 按钮 点赞按钮 进度条 TabLayout 图标 下拉刷新 Vi ...
- Android UI相关开源项目库汇总
最近做了一个Android UI相关开源项目库汇总,里面集合了OpenDigg 上的优质的Android开源项目库,方便移动开发人员便捷的找到自己需要的项目工具等,感兴趣的可以到GitHub上给个st ...
- Android UI目录
Android UI目录 序:最近一直想进阶android应用开发,虽然对一些相关的android知识都大体熟悉,但是自己的android知识体系,经不起推敲.经不起高手的垂问.经过几个月的努力学习, ...
- Android UI 绘制过程浅析(五)自定义View
前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View> ...
- 免费的Android UI库及组件推荐
短短数年时间Android平台就已经形成了一个庞大而活跃的开发者社区.许多社区开发的项目业已进入成熟阶段,甚至可以用于商业的软件生产中,且不用担心质量问题. 本文编译自androiduipattern ...
随机推荐
- MapReduce实现倒排索引(类似协同过滤)
一.问题背景 倒排索引其实就是出现次数越多,那么权重越大,不过我国有凤巢....zf为啥不管,总局回应推广是不是广告有争议... eclipse里ctrl+t找接口或者抽象类的实现类,看看都有啥方法, ...
- 搭建spring mvc项目
在之前搭建maven项目这篇的基础上继续集成,引入spring mvc支持 一.添加jar包引用 修改pom.xml文件,加入:(其他关联的jar包maven会自动引用) <!-- 项目属性 - ...
- [UML]UML系列——活动图activity diagram
系列文章 [UML]UML系列——用例图Use Case [UML]UML系列——用例图中的各种关系(include.extend) [UML]UML系列——类图Class [UML]UML系列——类 ...
- Bash 里的转义序列
在 Bash 里,一共有五个地方支持反斜杠开头的转义序列,包括两个内部命令 echo 和 printf 的参数里,字符串语法 $'...' 里,还有四个提示符变量 PS1-PS4 里,以及在 Read ...
- 使用JVMTI创建调试和监控代理
Java 虚拟机工具接口(JVMTI)提供了一个编程接口,允许你(程序员)创建software agent 来监视和控制你的Java应用. JVMTI 代替了原来的Java Virtual Machi ...
- CSS透明代码
透明往往能产生不错的网页视觉效果,先奉上兼容主流浏览器的CSS透明代码: .transparent_class { filter:alpha(opacity=50); -moz-opacity:0.5 ...
- MongoDB系列一:CentOS7.2下安装mongoDB3.2.8
最近在又在倒腾MongoDB,把安装配置的相关命令贴出来 1.下载 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70- ...
- thinkphp表单自动验证
ThinkPHP框架表单验证 对注册到test表的表单进行验证 在注册之前要对表单进行验证: 用户名非空验证,两次输入密码必须一致即相等验证,年龄在18~50之间即范围验证,邮箱格式正则验证. 自动验 ...
- spring aop 环绕通知around和其他通知的区别
前言: spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别: 1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只 ...
- Class.forName的使用
Class.forName的使用 Class.forName返回一个类,使用此方法可以获取类 首先,创建一个Student类 /*** * This Class is for Student bean ...