Hi everybody!
I'am new to Android development and got stuck with one interesting issue.
I've started implementing my own tree-like view, which will expand/collapse its nodes as user taps on them. Every node in a tree is a custom TextView(just for now) and there is two included ViewGroups to hold those textviews together. ViewGroup forced me to override it's onLayoutMethod() which in my case will call layout method on each TextView node. Also used drawChild(Canvas canvas, View child, long drawingTime) to pass drawing from viewgroup to it's children. Every node(textview) implements View.OnTouchListener to handle touch events and calls parent's requestLayout() method to force re-layout of tree nodes. Everything seems to be working fine, but unfortunately for not so long )) Problem is that drawChild() method get's invoked by system only four times! Every next (on touch event) requestLayout() call executes onLayout() as always but drawChild() never gets invoked again, only app restart helps.
Application seems to be running normally and no exceptions are thrown into the console.
Can anyone help solving limited paints problem ? Will appreciate any comments or advices.
Here are sorces:
Node.java
SimpleTree.java which holds Nodes
General TreeView which operates SimpleTree
and finally Activity class
Thanks in advance.
I'am new to Android development and got stuck with one interesting issue.
I've started implementing my own tree-like view, which will expand/collapse its nodes as user taps on them. Every node in a tree is a custom TextView(just for now) and there is two included ViewGroups to hold those textviews together. ViewGroup forced me to override it's onLayoutMethod() which in my case will call layout method on each TextView node. Also used drawChild(Canvas canvas, View child, long drawingTime) to pass drawing from viewgroup to it's children. Every node(textview) implements View.OnTouchListener to handle touch events and calls parent's requestLayout() method to force re-layout of tree nodes. Everything seems to be working fine, but unfortunately for not so long )) Problem is that drawChild() method get's invoked by system only four times! Every next (on touch event) requestLayout() call executes onLayout() as always but drawChild() never gets invoked again, only app restart helps.
Application seems to be running normally and no exceptions are thrown into the console.
Can anyone help solving limited paints problem ? Will appreciate any comments or advices.
Here are sorces:
Node.java
Code:
package com.example;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.widget.TextView;
import java.util.Vector;
public class Node extends TextView implements View.OnTouchListener
{
private Object data;
private Vector children;
private Node parentNode;
private boolean isExpanded = true;
public Node(Context context)
{
super(context);
setOnTouchListener(this);
}
public Node(Context context, Object data)
{
this(context);
setData(data);
if (data instanceof String)
setText((String) data);
}
public Vector<Node> getChildren()
{
if (this.children == null)
{
return new Vector();
}
return this.children;
}
public void setChildren(Vector children)
{
if (children != null)
{
for (int i = 0; i < children.size(); i++)
{
((Node) children.elementAt(i)).setParent(this);
}
}
this.children = children;
}
public int getNumberOfChildren()
{
if (children == null)
{
return 0;
}
return children.size();
}
public void addChild(Node child)
{
if (children == null)
{
children = new Vector();
}
child.setParent(this);
children.addElement(child);
}
public void insertChildAt(int index, Node child) throws IndexOutOfBoundsException
{
if (index == getNumberOfChildren())
{
// this is really an append
addChild(child);
}
else
{
child.setParent(this);
children.elementAt(index); //just to throw the exception, and stop here
children.insertElementAt(child, index);
}
}
public void removeChildAt(int index) throws IndexOutOfBoundsException
{
children.removeElementAt(index);
}
public Object getData()
{
return this.data;
}
public void setData(Object data)
{
this.data = data;
}
public boolean isExpanded()
{
return isExpanded;
}
public void setExpanded(boolean expanded)
{
isExpanded = expanded;
}
public void setParent(Node parentNode)
{
this.parentNode = parentNode;
}
public Node getParentNode()
{
return parentNode;
}
public boolean equals(Object obj)
{
if (obj instanceof Node)
{
Object objectData = ((Node) obj).data;
if (data != null)
return data.equals(objectData);
else
return objectData == null;
}
else
return false;
}
public int hashCode()
{
if (data == null)
return 0;
else
return data.hashCode();
}
@Override
public void onDraw(Canvas canvas)
{
System.out.println("in node onDraw");
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawText((String) getData(), getLeft() + 10, getBottom(), paint);
if (!getChildren().isEmpty())
{
canvas.drawLine(getLeft(), getTop() + ((getBottom() - getTop()) / 2) + 1, getLeft() + 8, getTop() + ((getBottom() - getTop()) / 2) + 1, paint);
if (!isExpanded)
canvas.drawLine(getLeft() + 4, getTop() + 4, getLeft() + 4, getBottom(), paint);
}
}
public boolean onTouch(View view, MotionEvent motionEvent)
{
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN)
{
isExpanded = !isExpanded;
System.out.println("Expanded=" + isExpanded);
ViewParent viewParent = view.getParent();
viewParent.requestLayout();
return true;
}
return false;
}
}
Code:
package com.example;
import android.content.Context;
import android.graphics.Canvas;
import android.view.ViewGroup;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Vector;
public class SimpleTree extends ViewGroup
{
private Node rootElement;
public SimpleTree(Context context)
{
super(context);
rootElement = new Node(context, new Long(-1));
}
public Node getRootElement()
{
return this.rootElement;
}
public Vector toVector()
{
Vector list = new Vector();
walk(rootElement, list);
return list;
}
public String toString()
{
return toVector().toString();
}
private void walk(Node element, Vector list)
{
list.addElement(element);
Vector children = element.getChildren();
for (int i = 0; i < element.getNumberOfChildren(); i++)
{
walk((Node) children.elementAt(i), list);
}
}
public Node findNode(Object data)
{
return findNode(rootElement, data);
}
private Node findNode(Node node, Object data)
{
if (node.getData().equals(data))
return node;
else
{
Vector children = node.getChildren();
for (int i = 0; i < children.size(); i++)
{
Node res = findNode((Node) children.elementAt(i), data);
if (res != null)
return res;
}
return null;
}
}
public void insertToTreeAsChild(Node parent, Node bufferTaskNode)
{
addView(bufferTaskNode);
Node parentNode = findNode(parent.getData());
if (parentNode == null)
parentNode = getRootElement();
Node taskNode = findNode(bufferTaskNode.getData());
if (taskNode == null)
taskNode = bufferTaskNode;
if (parentNode.isExpanded())
parentNode.insertChildAt(0, taskNode);
else
parentNode.addChild(taskNode);
}
public void insertToTreeAsSibling(Node parent, Node bufferTaskNode, Node focusTaskNode)
{
addView(bufferTaskNode);
Node parentNode;
Node taskNode = findNode(bufferTaskNode.getData());
if (taskNode == null)
taskNode = bufferTaskNode;
if (parent == null)
parentNode = getRootElement();
else
{
parentNode = findNode(parent.getData());
if (parentNode == null)
parentNode = getRootElement();
}
taskNode.setParent(parentNode);
if (focusTaskNode != null)
{
for (int i = 0; i < parentNode.getNumberOfChildren(); i++)
{
String tempChild = (String) ((Node) parentNode.getChildren().elementAt(i)).getData();
if (tempChild.equals(((String) focusTaskNode.getData())))
{
parentNode.insertChildAt(i + 1, taskNode);
break;
}
}
}
else
parentNode.insertChildAt(0, taskNode);
}
@Override
public void onDraw(Canvas canvas)
{
paintElements(canvas, getRootElement());
}
private void paintElements(Canvas canvas, Node node)
{
if (!(node.getData() instanceof Long))
{
node.draw(canvas);
}
if (node.getChildren() != null && node.getChildren().size() != 0 && node.isExpanded())
{
for (int i = 0; i < node.getChildren().size(); i++)
{
paintElements(canvas, node.getChildren().elementAt(i));
}
}
}
@Override
protected void onLayout(boolean b, int left, int top, int right, int bottom)
{
layoutElements(getRootElement(), 0, 1);
}
private int layoutElements(Node node, int left, int top)
{
if (!(node.getData() instanceof Long))
{
node.layout(left * 15, top * 15, 50 + left * 15, top * 15 + (int) node.getPaint().getTextSize());
}
if (node.getChildren() != null && node.getChildren().size() != 0 && node.isExpanded())
{
top++;
for (int i = 0; i < node.getChildren().size(); i++)
{
top = layoutElements(node.getChildren().elementAt(i), left + 1, top + i);
}
}
return top;
}
}
Code:
public class TreeView extends ViewGroup
{
SimpleTree tree;
public TreeView(Context context)
{
super(context);
tree = new SimpleTree(context);
addView(tree);
tree.insertToTreeAsSibling(tree.getRootElement(), new Node(context, "test1"), null);
tree.insertToTreeAsChild(tree.findNode("test1"), new Node(context, "test11"));
tree.insertToTreeAsChild(tree.findNode("test11"), new Node(context, "test111"));
tree.insertToTreeAsChild(tree.findNode("test11"), new Node(context, "test112"));
tree.insertToTreeAsSibling(tree.getRootElement(), new Node(context, "test2"), tree.findNode("test1"));
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3)
{
tree.onLayout(b, i, i1, i2, i3);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime)
{
tree.onDraw(canvas);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
return tree.dispatchTouchEvent(event);
}
}
Code:
public class MLOActivity extends Activity
{
public static final String GROUP_ID = "Group";
public static final String CHILD_ID = "Child";
public static final int GROUPS = 2;
public static final int CHILDREN = 2;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TreeView view = new TreeView(this);
setContentView(view);
view.invalidate();
}
}