Skip to content Skip to sidebar Skip to footer

Treeview Like Functionality Android

I'm implementing TreeView for my app. I've searched the web, found one ListView implementation TreeView which is too messy. Is it possible to implement n level TreeView using Expa

Solution 1:

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 :

publicinterfaceTreeElementIextendsSerializable{
  publicvoidaddChild(TreeElementI child);
  public String getId();
  publicvoidsetId(String id);
  public String getOutlineTitle();
  publicvoidsetOutlineTitle(String outlineTitle);
  public boolean isHasParent();
  publicvoidsetHasParent(boolean hasParent);
  public boolean isHasChild();
  publicvoidsetHasChild(boolean hasChild);
  publicintgetLevel();
  publicvoidsetLevel(int level);
  public boolean isExpanded();
  publicvoidsetExpanded(boolean expanded);
  public ArrayList<TreeElementI> getChildList();
  public TreeElementI getParent();
  publicvoidsetParent(TreeElementI parent);
}

TreeElement.java :

publicclassTreeElementimplementsTreeElementI{
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;
}

@Overridepublic void addChild(TreeElementI child) {
    this.getChildList().add(child);
    this.setHasParent(false);
    this.setHasChild(true);
    child.setParent(this);
    child.setLevel(this.getLevel() + 1);
}

@Overridepublic String getId() {
    returnthis.id;
}

@Overridepublic void setId(String id) {
    this.id = id;
}

@Overridepublic String getOutlineTitle() {
    returnthis.outlineTitle;
}

@Overridepublic void setOutlineTitle(String outlineTitle) {
    this.outlineTitle = outlineTitle;
}

@Overridepublic boolean isHasParent() {
    returnthis.hasParent;
}

@Overridepublic void setHasParent(boolean hasParent) {
    this.hasParent = hasParent;
}

@Overridepublic boolean isHasChild() {
    returnthis.hasChild;
}

@Overridepublic void setHasChild(boolean hasChild) {
    this.hasChild = hasChild;
}

@Overridepublic int getLevel() {
    returnthis.level;
}

@Overridepublic void setLevel(int level) {
    this.level = level;
}

@Overridepublic boolean isExpanded() {
    returnthis.expanded;
}

@Overridepublic void setExpanded(boolean expanded) {
    this.expanded = expanded;
}

@Overridepublic ArrayList<TreeElementI> getChildList() {
    returnthis.childList;
}

@Overridepublic TreeElementI getParent() {
    returnthis.parent;
}

@Overridepublic void setParent(TreeElementI parent) {
    this.parent = parent;
}
}

TreeViewClassifAdapter.java :

publicclassTreeViewClassifAdapterextendsBaseAdapter{
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() {
    returnthis.fileList;
}

@Overridepublic int getCount() {
    returnthis.fileList.size();
}

@Overridepublic Object getItem(int position) {
    returnthis.fileList.get(position);
}

@Overridepublic long getItemId(int position) {
    return position;
}

@Overridepublic 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);
    } elseif (elem.isHasChild() && (elem.isExpanded() == true)) {
        holder.getIcon().setImageBitmap(iconExpand);
    } elseif (!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;
}

privateclassViewHolder{
    ImageView icon;
    TextView text;

    public TextView getText() {
        returnthis.text;
    }

    public void setTextView(TextView text) {
        this.text = text;
    }

    public ImageView getIcon() {
        returnthis.icon;
    }

    public void setImageView(ImageView icon) {
        this.icon = icon;
    }
}

/**
 * Listener For TreeElement Text Click
 */privateclassTextClickListenerimplementsView.OnClickListener{
    private ArrayList<TreeElementI> list;
    private int position;

    public TextClickListener(ArrayList<TreeElementI> list, int position) {
        this.list = list;
        this.position = position;
    }

    @Overridepublic void onClick(View v) {
        treeView.setXValue(String.valueOf(list.get(position).getId()));
        dialog.dismiss();
    }
}

/**
 * Listener for TreeElement "Expand" button Click
 */privateclassIconClickListenerimplementsView.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;
    }

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

Solution 2:

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 ... :)

Solution 3:

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;

publicclassCustomExpandableListAdapterextendsSimpleExpandableListAdapter {

Contextcontext=null;
ExpandableListView topList;
LayoutInflaterinflater=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 = newHashMap<Integer, CustomExpandableListView>();

publicCustomExpandableListAdapter( 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,
            newString[] { "name", "_id", "parentId" },
            newint[] { R.id.tvLevelName, R.id.tvId, R.id.tvParentId },
            childData,
            R.layout.childrow,
            newString[] { "name", "_id", "parentId" },
            newint[] { R.id.tvLevelName, R.id.tvId, R.id.tvParentId }
    );  

    this.context = context;
    this.topList = topList;
    this.data = data;
    inflater = LayoutInflater.from(context);
}   

@Overridepublic 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); 
    IntegerlevelId= Integer.valueOf(levelinfo.get("_id"));

    if (levelinfo.get("hasChild").toString().equalsIgnoreCase("N")) {
        Viewv=super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent);            
        return v;   
    }
    else
    { // if the node contains child nodes then insert new expandable list           CustomExpandableListViewv=null;

        v = elvCache.get(levelId);
        if (v == null) {
            CreateData(levelinfo);
            v = newCustomExpandableListView(context, null, android.R.attr.expandableListViewStyle);
            v.setRows(groupData.size());        
            v.setPadding(10, 0, 0, 0);
            v.setAdapter(newCustomExpandableListAdapter(context, topList, data, groupData, childData));
            v.setOnGroupClickListener(newLevel2GroupExpandListener());
            elvCache.put(levelId, v);               
        }
        returnsuper.getChildView(groupPosition, childPosition, isLastChild, v, parent);
    }       
}

@OverridepublicbooleanhasStableIds() {
    returntrue;
}

@OverridepublicbooleanisChildSelectable(int groupPosition, int childPosition) {
    returntrue;
}

privateintCalculateRowCount(ExpandableListView parent ) {
    intcount=0;  
    //dig out the expanded child nodes in tree depthfor (inti=0; i < parent.getExpandableListAdapter().getGroupCount(); i++) {
        count++;
        if (parent.isGroupExpanded(i)) {
            count += GetFurtherChild(parent,i);
        }
    }   
    return count;
}

privateintGetFurtherChild(ExpandableListView parent, int index){

    intcount= parent.getExpandableListAdapter().getChildrenCount(index);
    ExpandableListViewelv=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;
}

publicvoidCreateData(HashMap<String, String> nodeData){

    // GET ID AND LEVEL OF PARENT NODE, LEVEL OF CHILD WILL BE LEVEL OF PARENT + 1Integerid= Integer.valueOf(nodeData.get("_id").toString());
    Integerlevel= Integer.valueOf(nodeData.get("level").toString()) + 1;

    groupData = newArrayList<Map<String, String>>();
    childData = newArrayList<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 = newArrayList<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 ENTRYif ( id == Integer.valueOf(levelDetail[1]) ) {

            childMap = newHashMap<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));
        }
    }
}

classLevel2GroupExpandListenerimplementsExpandableListView.OnGroupClickListener {

    @OverridepublicbooleanonGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
        if( parent.isGroupExpanded( groupPosition ) )
            parent.collapseGroup( groupPosition );
        else
            parent.expandGroup( groupPosition );

        if( parent instanceof CustomExpandableListView ) {
            CustomExpandableListViewcelv= (CustomExpandableListView)parent;
            Integerlevel= 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();
        returntrue;
    }
}

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;

publicclassCustomExpandableListViewextendsExpandableListView {

publicstaticint ROW_HEIGHT;
privateint rows;

publicCustomExpandableListView(Context context, AttributeSet attrs, int defStyle) {
    super( context, attrs, defStyle );

    if (Main.screenSize == ScreenSize.NARROW)
        ROW_HEIGHT = 45;
    elseROW_HEIGHT=31;
}

publicvoidsetRows( int rows ) {
    this.rows = rows;
}

protectedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension( getMeasuredWidth(), rows*ROW_HEIGHT);

}

protectedvoidonLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout( changed, left,top,right,bottom );
}

private String decodeMeasureSpec( int measureSpec ) {
    intmode= View.MeasureSpec.getMode( measureSpec );
    StringmodeString="<> ";
    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;

publicenumScreenSize {
NARROW, 
WIDE
}

Post a Comment for "Treeview Like Functionality Android"