I\'m implementing TreeView
for my app.
I\'ve searched the web, found one ListView
implementation TreeView
which is too messy. Is it p
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 ... :)
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
}
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();
}
}
}
}