Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getChildDrawingOrder called/used erratically?

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

Image of getChildDrawingOrder failure

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?

like image 367
KRyan Avatar asked Jan 24 '13 15:01

KRyan


1 Answers

OK, figured it out by looking at the Android source code. I had the mapping of getChildDrawingOrder: the i passed is “which child should I draw i th?” not "when should I draw child i?" The reason for the NULLs is because those children were being drawn before their own i was passed.

I changed my code to figure out the order for all children during the onMeasure pass, saving that in a SparseIntArray, and then just returned that from getChildDrawingOrder. 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.

like image 76
KRyan Avatar answered Oct 18 '22 02:10

KRyan