TreeView like Functionality Android

前端 未结 3 1089
南旧
南旧 2021-01-01 05:42

I\'m implementing TreeView for my app. I\'ve searched the web, found one ListView implementation TreeView which is too messy. Is it p

相关标签:
3条回答
  • 2021-01-01 06:10

    This google's projct will help to use it as a external android library. After unsetting the "isLibrary?" flag, the project can also be compiled and installed on its own - providing demo application that presents capability of the widget. It shows how the tree behaves dynamically including explanding and collapsing nodes for many/all node, providing context menu for the tree, custom tree view with checkboxes only available for leaf nodes, custom colouring and different text sizes for text for different levels of the tree (albeit ugly) of the tree.

    Hope this helps ... :)

    0 讨论(0)
  • 2021-01-01 06:24

    You will have to extend two classes for creating the tree view. Here is the sample code

    package com.example.mytreeview;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import android.content.Context;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ExpandableListView;
    import android.widget.SimpleExpandableListAdapter;
    
    public class CustomExpandableListAdapter extends SimpleExpandableListAdapter {
    
    Context context = null;
    ExpandableListView topList;
    LayoutInflater inflater = null;
    HashMap<Integer, ArrayList<String[]>> data;
    ArrayList<Map<String, String>> groupData;
    ArrayList<List<Map<String, String>>> childData;
    List<Map<String, String>> children;
    Map<String, String> childMap;
    ArrayList<String[]> list;
    int listSize;
    HashMap<Integer, CustomExpandableListView> elvCache = new HashMap<Integer, CustomExpandableListView>();
    
    public CustomExpandableListAdapter( Context context, 
            ExpandableListView topList, 
            HashMap<Integer, ArrayList<String[]>> data, 
            ArrayList<Map<String, String>> groupData, 
            ArrayList<List<Map<String, String>>> childData ) {      
        super(
                context,
                groupData,
                R.layout.grouprow,
                new String[] { "name", "_id", "parentId" },
                new int[] { R.id.tvLevelName, R.id.tvId, R.id.tvParentId },
                childData,
                R.layout.childrow,
                new String[] { "name", "_id", "parentId" },
                new int[] { R.id.tvLevelName, R.id.tvId, R.id.tvParentId }
        );  
    
        this.context = context;
        this.topList = topList;
        this.data = data;
        inflater = LayoutInflater.from(context);
    }   
    
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {       
    
        // if there are no child nodes then simply show the single row  
        HashMap <String, String> levelinfo = (HashMap<String, String>)getChild(groupPosition, childPosition); 
        Integer levelId = Integer.valueOf(levelinfo.get("_id"));
    
        if (levelinfo.get("hasChild").toString().equalsIgnoreCase("N")) {
            View v = super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent);            
            return v;   
        }
        else
        { // if the node contains child nodes then insert new expandable list           
            CustomExpandableListView v = null;
    
            v = elvCache.get(levelId);
            if (v == null) {
                CreateData(levelinfo);
                v = new CustomExpandableListView(context, null, android.R.attr.expandableListViewStyle);
                v.setRows(groupData.size());        
                v.setPadding(10, 0, 0, 0);
                v.setAdapter(new CustomExpandableListAdapter(context, topList, data, groupData, childData));
                v.setOnGroupClickListener(new Level2GroupExpandListener());
                elvCache.put(levelId, v);               
            }
            return super.getChildView(groupPosition, childPosition, isLastChild, v, parent);
        }       
    }
    
    @Override
    public boolean hasStableIds() {
        return true;
    }
    
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
    
    private int CalculateRowCount (ExpandableListView parent ) {
        int count = 0;  
        //dig out the expanded child nodes in tree depth
        for (int i = 0; i < parent.getExpandableListAdapter().getGroupCount(); i++) {
            count++;
            if (parent.isGroupExpanded(i)) {
                count += GetFurtherChild(parent,i);
            }
        }   
        return count;
    }
    
    private int GetFurtherChild (ExpandableListView parent, int index){
    
        int count = parent.getExpandableListAdapter().getChildrenCount(index);
        ExpandableListView elv = null;
    
        for (int i=0; i<parent.getExpandableListAdapter().getChildrenCount(index); i++ ) {          
            try {//check if this is expandable list
                elv = (ExpandableListView)parent.getExpandableListAdapter().getChildView(index, i, false, null, parent);
                if (elv != null && elv.isGroupExpanded(0)) {
                    count += GetFurtherChild(elv, 0);
                }
            } catch (Exception e) {
                Log.d("Exception", e.getMessage());
            }           
        }
        return count;
    }
    
    public void CreateData(HashMap<String, String> nodeData){
    
        // GET ID AND LEVEL OF PARENT NODE, LEVEL OF CHILD WILL BE LEVEL OF PARENT + 1
        Integer id = Integer.valueOf(nodeData.get("_id").toString());
        Integer level = Integer.valueOf(nodeData.get("level").toString()) + 1;
    
        groupData = new ArrayList<Map<String, String>>();
        childData = new ArrayList<List<Map<String, String>>>();
    
        // GET CHILD LIST. 
        list = data.get(level); 
        listSize = list.size();
    
        // PARENT NODE DATA IS ALREADY IN NODE DATA HASH MAP
        groupData.add(nodeData);
    
        // WE NEED TO CREATE CHILD DATA 
        children = new ArrayList<Map<String, String>>();
        childData.add(children);
    
        for (int i=0; i < listSize; i++) { 
            // GET THE DETAIL ARRAY 
            String [] levelDetail = list.get(i);
    
            // IF PARENT NODE ID AND CHILD NODE PARENT ID IS SAME THEN CREATE ENTRY
            if ( id == Integer.valueOf(levelDetail[1]) ) {
    
                childMap = new HashMap<String, String>();
                children.add(childMap);
                childMap.put("_id", levelDetail[0]);
                childMap.put("parentId", levelDetail[1]);
                childMap.put("name", levelDetail[2]);
                childMap.put("hasChild", levelDetail[3]);  
                childMap.put("level", String.valueOf(level));
            }
        }
    }
    
    class Level2GroupExpandListener implements ExpandableListView.OnGroupClickListener {
    
        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            if( parent.isGroupExpanded( groupPosition ) )
                parent.collapseGroup( groupPosition );
            else
                parent.expandGroup( groupPosition );
    
            if( parent instanceof CustomExpandableListView ) {
                CustomExpandableListView celv = (CustomExpandableListView)parent;
                Integer level = Integer.valueOf(((HashMap<String, String>) parent.getExpandableListAdapter().getGroup(groupPosition)).get("level").toString());
                celv.setRows(CalculateRowCount(celv));
                celv.requestLayout();
    
                if (level > 1) {
                    while (((HashMap<String, String>)parent.getExpandableListAdapter().getGroup(0)).get("level").toString().equalsIgnoreCase("1") == false) {
                        parent = (ExpandableListView)parent.getParent();
                        celv = (CustomExpandableListView)parent;
                        celv.setRows(CalculateRowCount(parent));
                        celv.requestLayout();           
                    }   
                }
            }
            topList.requestLayout();
            return true;
        }
    }
    
    package com.example.mytreeview;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.ColorFilter;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.View;
    import android.widget.ExpandableListView;
    
    public class CustomExpandableListView extends ExpandableListView {
    
    public static int ROW_HEIGHT;
    private int rows;
    
    public CustomExpandableListView(Context context, AttributeSet attrs, int defStyle) {
        super( context, attrs, defStyle );
    
        if (Main.screenSize == ScreenSize.NARROW)
            ROW_HEIGHT = 45;
        else
            ROW_HEIGHT = 31;
    }
    
    public void setRows( int rows ) {
        this.rows = rows;
    }
    
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension( getMeasuredWidth(), rows*ROW_HEIGHT);
    
    }
    
    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
        super.onLayout( changed, left,top,right,bottom );
    }
    
    private String decodeMeasureSpec( int measureSpec ) {
        int mode = View.MeasureSpec.getMode( measureSpec );
        String modeString = "<> ";
        switch( mode ) {
        case View.MeasureSpec.UNSPECIFIED:
            modeString = "UNSPECIFIED ";
            break;
    
        case View.MeasureSpec.EXACTLY:
            modeString = "EXACTLY ";
            break;
    
        case View.MeasureSpec.AT_MOST:
            modeString = "AT_MOST ";
            break;
        }
        return modeString+Integer.toString( View.MeasureSpec.getSize( measureSpec ) );
    }
    
    
    }
    
    
    package com.example.mytreeview;
    
    public enum ScreenSize {
    NARROW, 
    WIDE
    }
    
    0 讨论(0)
  • 2021-01-01 06:27

    I solved my problem long time ago with ListView. There were people who had the same problem and asked me to share my solution, so here I am.

    TreeElementI.java :

    public interface TreeElementI extends Serializable{
      public void addChild(TreeElementI child);
      public String getId();
      public void setId(String id);
      public String getOutlineTitle();
      public void setOutlineTitle(String outlineTitle);
      public boolean isHasParent();
      public void setHasParent(boolean hasParent);
      public boolean isHasChild();
      public void setHasChild(boolean hasChild);
      public int getLevel();
      public void setLevel(int level);
      public boolean isExpanded();
      public void setExpanded(boolean expanded);
      public ArrayList<TreeElementI> getChildList();
      public TreeElementI getParent();
      public void setParent(TreeElementI parent);
    }
    

    TreeElement.java :

    public class TreeElement implements TreeElementI{
    private String id;
    private String outlineTitle;
    private boolean hasParent;
    private boolean hasChild;
    private TreeElementI parent;
    private int level;
    private ArrayList<TreeElementI> childList;
    private boolean expanded;
    
    public TreeElement(String id, String outlineTitle) {
        super();
        this.childList = new ArrayList<TreeElementI>();
        this.id = id;
        this.outlineTitle = outlineTitle;
        this.level = 0;
        this.hasParent = true;
        this.hasChild = false;
        this.parent = null;
    }
    
    public TreeElement(String id, String outlineTitle, boolean hasParent, boolean hasChild, TreeElement parent, int level, boolean expanded) {
        super();
        this.childList = new ArrayList<TreeElementI>();
        this.id = id;
        this.outlineTitle = outlineTitle;
        this.hasParent = hasParent;
        this.hasChild = hasChild;
        this.parent = parent;
        if(parent != null) {
            this.parent.getChildList().add(this);
        }
        this.level = level;
        this.expanded = expanded;
    }
    
    @Override
    public void addChild(TreeElementI child) {
        this.getChildList().add(child);
        this.setHasParent(false);
        this.setHasChild(true);
        child.setParent(this);
        child.setLevel(this.getLevel() + 1);
    }
    
    @Override
    public String getId() {
        return this.id;
    }
    
    @Override
    public void setId(String id) {
        this.id = id;
    }
    
    @Override
    public String getOutlineTitle() {
        return this.outlineTitle;
    }
    
    @Override
    public void setOutlineTitle(String outlineTitle) {
        this.outlineTitle = outlineTitle;
    }
    
    @Override
    public boolean isHasParent() {
        return this.hasParent;
    }
    
    @Override
    public void setHasParent(boolean hasParent) {
        this.hasParent = hasParent;
    }
    
    @Override
    public boolean isHasChild() {
        return this.hasChild;
    }
    
    @Override
    public void setHasChild(boolean hasChild) {
        this.hasChild = hasChild;
    }
    
    @Override
    public int getLevel() {
        return this.level;
    }
    
    @Override
    public void setLevel(int level) {
        this.level = level;
    }
    
    @Override
    public boolean isExpanded() {
        return this.expanded;
    }
    
    @Override
    public void setExpanded(boolean expanded) {
        this.expanded = expanded;
    }
    
    @Override
    public ArrayList<TreeElementI> getChildList() {
        return this.childList;
    }
    
    @Override
    public TreeElementI getParent() {
        return this.parent;
    }
    
    @Override
    public void setParent(TreeElementI parent) {
        this.parent = parent;
    }
    }
    

    TreeViewClassifAdapter.java :

    public class TreeViewClassifAdapter extends BaseAdapter {
    private static final int TREE_ELEMENT_PADDING_VAL = 25;
    private List<TreeElementI> fileList;
    private Context context;
    private Bitmap iconCollapse;
    private Bitmap iconExpand;
    private Dialog dialog;
    private EditText textLabel;
    private XTreeViewClassif treeView;
    
    public TreeViewClassifAdapter(Context context, List<TreeElementI> fileList, Dialog dialog, EditText textLabel, XTreeViewClassif treeView) {
        this.context = context;
        this.fileList = fileList;
        this.dialog = dialog;
        this.textLabel = textLabel;
        this.treeView = treeView;
        iconCollapse = BitmapFactory.decodeResource(context.getResources(), R.drawable.x_treeview_outline_list_collapse);
        iconExpand = BitmapFactory.decodeResource(context.getResources(), R.drawable.x_treeview_outline_list_expand);
    }
    
    public List<TreeElementI> getListData() {
        return this.fileList;
    }
    
    @Override
    public int getCount() {
        return this.fileList.size();
    }
    
    @Override
    public Object getItem(int position) {
        return this.fileList.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
    
        convertView = View.inflate(context, R.layout.x_treeview_classif_list_item, null);
        holder = new ViewHolder();
        holder.setTextView((TextView) convertView.findViewById(R.id.text));
        holder.setImageView((ImageView) convertView.findViewById(R.id.icon));
        convertView.setTag(holder);
    
        final TreeElementI elem = (TreeElementI) getItem(position);
    
        int level = elem.getLevel();
        holder.getIcon().setPadding(TREE_ELEMENT_PADDING_VAL * (level + 1), holder.icon.getPaddingTop(), 0, holder.icon.getPaddingBottom());
        holder.getText().setText(elem.getOutlineTitle());
        if (elem.isHasChild() && (elem.isExpanded() == false)) {
            holder.getIcon().setImageBitmap(iconCollapse);
        } else if (elem.isHasChild() && (elem.isExpanded() == true)) {
            holder.getIcon().setImageBitmap(iconExpand);
        } else if (!elem.isHasChild()) {
            holder.getIcon().setImageBitmap(iconCollapse);
            holder.getIcon().setVisibility(View.INVISIBLE);
        }
    
        IconClickListener iconListener = new IconClickListener(this, position);
        TextClickListener txtListener = new TextClickListener((ArrayList<TreeElementI>) this.getListData(), position);
        holder.getIcon().setOnClickListener(iconListener);
        holder.getText().setOnClickListener(txtListener);
        return convertView;
    }
    
    private class ViewHolder {
        ImageView icon;
        TextView text;
    
        public TextView getText() {
            return this.text;
        }
    
        public void setTextView(TextView text) {
            this.text = text;
        }
    
        public ImageView getIcon() {
            return this.icon;
        }
    
        public void setImageView(ImageView icon) {
            this.icon = icon;
        }
    }
    
    /**
     * Listener For TreeElement Text Click
     */
    private class TextClickListener implements View.OnClickListener {
        private ArrayList<TreeElementI> list;
        private int position;
    
        public TextClickListener(ArrayList<TreeElementI> list, int position) {
            this.list = list;
            this.position = position;
        }
    
        @Override
        public void onClick(View v) {
            treeView.setXValue(String.valueOf(list.get(position).getId()));
            dialog.dismiss();
        }
    }
    
    /**
     * Listener for TreeElement "Expand" button Click
     */
    private class IconClickListener implements View.OnClickListener {
        private ArrayList<TreeElementI> list;
        private TreeViewClassifAdapter adapter;
        private int position;
    
        public IconClickListener(TreeViewClassifAdapter adapter, int position) {
            this.list = (ArrayList<TreeElementI>) adapter.getListData();
            this.adapter = adapter;
            this.position = position;
        }
    
        @Override
        public void onClick(View v) {
            if (!list.get(position).isHasChild()) {
                return;
            }
    
            if (list.get(position).isExpanded()) {
                list.get(position).setExpanded(false);
                TreeElementI element = list.get(position);
                ArrayList<TreeElementI> temp = new ArrayList<TreeElementI>();
    
                for (int i = position + 1; i < list.size(); i++) {
                    if (element.getLevel() >= list.get(i).getLevel()) {
                        break;
                    }
                    temp.add(list.get(i));
                }
                list.removeAll(temp);
                adapter.notifyDataSetChanged();
            } else {
                TreeElementI obj = list.get(position);
                obj.setExpanded(true);
                int level = obj.getLevel();
                int nextLevel = level + 1;
    
                for (TreeElementI element : obj.getChildList()) {
                    element.setLevel(nextLevel);
                    element.setExpanded(false);
                    list.add(position + 1, element);
                }
                adapter.notifyDataSetChanged();
            }
        }
    }
    }
    
    0 讨论(0)
提交回复
热议问题