3

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);
}
Sébastien
  • 13,831
  • 10
  • 55
  • 70

2 Answers2

2

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.

Dalmas
  • 26,409
  • 9
  • 67
  • 80
  • I think this involves drawing bitmaps on a Canvas. I thought drawing a bitmap in 2 different spots of a Canvas would result in storing the bitmap pixels twice in memory, thus causing basically the same memory usage as Option 1 ? Am I wrong ? – Sébastien Sep 09 '12 at 11:37
  • No, in Option 1 you have two big bitmaps in memory (the loaded PNG resource and the surface bitmap), but in Option 2 you will have the surface bitmap in memory, and the 5 small bitmaps used to draw it. This is a considerable difference on large screens. – Dalmas Sep 09 '12 at 15:58
  • Thanks, I will definitely try this implementation and RAM-benchmark it against Option 1 and 2. Now, reading the answers there (http://stackoverflow.com/questions/1243433/android-difference-between-surfaceview-and-view ) makes me think I could use a View instead of a SurfaceView, as I don't plan to use neither another thread to do the drawing, nor animations. What do you think ? – Sébastien Sep 10 '12 at 12:31
  • In this case a custom View is probably better than a SurfaceView, because a View use less resources. – Dalmas Sep 10 '12 at 13:57
0

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.

Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • I am not too familiar with GridView, but I think it is overkill: I don't need the scrolling or the ability to dynamically add objects to the grid. Plus there would still be hundreds of ImageViews inside the GridView. – Sébastien Sep 09 '12 at 11:28