Reputation: 14841
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:
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.
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) ?
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
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
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