Android tree TreeView implementation, supporting dynamic addition and deletion of nodes

Refer to the following very good blog and write a treeView
https://blog.csdn.net/lmj623565791/article/details/40212367

But what's lacking in beauty is:
1. This treeview can only be queried and cannot be modified (such as deleting nodes)
This implementation is an improvement of the above blog.

Principle analysis:
List view is android's native and powerful view for displaying list; my implementation is based on this listview.
Different from listview:
1. If you click a non leaf node of the tree structure, its nodes need to be expanded or scaled
2. According to the level of nodes, it is necessary to display the indent reasonably
Therefore, the core of the above functions is to transform the adapter corresponding to listview. When you click node / delete node or add node, adjust the data content corresponding to adapter dynamically.

This implementation includes three classes, Node, Tree and treelistviewapter.
Node: used to save the information of each node;
Tree is used to save the entire data structure.
Treelistviewapter inherits BaseAdapter, which is used for the Adapter of listView
Take a look at the contents of these three classes:

Node

public class Node <T>{
    private int _id;  //Node id
    private Node _parent;  //Parent node
    private List<Node> _children=new ArrayList<>(); //All son nodes
    private T obj; //User object attached to this node
    private int _size_all; //The size of the tree corresponding to this node (calculate all nodes under it, regardless of the expansion status)
    private int _size_expand; //Tree expansion state size corresponding to this node
    private boolean isExpand=true; //Expansion status of this node

    public Node(int id, T obj){
        this._id = id;
        this.obj = obj;
        _size_expand=1;
        _size_all=1;
    }
    Node get_parent(){return _parent;}
    void set_parent(Node node){
        this._parent = node;
    }
    List<Node> get_children(){return _children;}
    int get_size(boolean isExpand){return isExpand?_size_expand:_size_all;}
    void set_size(int size,boolean isExpand){
        if(isExpand)
            _size_expand = size;
        else
            _size_all=size;
    }
    //Find a node with id equal to the tree whose current node is the root. If it cannot be found, return null
    //isExpand:
    //true: indicates to find on the expand tree
    // false: it means that the expand attribute is not considered, and it is found on the whole tree
    Node find_Node_By_Id(int id,boolean isExpand){
        if(this.get_id()==id)
            return this;

        List<Node> list=this.get_children();
        if(list.size()==0){
            return null;
        }else{
            if((isExpand && this.getExpand()) || !isExpand){
                for(Node node: this.get_children()){
                    Node result=node.find_Node_By_Id(id,isExpand);
                    if(result!=null)
                        return result;
                }
            }
        }
        return null;
    }
    //According to the depth priority, traverse the entire tree with this node as the root, and return the node of the position element
    //position starts from 0
    //isExpand:
    //true: indicates to find on the expand tree
    // false: it means that the expand attribute is not considered, and it is found on the whole tree
    Node get(int position,boolean isExpand){
        if(position==0)
            return this;
        position--;
        List<Node> list=this.get_children();
        if(list.size()==0){
            return null;
        }else{
            if(!isExpand || (isExpand && this.getExpand())){
                for(Node node:this.get_children()){
                    int size = position - node.get_size(isExpand);
                    if(size<0){
                        return node.get(position,isExpand);
                    }else{
                        position=size;
                    }
                }//for
            }//if
        }//if
        return null;
    }
    void setExpand(boolean expand) {
        isExpand = expand;
    }
    boolean getExpand(){
        return isExpand;
    }
    public boolean isLeaf(){
        return this.get_children().size()==0;
    }
    public boolean isExpand(){
        return isExpand;
    }
    public int get_id(){return _id;}
    public int get_level(){
        if(_parent!=null)
            return _parent.get_level()+1;
        else
            return 0;
    }
    public T getObject(){
        return obj;
    }
}

Tree

/**
 * Created by deqiang.wang on 2018/4/6.
 */
//Forest in essence
public class Tree <T>{
    private Node root;

    public Tree(){
        root = new Node(-1,null);
    }

   public boolean addRoot(int id,T t){
       if(id<0){
           Log.w(TAG, "addRoot: node.id cannot be less than 0" );
           return false;
       }
        //Must find in all nodes
        if(findNode(id)==null){
            addNodeToParent(new Node(id,t),root);
        }else {
            Log.w(TAG, String.format("addRoot: node.id=%d exists!",id));
            return false;
        }

        return true;
   }

   public boolean addLeaf(int id, int pid, Object object){
        if(id<0 || pid<0){
            Log.w(TAG, "addNode: id or pid should not be less than 0");
            return false;
        }
        Node node = findNode(id);
        if(node!=null){
            Log.w(TAG, String.format("addNode: node.id=%d exists",id));
            return false;
        }
        Node parent = findNode(pid);
        if(parent==null){
            Log.w(TAG, String.format("addNode: cannot find parent with id=",pid));
            return false;
        }
        node = new Node(id,object);

        addNodeToParent(node,parent);
        return true;
   }

   //Hang a node under the parent node
    private void addNodeToParent(Node node, Node parent){
        node.set_parent(parent);
        parent.get_children().add(node);

        while(parent!=null){
            parent.set_size(parent.get_size(false)+node.get_size(false),false);
            parent.set_size(parent.get_size(true)+node.get_size(true),true);
            parent = parent.get_parent();
        }
    }
    //Delete a node
    public void deleteNode(int id){
        if(id<0){
            Log.w(TAG, "deleteNode: id should not be less than 0");
            return;
        }
        Node node = findNode(id);
        if(node==null){
            Log.w(TAG, "deleteNode: cannot find the node.id="+id );
            return;
        }
        Node parent = node.get_parent();
        parent.get_children().remove(node);
        while(parent!=null){
            parent.set_size(parent.get_size(false)-node.get_size(false),false);
            parent.set_size(parent.get_size(true)-node.get_size(true),true);
            parent=parent.get_parent();
        }
    }
    public Node findNode(int id){
        return root.find_Node_By_Id(id,false);
    }
    //According to the depth priority, traverse the entire tree and return the node of the position element
    //position starts from 0
    public Node getInAll(int position){
        return get(position,false);
    }
    //Traverse only visible parts
    public Node getInCollapse(int position){
        return get(position,true);
    }
    private Node get(int position, boolean isExpand){
        return root.get(position+1,isExpand);
    }
    public int sizeOfAll(){
        return size(false);
    }
    public int sizeOfCollapse(){
        return size(true);
    }
    //Get the size of the tree
    //isExpand:
    // true indicates the visible size of the deployment
    // false indicates the size of the whole tree
    private int size(boolean isExpand){
        return root.get_size(isExpand) - 1;
    }
    //Click position to collapse or expand the node
    //return
    // false: indicates that nothing has been done
    public boolean expandOrCollapse(int position){
        Node node = getInCollapse(position);
        if(node==null) {
            Log.w(TAG, "expandOrCollapse: cannot find node at position="+position );
            return false;
        }
        if(node.isLeaf())
            return false;
        if(node.getExpand()){
            //Collapse this node
            int sizedelta = node.get_size(true) - 1;
            node.set_size(1,true);
            Node parent = node.get_parent();
            while(parent!=null){
                if(parent.getExpand()==false){
                    Log.e(TAG, String.format("expandOrCollapse: node.id should be collapsed!",parent.get_id()) );
                    return false;
                }
                parent.set_size(parent.get_size(true) - sizedelta,true);
                parent = parent.get_parent();
            }
        }else{
            //Expand this node
            int sizedelta=0;
            List<Node> children=node.get_children();
            for(Node son:children){
                sizedelta +=son.get_size(true);
            }
            node.set_size(1+sizedelta,true);
            Node parent = node.get_parent();
            while(parent!=null){
                if(parent.getExpand()==false){
                    Log.e(TAG, String.format("expandOrCollapse: node.id should be collapsed!",parent.get_id()) );
                    return false;
                }
                parent.set_size(parent.get_size(true) + sizedelta,true);
                parent = parent.get_parent();
            }
        }
        node.setExpand(!node.getExpand());
        return true;
    }
}

TreeListViewAdapter

public abstract class TreeListViewAdapter<T> extends BaseAdapter
{

    protected Context mContext;
    protected Tree mTree;
    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 mTreeView, Context context, Tree tree)
    {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mTree = tree;

        mTreeView.setOnItemClickListener(new OnItemClickListener()
        {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
            {
                if(mTree!=null) {
                    mTree.expandOrCollapse(position);
                    notifyDataSetChanged();
                    if (onTreeNodeClickListener != null)
                    {
                        onTreeNodeClickListener.onClick(mTree.getInCollapse(position), position);
                    }
                }
            }
        });
    }

    @Override
    public int getCount()
    {
        return mTree==null?0:mTree.sizeOfCollapse();
    }

    @Override
    public Object getItem(int position)
    {
        return mTree==null?null:mTree.getInCollapse(position);
    }

    @Override
    public long getItemId(int position)
    {
        return position;
    }

    @Override
    public  View getView(int position, View convertView,
            ViewGroup parent)
    {
        if(mTree==null)
            return null;

        Node node = mTree.getInCollapse(position);
        convertView = getConvertView( node , position , convertView , parent);
        convertView.setPadding(node.get_level() * 30, 3, 3, 3);
        return convertView ;
    }

    public abstract View getConvertView(Node<T> node , int position, View convertView,
                                        ViewGroup parent);
}

Example

public class Tree_Activity extends Activity implements TreeListViewAdapter.OnTreeNodeClickListener{
    Paint mPaint;
    int mLeftX=0;
    int mLeftY=0;
    Tree<String> mTree = new Tree();
    TreeListViewAdapter myAdapter = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tree);

        mTree.addRoot(0,"");
        mTree.addLeaf(1,0,"a");
        mTree.addLeaf(2,0,"b");
        mTree.addLeaf(3,0,"c");
        mTree.addLeaf(5,0,"d");
        mTree.addLeaf(4,2,"e");
        mTree.addLeaf(7,4,"f");
        mTree.addLeaf(8,7,"g");
        mTree.addLeaf(9,8,"h");
        mTree.addLeaf(6,5,"i");

        ListView listView = findViewById(R.id.listViewTree);
        myAdapter = new MyAdapter(listView,this,mTree);
        listView.setAdapter(myAdapter);

        myAdapter.setOnTreeNodeClickListener(this);
    }

    public void onClickTreeNode(Node node, int position){
        //The processing of nodes can be done here,
        //For example, if you want to delete a node, click
        //mTree.deleteNode(node.get_id());
        //myAdapter.notifyDataSetChanged();
    }
}

Actual operation rendering:

Test code and source resource file
https://download.csdn.net/download/wangdeqiang2007/10386560

Tags: less Attribute Android

Posted on Thu, 26 Mar 2020 12:01:57 -0400 by Afser