Sébastien
Sébastien

Reputation: 14841

How to draw a tiled game board with low bitmap memory impact

I am developing a word game similar to the most famous one. The board has 15 x 15 tiles. There are 5 possible colors for the tiles.

The board can be zoomed in. I.e. you can either have a full view of the board, or zoom in 2X.

I have implemented 2 ways to draw the board in my Activity. Both have been tested OK on my Galaxy S and on various emulators. Both seem to work fine on a vast majority of devices. BUT, they both cause issues for a minority of users (which is what I am trying to solve here).

Here's for the description of the 2 designs:

Option 1: 'big image'

Description: This is the simplest one. The board is a unique ImageView (subclassed to handle double-tap zoom and dragging). The image PNG resource is high res: approx. twice the size of the biggest screen size to avoid upscaling when zoomed in.

Issues: OutOfMemory issues that seem to occur sometimes on big screens, especially on the 10" Asus TF101 tablet.

Comments: Tests on the TF101 show that RAM usage is 3X higher than on the Galaxy S. It seems logical considering the huge scaled bitmap that has to be drawn.

Option 2: repeat 5 small images

Description: I tried to reduce the RAM used by bitmaps and to take advantage of the fact the board is just a repetition of small squares of 5 kinds. In order to make the resizing of the board view easy, I chose to make it a custom LinearLayout, containing 15 nested LinearLayout, each containing 15 images (which is certainly not the best option performance wise).

Here's what my XML layout would look like for a 4 x 4 board:

        <MyCustomLinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" >
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal" >
                <ImageView
                    android:layout_width="0dp"
                    android:layout_height="fill_parent"
                    android:layout_weight="1" >
                </ImageView>

                <ImageView
                    android:layout_width="0dp"
                    android:layout_height="fill_parent"
                    android:layout_weight="1" >
                </ImageView>
            </LinearLayout>

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal" >
                <ImageView
                    android:layout_width="0dp"
                    android:layout_height="fill_parent"
                    android:layout_weight="1" >
                </ImageView>

                <ImageView
                    android:layout_width="0dp"
                    android:layout_height="fill_parent"
                    android:layout_weight="1" >
                </ImageView>
            </LinearLayout>
        </MyCustomLinearLayout>

Issues: Some crashes or visual glitches for somes users. (I still don't have much info on this one)

Comments: Pros: of the option is that the memory footprint is optimized: only one instance of each of the 5 small bitmaps is kept in memory. Resizing the board is straightforward: Android does all the resizing of bitmaps and nested LinearLayouts when I resize MyCustomLinearLayout.

Cons: Too many views, which dimensions must be recomputed by the system on each zoom.

How can I improve Option 2 to keep a low RAM footprint, while respecting the other performance good practices (like not having hundreds of views) ?


EDIT: Final (?) implementation

Instead of a SurfaceView, I went for a simple custom View.

I was afraid that doing a lot of bitmap drawing in the onDraw() method would cause the dragging of the board to be laggy (because onDraw() is constantly called when the board is moved).

Turns out it is pretty smooth (maybe even a bit smoother than Option 2). As for the RAM usage, it is just a bit heavier than option 2 (comparison done using Eclipse Memory Analyzer), so it is satisfactory. I still have a few reports of OutOfMemory errors ("bitmap exceeded max VM heap size"), but I think it is inevitable when one uses bitmaps.

Here is the most significant code, where I draw the 15x15 bitmaps (initialized from resources in the constructor of the view) to build the game board:

public void drawTileAtCoordsOnCanvas(Bitmap bm, Canvas canvas, int x, int y) {
    _rect.set(x * _sizeOfTile, y * _sizeOfTile, (x + 1) * _sizeOfTile, (y + 1) * _sizeOfTile);
    canvas.drawBitmap(bm, null, _rect, _paint);
}

public void onDraw(Canvas canvas) {
    for (int i = 0; i < _rules.BOARD_SIZE; i++) {
        for (int j = 0; j < _rules.BOARD_SIZE; j++) {
            switch (_rules._boardOfMultiplicators.get(i).get(j)) {
            case NL:
                drawTileAtCoordsOnCanvas(_bitmapNormalTile, canvas, i, j);
                break;
            case DL:
                drawTileAtCoordsOnCanvas(_bitmapDlTile, canvas, i, j);
                break;
            case TL:
                drawTileAtCoordsOnCanvas(_bitmapTlTile, canvas, i, j);
                break;
            case DW:
                drawTileAtCoordsOnCanvas(_bitmapDwTile, canvas, i, j);
                break;
            case TW:
                drawTileAtCoordsOnCanvas(_bitmapTwTile, canvas, i, j);
                break;
            }
        }
    }
    drawTileAtCoordsOnCanvas(_bitmapStarTile, canvas, 7, 7);
}

Upvotes: 3

Views: 1204

Answers (2)

Dalmas
Dalmas

Reputation: 26547

Your second option is the best (repeating 5 images), but instead of using ImageViews it will be way more efficient to draw directly on a custom view. The performance will be significantly better.

A SurfaceView is probably the best choice.

Upvotes: 2

Ilmari Karonen
Ilmari Karonen

Reputation: 50368

I have no Android development experience at all, so take this with a grain of salt, but just based on looking at related questions and a bit of Googling, it seems to me that a GridView may be what you want.

Upvotes: 0

Related Questions