Android 使用listview实现树形结构
来源:互联网 发布:三种网络分层结构 编辑:程序博客网 时间:2024/06/18 06:51
一、概述:
1、效果图:
2、实现的功能:
1)缩进的树形结构
2)点击箭头可以展开与关闭
3)可以是任意层级的树
3、使用的技术:
1)子父节点关联
2)在listview树结构里的onItemClick实现函数回调OnTreeNodeClickListener
if (onTreeNodeClickListener != null) {
onTreeNodeClickListener.onClick(mNodes.get(position), position);
}
3)对节点排序:
this.mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel);
4)对节点的过滤
public static List<Node> filterVisibleNode(List<Node> mAllNodes) { List<Node> result = new ArrayList<Node>(); for (Node node : mAllNodes) { // 根节点是必须可见的,如果父亲节点是展开的话,这个节点当然是展开的 if (node.isRoot() || node.isParentExptend()) { setIcon(node); result.add(node); } } return result; }
5)使用注解把数据转化为节点
protected static <T> List<Node> convetData2Node(List<T> datas) throws IllegalArgumentException, IllegalAccessException { List<Node> nodes = new ArrayList<Node>(); for (T t : datas) { int id = -1; int pId = -1; String lable = null; // 使用反射的方法获得类的名称 Class<? extends Object> clazz = t.getClass(); // 根据类得到声明的字段 Field[] fields = clazz.getDeclaredFields(); // 遍历所有的字段 for (Field field : fields) { // 如果字段里有注解,说明得到的字段就存在 if (field.getAnnotation(TreeNodeId.class) != null) { field.setAccessible(true); 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); lable = (String) field.get(t); } //如果都遍历了,就不需要再次遍历了 if (id != -1 && pId != -1 && lable != null) { break; } } Node node = new Node(id, pId, lable); nodes.add(node); } /** * 使用选着排序,比较两个节点的关系 */ for (int i = 0; i < nodes.size(); i++) { Node node1 = nodes.get(i); // 从i+1处开始比较,使用了选择排序法 for (int j = i + 1; j < nodes.size(); j++) { Node node2 = nodes.get(j); // 说明,node2是node1的父类 if (node1.getpId() == node2.getId()) { node2.getChildren().add(node1); node1.setParent(node2); // node1是node2的父类 } else if (node2.getpId() == node1.getId()) { node1.getChildren().add(node2); node2.setParent(node1); } } } /** * 设置图片 */ for (Node node : nodes) { setIcon(node); } return nodes; }
二、框架搭建:
1、创建实体类:
/** * 创建文件实体类 * * @Project App_View * @Package com.android.view.tree * @author chenlin * @version 1.0 * @Date 2014年6月4日 */public class FileBean { @TreeNodeId private int _id; @TreeNodePid private int parentId;//父节点id @TreeNodeLabel private String name;//文件名称 private long length;//文件长度 private String desc;//文件描述 public FileBean() { } public FileBean(int _id, int parentId, String name) { super(); this._id = _id; this.parentId = parentId; this.name = name; } public int get_id() { return _id; } public void set_id(int _id) { this._id = _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getLength() { return length; } public void setLength(long length) { this.length = length; } public int getParentId() { return parentId; } public void setParentId(int parentId) { this.parentId = parentId; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; }}
2、创建节点类:
/** * 节点 * @Project App_View * @Package com.android.view.tree * @author chenlin * @version 1.0 * @Date 2013年6月4日 * @Note TODO */public class Node { private int id; private int pId = 0;//父节点,根节点是0 private int level;//级别 private String name;//节点名称 private int icon;//小图标 private Node parent;//父节点 //子节点 private List<Node> children = new ArrayList<Node>(); private boolean isExpend = false;//是否展开 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 void setLevel(int level) { this.level = level; } public String getName() { return name; } public void setName(String name) { this.name = name; } 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; } public boolean isExpend() { return isExpend; } /** * 设置展开时不但要展开自己,也要展开所有的子节点 * @param isExpend */ public void setExpend(boolean isExpend) { this.isExpend = isExpend; if (!isExpend) { for(Node node : children){ node.setExpend(isExpend); } } } /** * 获得级别 * @return */ public int getLevel() { return parent == null ? 0 : parent.getLevel() + 1; } /** * 判断是否是叶子节点 * @return */ public boolean isLeaf(){ return children.size() == 0; } /** * 判断是否是根节点 * @return */ public boolean isRoot(){ return parent == null; } /** * 判断父节点是否打开 * @return */ public boolean isParentExptend(){ if (parent == null) { return false; } return parent.isExpend(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((children == null) ? 0 : children.hashCode()); result = prime * result + icon; result = prime * result + id; result = prime * result + (isExpend ? 1231 : 1237); result = prime * result + level; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + pId; result = prime * result + ((parent == null) ? 0 : parent.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Node other = (Node) obj; if (children == null) { if (other.children != null) return false; } else if (!children.equals(other.children)) return false; if (icon != other.icon) return false; if (id != other.id) return false; if (isExpend != other.isExpend) return false; if (level != other.level) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (pId != other.pId) return false; if (parent == null) { if (other.parent != null) return false; } else if (!parent.equals(other.parent)) return false; return true; }}
3、主页面
/** * 主页 * * @Project App_View * @Package com.android.view.tree * @author chenlin * @version 1.0 * @Date 2015年6月4日 */public class TreeActivity extends Activity { private List<FileBean> mDatas = new ArrayList<FileBean>(); private ListView mListView; @SuppressWarnings("rawtypes") private TreeListViewAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tree); initDatas(); init(); } private void init() { try { mListView = (ListView) findViewById(R.id.lv_tree); mAdapter = new SimpleTreeAdapter<FileBean>(mListView, this, mDatas, 10); mListView.setAdapter(mAdapter); mAdapter.setOnTreeNodeClickListener(new OnTreeNodeClickListener() { @Override public void onClick(Node node, int position) { Toast.makeText(TreeActivity.this, node.getName(), Toast.LENGTH_LONG).show(); } }); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private void initDatas() { // id , pid , label , 其他属性 mDatas.add(new FileBean(1, 0, "文件管理系统")); mDatas.add(new FileBean(2, 1, "游戏")); mDatas.add(new FileBean(3, 1, "文档")); mDatas.add(new FileBean(4, 1, "程序")); mDatas.add(new FileBean(5, 2, "war3")); mDatas.add(new FileBean(6, 2, "刀塔传奇")); mDatas.add(new FileBean(7, 4, "面向对象")); mDatas.add(new FileBean(8, 4, "非面向对象")); mDatas.add(new FileBean(9, 7, "C++")); mDatas.add(new FileBean(10, 7, "JAVA")); mDatas.add(new FileBean(11, 7, "Javascript")); mDatas.add(new FileBean(12, 8, "C")); }}
4、布局
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@+id/lv_tree" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="#aaa" android:dividerHeight="1px" > </ListView></LinearLayout>
三、实现适配器
1、添加基本适配器
/** * 树形结构适配器 * * @Project App_View * @Package com.android.view.tree * @author chenlin * @version 1.0 * @Date 2014年6月4日 * @Note TODO */public abstract class TreeListViewAdapter<T> extends BaseAdapter { protected ListView mListView; protected Context mContext; /** 存储所有可见的Node */ protected List<Node> mNodes; /** 存储所有的Node */ protected List<Node> mAllNodes; protected LayoutInflater mInflater; /********************************************************************************* * 点击的回调接口 */ private OnTreeNodeClickListener onTreeNodeClickListener; public interface OnTreeNodeClickListener { void onClick(Node node, int position); } public void setOnTreeNodeClickListener(OnTreeNodeClickListener onTreeNodeClickListener) { this.onTreeNodeClickListener = onTreeNodeClickListener; } /************************************************************************************/ public TreeListViewAdapter(ListView mTree, Context context, List<T> datas, int defaultExpandLevel) throws IllegalArgumentException, IllegalAccessException { this.mContext = context; this.mInflater = LayoutInflater.from(context); // 对所有的Node进行排序 this.mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel); // 过滤出可见的Node this.mNodes = TreeHelper.filterVisibleNode(mAllNodes); // 点击item事件 mTree.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // 点击时展开或关闭item expandOrCollapse(position); //使用回调函数 if (onTreeNodeClickListener != null) { onTreeNodeClickListener.onClick(mNodes.get(position), position); } } }); } /** * 点击时展开或关闭item * @param position */ protected void expandOrCollapse(int position) { Node node = mNodes.get(position); if (node!= null) { if (!node.isLeaf()) { //表示如果关闭的就打开,如果打开的就关闭 node.setExpend(!node.isExpend()); //重新赋值 mNodes = TreeHelper.filterVisibleNode(mAllNodes); //通知视图改变了 notifyDataSetChanged(); } } } @Override public int getCount() { return mNodes.size(); } @Override public Object getItem(int position) { return mNodes.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Node node = mNodes.get(position); convertView = getConvertView(node, position, convertView, parent); // 设置内边距, 层级越大,则离左边的距离越大 convertView.setPadding(node.getLevel() * 30, 3, 3, 3); return convertView; } public abstract View getConvertView(Node node, int position, View convertView, ViewGroup parent);}
2、适配器实现类
/** * 简单适配器 * * @Project App_View * @Package com.android.view.tree * @author chenlin * @version 1.0 * @Date 2014年6月4日 * @param <T> */public class SimpleTreeAdapter<T> extends TreeListViewAdapter<T> { public SimpleTreeAdapter(ListView mTree, Context context, List<T> datas, int defaultExpandLevel) throws IllegalArgumentException, IllegalAccessException { super(mTree, context, datas, defaultExpandLevel); } @Override public View getConvertView(Node node, int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.list_tree_item, parent, false); } ViewHolder viewHolder = ViewHolder.getHolder(convertView); // 如果图标不存在,就隐藏 if (node.getIcon() == -1) { viewHolder.icon.setVisibility(View.INVISIBLE); } else { viewHolder.icon.setVisibility(View.VISIBLE); viewHolder.icon.setImageResource(node.getIcon()); } viewHolder.label.setText(node.getName()); return convertView; } static class ViewHolder { ImageView icon; TextView label; public static ViewHolder getHolder(View view) { Object tag = view.getTag(); if (tag != null) { return (ViewHolder) tag; } else { ViewHolder viewHolder = new ViewHolder(); viewHolder.icon = (ImageView) view.findViewById(R.id.id_treenode_icon); viewHolder.label = (TextView) view.findViewById(R.id.id_treenode_label); view.setTag(viewHolder); return viewHolder; } } }}
四、树形结构实现
1、帮助类
/** * 树结构帮助类 * * @Project App_View * @Package com.android.view.tree * @author chenlin * @version 1.0 * @Date 2014年6月4日 * @Note TODO */public class TreeHelper { private static final String TAG = "ture"; /** * 得到排好序的节点 * * @param datas * @param defaultExpandLevel * @return * @throws IllegalArgumentException * @throws IllegalAccessException */ public static <T> List<Node> getSortedNodes(List<T> datas, int defaultExpandLevel) throws IllegalArgumentException, IllegalAccessException { List<Node> result = new ArrayList<Node>(); // 1.将用户数据转化为List<Node>以及设置Node间关系 List<Node> nodes = convetData2Node(datas); // 2.拿到跟节点 List<Node> rootNodes = getRootNodes(nodes); // 3.依次展开排序把字节点添加到根节点 for (Node node : rootNodes) { addNode(result, node, defaultExpandLevel, 1); } return result; } /** * 把数据转化为节点数据 * * @param datas * @return * @throws IllegalArgumentException * @throws IllegalAccessException */ protected static <T> List<Node> convetData2Node(List<T> datas) throws IllegalArgumentException, IllegalAccessException { List<Node> nodes = new ArrayList<Node>(); for (T t : datas) { int id = -1; int pId = -1; String lable = null; // 使用反射的方法获得类的名称 Class<? extends Object> clazz = t.getClass(); // 根据类得到声明的字段 Field[] fields = clazz.getDeclaredFields(); // 遍历所有的字段 for (Field field : fields) { // 如果字段里有注解,说明得到的字段就存在 if (field.getAnnotation(TreeNodeId.class) != null) { field.setAccessible(true); 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); lable = (String) field.get(t); } //如果都遍历了,就不需要再次遍历了 if (id != -1 && pId != -1 && lable != null) { break; } } Node node = new Node(id, pId, lable); nodes.add(node); } /** * 使用选着排序,比较两个节点的关系 */ for (int i = 0; i < nodes.size(); i++) { Node node1 = nodes.get(i); // 从i+1处开始比较,使用了选择排序法 for (int j = i + 1; j < nodes.size(); j++) { Node node2 = nodes.get(j); // 说明,node2是node1的父类 if (node1.getpId() == node2.getId()) { node2.getChildren().add(node1); node1.setParent(node2); // node1是node2的父类 } else if (node2.getpId() == node1.getId()) { node1.getChildren().add(node2); node2.setParent(node1); } } } /** * 设置图片 */ for (Node node : nodes) { setIcon(node); } return nodes; } /** * 最主要根据节点是否有字节点和是否展开来判断显示什么样的图标 如果子节点是展开的,用- 否则有+ * * @param node */ private static void setIcon(Node node) { Logger.i(TAG, "node.isExpend() == " + node.isExpend()); Logger.i(TAG, "node.getChildren().size() == " + node.getChildren().size()); if (node.getChildren().size() > 0 && node.isExpend()) { node.setIcon(R.drawable.tree_ex); } else if (node.getChildren().size() > 0 && !node.isExpend()) { node.setIcon(R.drawable.tree_ec); } else { // 设置为-1时会在适配器里判断,如果为-1就隐藏 node.setIcon(-1); } } /** * 把一个节点上的所有的内容都挂上去 * * @param nodes * @param node * @param defaultExpandLevel * @param i */ protected static void addNode(List<Node> nodes, Node node, int defaultExpandLevel, int currentLevel) { //添加到集合里 nodes.add(node); // 如果传进来的<currentLevel,说明在下一级,展开 if (defaultExpandLevel >= currentLevel) { node.setExpend(true); } if (node.isLeaf()) { return; } // 使用递归,展开所有的子node for (int i = 0; i < node.getChildren().size(); i++) { addNode(nodes, node.getChildren().get(i), defaultExpandLevel, currentLevel + 1); } } /** * 判断是否是根节点,只要判断是否是isRoot(); * * @param nodes * @return */ protected static List<Node> getRootNodes(List<Node> nodes) { List<Node> result = new ArrayList<Node>(); for (Node node : nodes) { if (node.isRoot()) { result.add(node); } } return result; } /** * 过滤出所有可见的Node * * @param mAllNodes * @return */ public static List<Node> filterVisibleNode(List<Node> mAllNodes) { List<Node> result = new ArrayList<Node>(); for (Node node : mAllNodes) { // 根节点是必须可见的,如果父亲节点是展开的话,这个节点当然是展开的 if (node.isRoot() || node.isParentExptend()) { setIcon(node); result.add(node); } } return result; }}
二、几个注解文件
@Target({ ElementType.FIELD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface TreeNodeId {}
@Target({ ElementType.FIELD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface TreeNodeLabel {}
@Target({ ElementType.FIELD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface TreeNodePid {}
五、源码下载:
链接:http://pan.baidu.com/s/1hsfiABM 密码:goev
0 0
- Android 使用listview实现树形结构
- ListView 实现树形层级结构
- android 用ListVew实现树形结构菜单
- Android - N级树形结构实现
- android listview实现树形菜单及进行选择操作
- Android学习笔记之:实现树形层级ListView
- 树形结构的实现
- 树形结构的实现
- ajax实现树形结构
- JSP实现树形结构
- J2ME树形结构实现
- Java 实现树形结构
- 实现GridView树形结构
- JavaSwing实现树形结构
- JavaSwing实现树形结构
- 树形结构的实现
- 手动实现树形结构
- 树形结构递归实现
- Gradle Build速度加快终极方法
- 【转帖】PySpider HTTP 599: SSL certificate problem错误的解决方法
- 成为Java高手的25个学习要点
- Visual Studio的SDK
- Apache Commons工具集简介
- Android 使用listview实现树形结构
- Android Framework的启动方法及原理详解
- 各种Hadoop软件集成包 其它Apache项目
- jQuery中的事件绑定bind(), live(), on(), delegate()
- 利用python进行数据分析-pandas入门3
- μCOS-II系统之时间管理函数OSTimeDlyHMSM()
- T--Linux SSH免密码登录
- 设置或获取元素的内容
- 【Android基础】动画