Android UI 之实现多级列表TreeView

时间:2022-05-28 03:43:51

所谓TreeView就是在Windows中常见的多级列表树,在Android中系统只默认提供了ListView和ExpandableListView两种列表,最多只支持到二级列表的实现,所以如果想要实现三级和更多层次的列表,就需要我们自己来做一些处理了。

    其实这个效果很久以前就有人想办法实现了,但是实现的效果有一些问题,我的实现思路主要也是来自于网络,但是在其基础上修正了逻辑上的一些错误,做了一些优化。

    先来看一下效果:

Android UI 之实现多级列表TreeView

然后大体说一下思路:

    其实这里实现的多级列表只是一个视觉效果,我们看到的分级效果是由于每行的缩进不同造成的。比如在上面的效果中,山东省和广东省是级别最高的层次,山东省下的青岛市作为山东省的子项,我们增加他的左缩进,这样看起来就有了层次感了。其他的层次也是同理。

    也就是说,我们只用了一个ListView,工作的重点就在于不断变化ListView显示的数据,根据用户的操作,将数据修改为用户想要看到的数据内容,并根据每个数据项的不同,在显示效果上做不同的缩进处理,最终呈现出一个TreeView的效果。

    具体的实现思路参考下面的项目结构和具体代码:

Android UI 之实现多级列表TreeView

Element.Java:

  1. package com.example.androidtreeviewdemo.treeview;
  2. /**
  3. * Element类
  4. * @author carrey
  5. *
  6. */
  7. public class Element {
  8. /** 文字内容 */
  9. private String contentText;
  10. /** 在tree中的层级 */
  11. private int level;
  12. /** 元素的id */
  13. private int id;
  14. /** 父元素的id */
  15. private int parendId;
  16. /** 是否有子元素 */
  17. private boolean hasChildren;
  18. /** item是否展开 */
  19. private boolean isExpanded;
  20. /** 表示该节点没有父元素,也就是level为0的节点 */
  21. public static final int NO_PARENT = -1;
  22. /** 表示该元素位于最顶层的层级 */
  23. public static final int TOP_LEVEL = 0;
  24. public Element(String contentText, int level, int id, int parendId,
  25. boolean hasChildren, boolean isExpanded) {
  26. super();
  27. this.contentText = contentText;
  28. this.level = level;
  29. this.id = id;
  30. this.parendId = parendId;
  31. this.hasChildren = hasChildren;
  32. this.isExpanded = isExpanded;
  33. }
  34. public boolean isExpanded() {
  35. return isExpanded;
  36. }
  37. public void setExpanded(boolean isExpanded) {
  38. this.isExpanded = isExpanded;
  39. }
  40. public String getContentText() {
  41. return contentText;
  42. }
  43. public void setContentText(String contentText) {
  44. this.contentText = contentText;
  45. }
  46. public int getLevel() {
  47. return level;
  48. }
  49. public void setLevel(int level) {
  50. this.level = level;
  51. }
  52. public int getId() {
  53. return id;
  54. }
  55. public void setId(int id) {
  56. this.id = id;
  57. }
  58. public int getParendId() {
  59. return parendId;
  60. }
  61. public void setParendId(int parendId) {
  62. this.parendId = parendId;
  63. }
  64. public boolean isHasChildren() {
  65. return hasChildren;
  66. }
  67. public void setHasChildren(boolean hasChildren) {
  68. this.hasChildren = hasChildren;
  69. }
  70. }

TreeViewAdapter.java:

  1. package com.example.androidtreeviewdemo.treeview;
  2. import java.util.ArrayList;
  3. import com.example.androidtreeviewdemo.R;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.ViewGroup;
  7. import android.widget.BaseAdapter;
  8. import android.widget.ImageView;
  9. import android.widget.TextView;
  10. /**
  11. * TreeViewAdapter
  12. * @author carrey
  13. *
  14. */
  15. public class TreeViewAdapter extends BaseAdapter {
  16. /** 元素数据源 */
  17. private ArrayList<Element> elementsData;
  18. /** 树中元素 */
  19. private ArrayList<Element> elements;
  20. /** LayoutInflater */
  21. private LayoutInflater inflater;
  22. /** item的行首缩进基数 */
  23. private int indentionBase;
  24. public TreeViewAdapter(ArrayList<Element> elements, ArrayList<Element> elementsData, LayoutInflater inflater) {
  25. this.elements = elements;
  26. this.elementsData = elementsData;
  27. this.inflater = inflater;
  28. indentionBase = 50;
  29. }
  30. public ArrayList<Element> getElements() {
  31. return elements;
  32. }
  33. public ArrayList<Element> getElementsData() {
  34. return elementsData;
  35. }
  36. @Override
  37. public int getCount() {
  38. return elements.size();
  39. }
  40. @Override
  41. public Object getItem(int position) {
  42. return elements.get(position);
  43. }
  44. @Override
  45. public long getItemId(int position) {
  46. return position;
  47. }
  48. @Override
  49. public View getView(int position, View convertView, ViewGroup parent) {
  50. ViewHolder holder = null;
  51. if (convertView == null) {
  52. holder = new ViewHolder();
  53. convertView = inflater.inflate(R.layout.treeview_item, null);
  54. holder.disclosureImg = (ImageView) convertView.findViewById(R.id.disclosureImg);
  55. holder.contentText = (TextView) convertView.findViewById(R.id.contentText);
  56. convertView.setTag(holder);
  57. } else {
  58. holder = (ViewHolder) convertView.getTag();
  59. }
  60. Element element = elements.get(position);
  61. int level = element.getLevel();
  62. holder.disclosureImg.setPadding(
  63. indentionBase * (level + 1),
  64. holder.disclosureImg.getPaddingTop(),
  65. holder.disclosureImg.getPaddingRight(),
  66. holder.disclosureImg.getPaddingBottom());
  67. holder.contentText.setText(element.getContentText());
  68. if (element.isHasChildren() && !element.isExpanded()) {
  69. holder.disclosureImg.setImageResource(R.drawable.close);
  70. //这里要主动设置一下icon可见,因为convertView有可能是重用了"设置了不可见"的view,下同。
  71. holder.disclosureImg.setVisibility(View.VISIBLE);
  72. } else if (element.isHasChildren() && element.isExpanded()) {
  73. holder.disclosureImg.setImageResource(R.drawable.open);
  74. holder.disclosureImg.setVisibility(View.VISIBLE);
  75. } else if (!element.isHasChildren()) {
  76. holder.disclosureImg.setImageResource(R.drawable.close);
  77. holder.disclosureImg.setVisibility(View.INVISIBLE);
  78. }
  79. return convertView;
  80. }
  81. /**
  82. * 优化Holder
  83. * @author carrey
  84. *
  85. */
  86. static class ViewHolder{
  87. ImageView disclosureImg;
  88. TextView contentText;
  89. }
  90. }

TreeViewItemClickListener.java:

  1. package com.example.androidtreeviewdemo.treeview;
  2. import java.util.ArrayList;
  3. import android.view.View;
  4. import android.widget.AdapterView;
  5. import android.widget.AdapterView.OnItemClickListener;
  6. /**
  7. * TreeView item点击事件
  8. * @author carrey
  9. *
  10. */
  11. public class TreeViewItemClickListener implements OnItemClickListener {
  12. /** adapter */
  13. private TreeViewAdapter treeViewAdapter;
  14. public TreeViewItemClickListener(TreeViewAdapter treeViewAdapter) {
  15. this.treeViewAdapter = treeViewAdapter;
  16. }
  17. @Override
  18. public void onItemClick(AdapterView<?> parent, View view, int position,
  19. long id) {
  20. //点击的item代表的元素
  21. Element element = (Element) treeViewAdapter.getItem(position);
  22. //树中的元素
  23. ArrayList<Element> elements = treeViewAdapter.getElements();
  24. //元素的数据源
  25. ArrayList<Element> elementsData = treeViewAdapter.getElementsData();
  26. //点击没有子项的item直接返回
  27. if (!element.isHasChildren()) {
  28. return;
  29. }
  30. if (element.isExpanded()) {
  31. element.setExpanded(false);
  32. //删除节点内部对应子节点数据,包括子节点的子节点...
  33. ArrayList<Element> elementsToDel = new ArrayList<Element>();
  34. for (int i = position + 1; i < elements.size(); i++) {
  35. if (element.getLevel() >= elements.get(i).getLevel())
  36. break;
  37. elementsToDel.add(elements.get(i));
  38. }
  39. elements.removeAll(elementsToDel);
  40. treeViewAdapter.notifyDataSetChanged();
  41. } else {
  42. element.setExpanded(true);
  43. //从数据源中提取子节点数据添加进树,注意这里只是添加了下一级子节点,为了简化逻辑
  44. int i = 1;//注意这里的计数器放在for外面才能保证计数有效
  45. for (Element e : elementsData) {
  46. if (e.getParendId() == element.getId()) {
  47. e.setExpanded(false);
  48. elements.add(position + i, e);
  49. i ++;
  50. }
  51. }
  52. treeViewAdapter.notifyDataSetChanged();
  53. }
  54. }
  55. }

MainActivity.java:

  1. package com.example.androidtreeviewdemo;
  2. import java.util.ArrayList;
  3. import com.example.androidtreeviewdemo.treeview.Element;
  4. import com.example.androidtreeviewdemo.treeview.TreeViewAdapter;
  5. import com.example.androidtreeviewdemo.treeview.TreeViewItemClickListener;
  6. import android.os.Bundle;
  7. import android.app.Activity;
  8. import android.content.Context;
  9. import android.view.LayoutInflater;
  10. import android.view.Menu;
  11. import android.widget.ListView;
  12. public class MainActivity extends Activity {
  13. /** 树中的元素集合 */
  14. private ArrayList<Element> elements;
  15. /** 数据源元素集合 */
  16. private ArrayList<Element> elementsData;
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  22. init();
  23. ListView treeview = (ListView) findViewById(R.id.treeview);
  24. TreeViewAdapter treeViewAdapter = new TreeViewAdapter(
  25. elements, elementsData, inflater);
  26. TreeViewItemClickListener treeViewItemClickListener = new TreeViewItemClickListener(treeViewAdapter);
  27. treeview.setAdapter(treeViewAdapter);
  28. treeview.setOnItemClickListener(treeViewItemClickListener);
  29. }
  30. private void init() {
  31. elements = new ArrayList<Element>();
  32. elementsData = new ArrayList<Element>();
  33. //添加节点  -- 节点名称,节点level,节点id,父节点id,是否有子节点,是否展开
  34. //添加最外层节点
  35. Element e1 = new Element("山东省", Element.TOP_LEVEL, 0, Element.NO_PARENT, true, false);
  36. //添加第一层节点
  37. Element e2 = new Element("青岛市", Element.TOP_LEVEL + 1, 1, e1.getId(), true, false);
  38. //添加第二层节点
  39. Element e3 = new Element("市南区", Element.TOP_LEVEL + 2, 2, e2.getId(), true, false);
  40. //添加第三层节点
  41. Element e4 = new Element("香港中路", Element.TOP_LEVEL + 3, 3, e3.getId(), false, false);
  42. //添加第一层节点
  43. Element e5 = new Element("烟台市", Element.TOP_LEVEL + 1, 4, e1.getId(), true, false);
  44. //添加第二层节点
  45. Element e6 = new Element("芝罘区", Element.TOP_LEVEL + 2, 5, e5.getId(), true, false);
  46. //添加第三层节点
  47. Element e7 = new Element("凤凰台街道", Element.TOP_LEVEL + 3, 6, e6.getId(), false, false);
  48. //添加第一层节点
  49. Element e8 = new Element("威海市", Element.TOP_LEVEL + 1, 7, e1.getId(), false, false);
  50. //添加最外层节点
  51. Element e9 = new Element("广东省", Element.TOP_LEVEL, 8, Element.NO_PARENT, true, false);
  52. //添加第一层节点
  53. Element e10 = new Element("深圳市", Element.TOP_LEVEL + 1, 9, e9.getId(), true, false);
  54. //添加第二层节点
  55. Element e11 = new Element("南山区", Element.TOP_LEVEL + 2, 10, e10.getId(), true, false);
  56. //添加第三层节点
  57. Element e12 = new Element("深南大道", Element.TOP_LEVEL + 3, 11, e11.getId(), true, false);
  58. //添加第四层节点
  59. Element e13 = new Element("10000号", Element.TOP_LEVEL + 4, 12, e12.getId(), false, false);
  60. //添加初始树元素
  61. elements.add(e1);
  62. elements.add(e9);
  63. //创建数据源
  64. elementsData.add(e1);
  65. elementsData.add(e2);
  66. elementsData.add(e3);
  67. elementsData.add(e4);
  68. elementsData.add(e5);
  69. elementsData.add(e6);
  70. elementsData.add(e7);
  71. elementsData.add(e8);
  72. elementsData.add(e9);
  73. elementsData.add(e10);
  74. elementsData.add(e11);
  75. elementsData.add(e12);
  76. elementsData.add(e13);
  77. }
  78. @Override
  79. public boolean onCreateOptionsMenu(Menu menu) {
  80. // Inflate the menu; this adds items to the action bar if it is present.
  81. getMenuInflater().inflate(R.menu.activity_main, menu);
  82. return true;
  83. }
  84. }

treeview_item.xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <ImageView
  6. android:id="@+id/disclosureImg"
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:layout_centerVertical="true"
  10. android:layout_alignParentLeft="true"/>
  11. <TextView
  12. android:id="@+id/contentText"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:layout_centerVertical="true"
  16. android:layout_toRightOf="@id/disclosureImg"/>
  17. </RelativeLayout>

activity_main.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. tools:context=".MainActivity" >
  6. <ListView
  7. android:id="@+id/treeview"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"/>
  10. </RelativeLayout>

转载请注明:http://blog.csdn.net/carrey1989/article/details/10227165

源码下载

Android UI 之实现多级列表TreeView的更多相关文章

  1. Android UI组件----ListView列表控件详解

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...

  2. Android UI(五)云通讯录项目之联系人列表,带侧滑选择,带搜索框

    作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节.交流QQ群:[编程之美 365234583]h ...

  3. 基于bootstrap的jQuery多级列表树插件 treeview

    http://www.cnblogs.com/mfc-itblog/p/5233453.html http://www.htmleaf.com/jQuery/Menu-Navigation/20150 ...

  4. 基于bootstrup treeview多级列表树插件

    <!doctype html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...

  5. GitHub上受欢迎的Android UI Library

    GitHub上受欢迎的Android UI Library 内容 抽屉菜单 ListView WebView SwitchButton 按钮 点赞按钮 进度条 TabLayout 图标 下拉刷新 Vi ...

  6. Android UI相关开源项目库汇总

    最近做了一个Android UI相关开源项目库汇总,里面集合了OpenDigg 上的优质的Android开源项目库,方便移动开发人员便捷的找到自己需要的项目工具等,感兴趣的可以到GitHub上给个st ...

  7. Android UI目录

    Android UI目录 序:最近一直想进阶android应用开发,虽然对一些相关的android知识都大体熟悉,但是自己的android知识体系,经不起推敲.经不起高手的垂问.经过几个月的努力学习, ...

  8. Android UI 绘制过程浅析(五)自定义View

    前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View&gt ...

  9. 免费的Android UI库及组件推荐

    短短数年时间Android平台就已经形成了一个庞大而活跃的开发者社区.许多社区开发的项目业已进入成熟阶段,甚至可以用于商业的软件生产中,且不用担心质量问题. 本文编译自androiduipattern ...

随机推荐

  1. CSS Sprite雪碧图应用

    在写网页过程中,会遇到这种需要使用多个小图标: 如上图中的「女装」文字左边的图标.容易想到的解决方法是为每张图片加入<img>标签,但这样做会增加HTTP请求数量,影响网站加载速度.比这更 ...

  2. fiddler实现手机端抓包(代理)

    一.fiddler设置代理 1.打开Fiddler->Tools->Fiddler Options在Connection面板里将 Allow remote computers to con ...

  3. python 中的&lbrack;&colon;&colon;-1&rsqb;

    for value in rang(10)涉及的数字倒序输出: for value in rang(10)[::-1]涉及的数字倒序输出: 一.反转 二.详解 这个是python的slice nota ...

  4. DES跨&lpar;C&num; Android IOS&rpar;三个平台通用的加解密方法

          #region   跨平台加解密(c# 安卓 IOS)       //  public static string sKey = "12345678";       ...

  5. 转:SQL的内连接与外连接

    参考:http://www.cuiyongjian.com/post-130.html 在oracle的SQL语句常用的连接有内连接(inner join),外连接(outer join)等,内连接又 ...

  6. qt 5 小练习 纯代码制作自定义按钮

    大家都知道QT设计师中直接拖动的按钮是长方形带有圆角的图案,那我们如何来设置自定义按钮呢 要设计一个按钮,我们必须要知道按钮有什么属性,首先,按钮必须有一个位置 第二,按钮必须有一个名称.还有当我们点 ...

  7. 怎样用Google APIs和Google的应用系统进行集成&lpar;1&rpar;----Google APIs简介

    Google的应用系统提供了非常多的应用,比方 Google广告.Google 任务,Google 日历.Google blogger,Google Plus,Google 地图等等非常的多的应用,请 ...

  8. PHP 动态调整内存限制

    最近公司的一个PHP项目在操作大文件的时候总是抛出这个异常 Fixing PHP Fatal Error: Allowed Memory Size Exhausted 经过一番调试后发现是达到了PHP ...

  9. Python中subplots&lowbar;adjust函数的说明

    使用subplots_adjust一般会传入6个参数,我们分别用A,B,C,D,E,F表示.然后我们对图框建立坐标系,将坐标轴原点定在左下角点,并将整个图框归一化,即横纵坐标都是0到1之间.从下图中可 ...

  10. 获取高精度时间注意事项 &lpar;QueryPerformanceCounter &comma; QueryPerformanceFrequency&rpar;

    花了很长时间才得到的经验,与大家分享. 1. RDTSC - 粒度: 纳秒级 不推荐优势: 几乎是能够获得最细粒度的计数器抛弃理由: A) 定义模糊- 曾经据说是处理器的cycle counter,但 ...