直接贴代码,代码中有相应注释:
主界面Activity,布局就只一个ListView:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
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); } } |
数据适配器基类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
** * 树形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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
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(); } } |
数据处理的工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
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; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
/** * 获取数据中的所有根节点数据 * @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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
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; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
/** * 当前节点所在的层级 * @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; } } } |
用到的注解类:
1
2
3
4
|
@Target (ElementType.FIELD) //定义注解的作用目标:字段、枚举的常量 @Retention (RetentionPolicy.RUNTIME) //注解策略: 注解会在class字节码文件中存在,在运行时可以通过反射获取到 public @interface TreeNodeId { } |
1
2
3
4
5
|
@Target (ElementType.FIELD) //定义注解的作用目标:字段、枚举的常量 @Retention (RetentionPolicy.RUNTIME) //注解策略: 注解会在class字节码文件中存在,在运行时可以通过反射获取到 public @interface TreeNodeLabel { } `` |
1
2
3
4
|
@Target (ElementType.FIELD) //定义注解的作用目标:字段、枚举的常量 @Retention (RetentionPolicy.RUNTIME) //注解策略: 注解会在class字节码文件中存在,在运行时可以通过反射获取到 public @interface TreeNodePid { } |
以上就是Android实现树形层级ListView的相关代码,希望对大家的学习有所帮助。