I am creating an isometric map with simple tiles, and I’ve extended RelativeLayout
to create a layout that holds these tiles. Really, just using a RelativeLayout
as-is worked fine as long as my orientation matched the order in which the tiles were written to the XML file; all I’ve overwritten are the constructors, where I simply call super
and setChildrenDrawingOrderEnabled(true);
along with setting up some variables (height and width of the grid), and then getChildDrawingOrder
itself.
My code for getChildDrawingOrder
figures out the new index for the given child, and also sets a string in the child to i->i'
, where i
is the original index and i'
is the new index. I’m using this for testing; the children are set to draw this string on themselves along with their coordinates.
Unfortunately, it does not work correctly, or rather it works erratically. Of the nine tiles in my test case, three don’t seem to have getChildDrawingOrder
called at all: the string I mentioned above is null
. Of the rest, at least one is being drawn out of order despite getting the correct index passed to it.
Here’s a picture (in the TOP
orientation):
Notice that (0,2), (1,2), and (2,1) are all listed as NULL
, and therefore getChildDrawingOrder
appears to have never been called for them. Also note that (1,0) is drawn on top of (1,1), even though both its i
(3) and i'
(1) are less than (1,1)’s (4 and 4, respectively).
Here’s the code from getChildDrawingOrder
:
@Override
protected int getChildDrawingOrder(int childCount, int i)
{
TileView ch = (TileView)getChildAt(i);
ch.order = "Called"; // this string is drawn on my children
int gx, gy; // the "true" x,y for the current rotation,
// where 0,0 is the top corner
switch (rotation)
{
case TOP:
gx = ch.x();
gy = ch.y();
break;
case LEFT:
gx = (width()-1-ch.x());
gy = ch.y();
break;
case RIGHT:
gx = ch.x();
gy = (length()-1-ch.y());
break;
case BOTTOM:
gx = (width()-1-ch.x());
gy = (length()-1-ch.y());
break;
default:
gx = ch.x();
gy = ch.y();
}
int row = gx+gy; // current row
if ( row == 0 ) // row 0 is always just the top corner and 0
{
ch.order = new String(i+"->0"); // string set to i->i'
return 0;
}
else
{
int mx = width()-1, // maximum x value
my = length()-1, // maximum y value
mrow = mx+my, // maximum row
min = Math.min(mx, my), // minor axis length
maj = Math.max(mx, my), // major axis length
retn; // for storing the return value
// inside the top corner
if ( row <= min )
{
// Gauss's formula to get number of cells in previous rows
// plus the number for which cell in this row this is.
retn = row*(row+1)/2+gy;
}
// in the middle
else if ( row <= maj )
{
// Gauss's formula to get number of cells in top corner
// plus the number of cells in previous rows of the middle section
// plus the number for which cell in this row this is.
retn = min*(min+1)/2+min*(row-min)+gy;
}
// bottom corner
else
{
retn = (min+1)*(min+2)/2 // cells in the top corner
+ min*(maj-min) // cells in the middle
+ (mrow-maj)*(mrow-maj+1)/2 // total cells in bottom triangle
- (mrow-row+1)*(mrow-row+2)/2 // less cells after this one
+ gy // which cell in this row
- (row-maj) // to account for gy not starting at zero
;
}
ch.order = new String(i+"->"+retn); // string set to i->i'
return retn;
}
}
Can anyone shed some light on what’s going on? Why isn’t getChildDrawingOrder
being called for those three tiles? Why is (1,0) drawn in the wrong order, even though getChildDrawingOrder
is called on it?
OK, figured it out by looking at the Android source code. I had the mapping of
getChildDrawingOrder
: thei
passed is “which child should I draw i th?” not "when should I draw child i?" The reason for theNULL
s is because those children were being drawn before their owni
was passed.I changed my code to figure out the order for all children during the
onMeasure
pass, saving that in aSparseIntArray
, and then just returned that fromgetChildDrawingOrder
. This works.Back-calculating the index in the
getChildDrawingOrder
function, by the way, is a bad idea unless you want to rely on the order in which the children are declared. Because if you don’t rely on that order, you have to walk through the list of children to find the one that has the appropriate x and y values, which means you have to walk through the list of children for each child. That’s an O(n²) operation (read: fairly inefficient). The mathematics are also reasonably complicated.Here is a simple example that shows how to override
getChildDrawingOrder
The RelativeLayout class:
The xml layout
What it looks like
Pictured on the left is the starting drawing order, which is the default based on the xml layout:
So from first to last (or think bottom to top) that is: Red, Green, Blue. Here is the log dump of
getChildDrawingOrder
being calledIn the middle, after our first tap, the order changes to Green, Blue, Red
And, on the right side shows us what it looks like after our second tap since the order change to: Blue, Red, Green.
Any tap after this pretty much loops it back to the original order where blue was last to be drawn due to the modulus calculation
HTHs!