Android creates random-level tree controls to test your data structure and design

Source: Internet
Author: User

Reprint please indicate the source: http://blog.csdn.net/lmj623565791/article/details/40212367, this article from: "Zhang Hongyang Blog" 1, overview

You may see more or less in the project, and occasionally projects need to display a tree-shaped control on the app, such as showing an organization, the top of which is the boss. Then various departments. Various small boss, finally various small ro-ro. The overall is a tree-shaped structure. In this case, we may go back to Baidu. Because of the level of many, may be more easy to think of Expandablelistview, because this thing hierarchy than the ListView more. But Expandablelistview implementation of the current only support level two, of course, some people have changed to multi-level, but from my personal point of view, first I do not like Expandablelistview, the data set is more complex organization.

So let's use the ListView today to create a tree-shaped display. The ListView should be the only control that you are familiar with, and the data set is a list<t>.

This blog goal to achieve, only if the tree-like structure of the data can be easily through our code, to achieve a tree effect, how easy, the end of the text will know ~ ~

Well, since we're going to show the tree structure. Then the data must be a tree-shaped dependency. Other words. Each of your records has at least one field pointing to its parent node, similar (ID, pId, others ...). )

2. Principle Analysis

Let's take a look at our:


We support the arbitrary level, including the item layout still let the user to control, our demo's item layout is very easy. An icon + Text ~ ~

The principle is that the tree is not tree-shaped. In fact, is not multiple indentation, just to be able to infer that each item belongs to the first layer of the tree (the term appears to be called height), set the appropriate indentation can be.

Of course. The principle is simple to say, but also to control the relationship between each layer, to join the unwinding and so on, as well as the indentation to be able to display in the correct position, but it doesn't matter, I will take everyone step by step to achieve.

3. How to use

As a result of the overall longer, I decided to first take a look at the use of methods, is to assume that the study of this blog. We need the tree control, we need to spend a lot of energy to finish ~ ~

Now demand comes: I need to present a tree structure of a file management system:

The data is this:

ID, PID, label, other properties Mdatas.add (new Filebean (1, 0, "File Management System")), Mdatas.add (New Filebean (2, 1, "game")); Mdatas.add (new file Bean (3, 1, "document")), Mdatas.add (New Filebean (4, 1, "program")), Mdatas.add (New Filebean (5, 2, "War3")), Mdatas.add (New Filebean (6 , 2, "turret legend"), Mdatas.add (New Filebean (7, 4, "Object Oriented")), Mdatas.add (New Filebean (8, 4, "Non-object Oriented")), Mdatas.add (New Filebean (9, 7, "C + +")), Mdatas.add (New Filebean (7, "JAVA")), Mdatas.add (New Filebean (one, 7, "Javascript")), Mdatas.add (new Filebean (8, "C"));

Of course, beans can have very many properties, we provide you with dynamic settings on the tree node display, as well as the unconstrained ID, PID naming. You can afford to be a random and insane attribute name;

So how can we be sure?

Look at the bean:

Package Com.zhy.bean;import Com.zhy.tree.bean.treenodeid;import Com.zhy.tree.bean.treenodelabel;import Com.zhy.tree.bean.treenodepid;public class filebean{@TreeNodeIdprivate int _id; @TreeNodePidprivate int parentid;@ Treenodelabelprivate string name;private Long length;private string desc;public filebean (int _id, int parentid, String nam e) {super (); this._id = _id;this.parentid = Parentid;this.name = name;}}

Now, needless to say. It should also be known that we use annotations to determine.

Here's how we turn this data into a tree

The layout file is a ListView. It's subsidized, just looking at the activity.

Package Com.zhy.tree_view;import Java.util.arraylist;import Java.util.list;import android.app.activity;import Android.os.bundle;import Android.widget.listview;import Com.zhy.bean.filebean;import Com.zhy.tree.bean.treelistviewadapter;public class Mainactivity extends Activity{private list<filebean> MDatas = new Arraylist<filebean> ();p rivate ListView mtree;private treelistviewadapter madapter; @Overrideprotected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (R.layout.activity_main); Initdatas (); mTree = (ListView) Findviewbyid (r.id.id_tree); try{madapter = new Simpletreeadapter<filebean> (mTree , this, Mdatas, ten); Mtree.setadapter (Madapter);} catch (Illegalaccessexception e) {e.printstacktrace ();}} private void Initdatas () {//ID, PID, label, other attribute Mdatas.add (new Filebean (1, 0, "File Management System")), Mdatas.add (New Filebean (2, 1, "Game")); Mdatas.add (New Filebean (3, 1, "Documentation")), Mdatas.add (New Filebean (4, 1, "program")), Mdatas.add (New Filebean (5, 2, "War3")); Mdatas.add (New Filebean (6, 2, "turret Legend")), Mdatas.add (New Filebean (7, 4, "Object Oriented")), Mdatas.add (New Filebean (8, 4, "Non-object Oriented")) ; Mdatas.add (New Filebean (9, 7, "C + +")), Mdatas.add (New Filebean (7, "JAVA")), Mdatas.add (New Filebean (11, 7, " Javascript ")); Mdatas.add (New Filebean (8," C "));}}

There is no special code in the activity. Get the ListView. Incoming Mdata, in which a adapter is initialized;

It seems that our core code is in our adapter:

Then take a look at our adapter.

Package Com.zhy.tree_view;import Java.util.list;import Android.content.context;import android.view.View;import Android.view.viewgroup;import Android.widget.imageview;import Android.widget.listview;import Android.widget.textview;import Com.zhy.tree.bean.node;import Com.zhy.tree.bean.treelistviewadapter;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);} @Overridepublic View Getconvertview (node node, int position, view Convertview, ViewGroup parent) {Viewholder Viewholder = Null;if (Convertview = = null) {Convertview = Minflater.inflate (R.layout.list_item, parent, false); Viewholder = new Viewholder (); Viewholder.icon = (ImageView) Convertview.findviewbyid (r.id.id_treenode_icon); Viewholder.label = ( TextView) Convertview.findviewbyid (R.id.id_treenode_label); convertView.settag (Viewholder);} Else{viewholder = (Viewholder) Convertview.gettag ();} 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;} Private final class Viewholder{imageview icon; TextView label;}}

Our simpletreeadapter inherit our treelistviewadapter, and in addition, the code only needs to be getconvertview. and Getconvetview in fact and our usual getview written in accordance with the wording;

The purpose of publishing getconvertview is to let the user decide the effect of the item. The other code, I have packaged into a jar, use the time to import.

So we're finished with our tree control.

In other words, with our tree-shaped controls, we simply need to change the traditional inheritance baseadapter to our treelistviewadapter. And then to realize the getconvertview just fine.

So now the effect is:


The default is all turned on, as we also support dynamically setting the open hierarchy. Used by the user.

Use it is not very casual, add a few annotations, the ListView Adapater change class inherit under ~ ~ OK. The following starts to bring everyone together from scratch realization ~

4. Realize
1. Ideas

Our train of thought is this, when we show. Requires very many attributes, we need to know whether the current node is the parent node, the current level. His child nodes and so on. But the user's data set is not fixed, at most can only give a similar ID. PId this property. In other words, the user-given bean is not suitable for us to control the display, so we are prepared to do this:

1. Extract the necessary element IDs, PId, and the displayed text (through Annotations + reflections) in the user's bean, then assemble the node that we really displayed. List<bean>, list<node>

2, not all of the node is displayed. For example, the parent node of some nodes is off state, we need to filter, that is, list<node> filter list<node>

3, when the display, for example, click on the parent node, its child nodes will be followed by the display, we are a list inside, that is, the list of the order is very critical; Of course we can put it as step one;

Finally, the filtered node will be displayed, and the left padding will be set.

Said so much. First take a look at our encapsulated node.

2. Node

Package Com.zhy.tree.bean;import Java.util.arraylist;import Java.util.list;import org.w3c.dom.namednodemap;import Android.util.log;public class Node{private int id;/** * Root node pid is 0 */private int pid = 0;private String name;/** * Current level */p  rivate int level;/** * Whether to expand */private boolean isexpand = false;private int icon;/** * Next level child Node */private list<node> Children = new arraylist<node> ();/** * Parent Node */private node parent;public node () {}public node (int id, int pId, stri ng name) {super (); this.id = Id;this.pid = Pid;this.name = name;} public int GetIcon () {return icon;} public void SetIcon (int icon) {This.icon = icon;} 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;} public void SetLevel (Int. level) {this.level = level;} public Boolean Isexpand () {return isexpand;} Public list<node> GetChildren () {return CHildren;} public void Setchildren (list<node> children) {This.children = children;} Public Node getParent () {return parent;} public void SetParent (Node parent) {this.parent = parent;} /** * Whether to follow node * * @return */public boolean isRoot () {return parent = = NULL;} /** * Infers whether the parent node expands * * @return */public boolean isparentexpand () {if (parent = = NULL) return False;return Parent.isexpand ();} /** * is the leaf field point * * @return */public boolean isleaf () {return children.size () = = 0; /** * Get level */public int getlevel () {return parent = = NULL?

0:parent.getlevel () + 1;} /** * Settings Expand * * @param isexpand */public void Setexpand (Boolean isexpand) {This.isexpand = Isexpand;if (!isexpand) {for (Nod E Node:children) {Node.setexpand (Isexpand);}}}


Includes some common properties of tree nodes, some common methods, and for Getlevel,setexpand these methods. Everyone can take a good look ~

With node, just in the use of the method, we adapter inherited the superclass: Treelistviewadapter, the core code is inside. We are going to look into the end:

3, Treelistviewadapter

The code is not very long. Direct and complete placement:

Package Com.zhy.tree.bean;import Java.util.list;import Android.content.context;import android.view.LayoutInflater; Import Android.view.view;import Android.view.viewgroup;import Android.widget.adapterview;import Android.widget.adapterview.onitemclicklistener;import Android.widget.baseadapter;import Android.widget.ListView; Public abstract class Treelistviewadapter<t> extends baseadapter{protected Context mcontext;/** * Store all visible node */ Protected list<node> mnodes;protected layoutinflater minflater;/** * Store all Node */protected list<node> mallnodes;/** * Click on the callback interface */private Ontreenodeclicklistener Ontreenodeclicklistener;public interface Ontreenodeclicklistener{void OnClick (node node, int position);} public void Setontreenodeclicklistener (Ontreenodeclicklistener ontreenodeclicklistener) { This.ontreenodeclicklistener = Ontreenodeclicklistener;} /** * * @param mTree * @param context * @param datas * @param defaultexpandlevel * Default to expand the few trees * @throws illegalar Gumentexception *@throws illegalaccessexception */public treelistviewadapter (ListView mTree, context context, list<t> Datas,int Defaultexpandlevel) Throws Illegalargumentexception,illegalaccessexception{mcontext = context;/** * To sort all node */ Mallnodes = Treehelper.getsortednodes (datas, defaultexpandlevel);/** * Filters out visible Node */mnodes = Treehelper.filtervisiblenode (mallnodes); minflater = Layoutinflater.from (context);/** * Set the node to expand and close when clicked. And the ItemClick event continues to be released */mtree.setonitemclicklistener (new Onitemclicklistener () {@Overridepublic void Onitemclick ( adapterview<?

> Parent, View view,int position, long id) {expandorcollapse (position); if (ontreenodeclicklistener! = null) { Ontreenodeclicklistener.onclick (Mnodes.get (position), position);}});} /** * Click event for corresponding ListView to expand or close a node * * @param position */public void expandorcollapse (int position) {Node n = mnodes.get (posit ion); if (n! = null)//Exclude incoming argument error exception {if (!n.isleaf ()) {N.setexpand (!n.isexpand ()); mnodes = Treehelper.filtervisiblenode ( Mallnodes); notifydatasetchanged ();//Refresh View}}} @Overridepublic int GetCount () {return mnodes.size ();} @Overridepublic Object getItem (int position) {return mnodes.get (position);} @Overridepublic long Getitemid (int position) {return position;} @Overridepublic view GetView (int position, view Convertview, ViewGroup Parent) {Node node = mnodes.get (position); Convertview = Getconvertview (node, position, Convertview, parent);//Set Inner margin convertview.setpadding (node.getlevel () * 30, 3, 3, 3); return Convertview;} Public abstract View Getconvertview (node node, int position,view convertview, viewgroup parENT);}


First our class inherits from Baseadapter, and then our corresponding data sets are filtered out of the visible node.

Our construction method receives 4 parameters by default: Listview,context,mdatas, and the number of expansions by default: 0 shows only the root node;

Can be seen in the construction method: The user's incoming data sets are sorted, and filtered operations. Looking at these methods later, we used a treehelper to encapsulate them.

Note: Suppose you think your item layout is very complex. And the layout will show other data for the bean. So, for convenience, you can have node include a generic t, and each node carries all the data for the bean with which it is associated.

Can see that we also set a click event directly for item. Because of our tree, the default is to click on the parent node to expand and close, but in order for the user to still be available click-to-listen, we define a click callback for the user to use.

The Expandorcollapse method is called by default when the user taps. Resets the nodes of course to the expand Flag, and then filters out the visible node again. The last notifydatasetchanged is possible;

Other methods are baseadapter default methods.

Below we look at some of the methods in Treehelper:

4, Treehelper

First look at the two methods used in the Treelistviewadapter construction method:

/** * into our regular bean, converted to our sorted node * @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> ();//convert user data to list<node> and set node relationship list<node> nodes = Convetdata2node (datas);//Get root node list <Node> rootnodes = getrootnodes (nodes);//Sort for (node Node:rootnodes) {AddNode (result, Node, Defaultexpandlevel, 1 );} return result;}

Get incoming data from the user. Convert to list<node> and set the relationship between nodes, then the root node, from the root to traverse down to sort;

Next look: Filtervisiblenode

/** * filter out all visible node *  * @param nodes * @return */public static list<node> Filtervisiblenode (list<node> node s) {list<node> result = new arraylist<node> (); For (node node:nodes) {//is assumed to be a node, or the upper folder is an expanded state if (Node.isroot () || Node.isparentexpand ()) {Setnodeicon (node); Result.add (node);}} return result;}

The Code to filter node is very easy to traverse all nodes, only if the root node or parent node is the expanded state to join the return;

Finally look at some of the other private methods used by these two methods:

/** * Convert our data into nodes of the tree * * @param datas * @return * @throws nosuchfieldexception * @throws illegalaccessexception * @throws IllegalArgumentException */private static <T> list<node> convetdata2node (list<t> datas) throws IllegalArgumentException, illegalaccessexception{list<node> nodes = new arraylist<node> (); Node node = null;for (T t:datas) {int id = -1;int PId =-1; String label = NULL; class<? Extends object> clazz = T.getclass (); field[] Declaredfields = Clazz.getdeclaredfields (); for (Field f:declaredfields) {if (F.getannotation (treenodeid.class ) = null) {f.setaccessible (true); id = f.getint (t);} if (f.getannotation (treenodepid.class) = null) {f.setaccessible (true);p id = f.getint (t);} if (f.getannotation (treenodelabel.class) = null) {f.setaccessible (true); label = (String) f.get (t);} if (id! =-1 && pId! =-1 && label! = NULL) {break;}} node = new node (ID, pId, label); Nodes.Add (node);} /** * Set node, parent-child relationship, make every two nodes more than once. You can set the relationship */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 () {N.getchildren (). Add (M); M.setparent (n);} else if (m.getid () = = N.getpid ()) {M.getchildren (). Add (n); n.setparent (M);}}} Set picture for (Node n:nodes) {Setnodeicon (n);} return nodes;} 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;} /** * Put all the content on a node up */private static void AddNode (list<node> nodes, node node,int defaultexpandleval, int current Level) {Nodes.Add (node), if (Defaultexpandleval >= currentlevel) {Node.setexpand (true);} if (Node.isleaf ()) return;for (int i = 0; i < Node.getchildren (). Size (); i++) {AddNode (nodes, Node.getchildren (). get (i) , Defaultexpandleval,currentlevel + 1);}} /** * Set the icon for the node * * @param node */private static void Setnodeicon (node node) {if (Node.getchildren (). Size () > 0 && Node.isexpand ()) {Node.seticon (R.DRAWABLE.TREE_EX);} else if (Node.getchildren (). Size () > 0 &&! Node.isexpand ()) {Node.seticon (R.DRAWABLE.TREE_EC);} elsenode.seticon (-1);}

Convetdata2node is to traverse the user's incoming bean and convert it to node. Among the id,pid. The label is obtained by annotation plus reflection, and then the node relationship is set.

Getrootnodes this simple, get root node

AddNode: By recursive way, all the child nodes on a node are put in order;

Setnodeicon: Set icon, indicated here. Our jar also relies on two small icons. That is, two triangles. Suppose you think the tree does not need this icon, can be removed;


5. Class of annotations

The last one is our 3 annotated class, not sprinkled. The role of the boot to an identity

Treenodeid

Package Com.zhy.tree.bean;import Java.lang.annotation.elementtype;import Java.lang.annotation.retention;import Java.lang.annotation.retentionpolicy;import java.lang.annotation.Target; @Target (Elementtype.field) @Retention ( retentionpolicy.runtime) public @interface treenodeid{}

Treenodepid

Package Com.zhy.tree.bean;import Java.lang.annotation.elementtype;import Java.lang.annotation.retention;import Java.lang.annotation.retentionpolicy;import java.lang.annotation.Target; @Target (Elementtype.field) @Retention ( retentionpolicy.runtime) public @interface treenodepid{}
Treenodelabel

Package Com.zhy.tree.bean;import Java.lang.annotation.elementtype;import Java.lang.annotation.retention;import Java.lang.annotation.retentionpolicy;import java.lang.annotation.Target; @Target (Elementtype.field) @Retention ( retentionpolicy.runtime) public @interface treenodelabel{}


5, the final Outlook

Based on the example above. We still have a lot of places to improve, and here's what I mention:

1. The layout of item relies on the properties of very many beans. Use generics to store the corresponding bean in node. This allows the original bean data to be acquired through node in the Getconvertview.

2, about their own definition or do not triangle icon, can let Treelistviewadapter publish the method of setting icon, node all use the icon set in Treelistviewadapter; Direct Getconverview inside whether it is possible;

3. We get the ID, pId, label through annotations. Assumptions are slow and can be retrieved by means of callbacks. We traverse the time, to go through the definition of similarity in adapter: Abstract int getId (T t), the t as the parameter, let the user return ID, similar to PID, label, so the loop code needs to be extracted from Viewhelper to adapter construction method;

4, about the settings include check box. Select more than one node, do not save position finished. To save the ID in node is the primary key of the original bean, and then control the ID in Getconvertview to prevent confusion;

5, about the annotations, the current annotations only to the left and right of the logo; in fact, very many things, such as the default of our task User ID, PID is shaping. But there may be other types; we can determine by setting the method in the annotations, for example:

@Target (Elementtype.field) @Retention (retentionpolicy.runtime) public @interface treenodeid{class type ();}

@TreeNodeId (type = integer.class) private int _id;

Of course, assuming that your needs do not need to change the above, there is no need to toss ~ ~

To this, our entire blog ended up ~ ~ in the design of the assumption that there is insufficient, we can improve their own, I hope you learn through this blog is not only a sample how to achieve. Many other is how to design, of course, my ability is limited, please go to their own dross;



Source code click to download (already hit Jar)

Source code click to download (not called Jar version)



Bo Master part of the video has been launched. Let's say you don't like boring text. Please poke (first record, look forward to your support):

1, High imitation 5.2.1 main interface and message alert

2, high imitation QQ5.0 sideways slip











Android creates random-level tree controls to test your data structure and design

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.