Treeview Like Functionality Android
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"