【问题标题】:TreeView like Functionality AndroidTreeView 类似功能 Android
【发布时间】:2013-09-09 12:35:25
【问题描述】:

我正在为我的应用实现TreeView。 我在网上搜索过,发现一个ListView 实现TreeView 太乱了。是否可以使用ExpandableListView 实现n 级TreeView

请分享您的想法或参考一些示例。

提前致谢。

【问题讨论】:

    标签: android android-listview treeview expandablelistview


    【解决方案1】:

    我很久以前用ListView 解决了我的问题。有人遇到同样的问题,让我分享我的solution,所以我来了。

    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();
            }
        }
    }
    }
    

    【讨论】:

    • 你能发布你的 XTreeViewClassif 类吗?
    • XTreeViewClassif 类只是一个自定义视图,onClick 打开带有 ListView 的对话框窗口,它使用 TreeViewClassifAdapter。
    【解决方案2】:

    This google 的项目将有助于将其用作外部 android 库。取消设置“isLibrary?”后标志,该项目也可以自己编译和安装 - 提供演示小部件功能的演示应用程序。它显示了树的动态行为,包括为许多/所有节点展开和折叠节点、为树提供上下文菜单、带有仅可用于叶节点的复选框的自定义树视图、自定义颜色和不同文本大小的文本树的不同级别(尽管丑陋)的树。

    希望这会有所帮助... :)

    【讨论】:

    【解决方案3】:

    您必须扩展两个类来创建树视图。这是示例代码

    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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-25
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      • 2012-12-15
      • 2012-05-20
      • 1970-01-01
      相关资源
      最近更新 更多