Writing a custom layout for board games in Android — the BoardLayout!!!

Shukant Pal
3 min readFeb 9, 2019

--

Photo by rawpixel on Unsplash

Ever tried using TableLayout when building a board game for Android — don’t, it’s horrible, from my experience!

In this tutorial, we are going to code a layout called BoardLayout which is like GridLayout in JavaFX — each child view can be placed in a cell, given its coordinates.

To build our custom layout, we need to inherit from ViewGroup and override two methods — onMeasure and onLayout.

Implementing onMeasure()

onMeasure takes two parameters — the width and height measure specs. Measure specs are basically specifications that tell the size a parent view wants to given to a child encoded in an int that contains the mode and size. The mode gives the type of constraint and size gives the numerical value.

onMeasure is also supposed to call measure on each of its children — as BoardLayout itself is a parent view.

By the way, there are three MeasureSpec modes:

  • MeasureSpec.UNSPECIFIED — this means that the parent view is not imposing any constraints on the child. The child view should set its “wanted” size.
  • MeasureSpec.EXACTLY — this means that the parent view wants the child to be exactly the size it provided (in the measure-spec).
  • MeasureSpec.AT_MOST — this means that the parent view is allowing the child to be any size up to the size it provided (in the measure-spec).

The methods MeasureSpec.getMode and MeasureSpec.getSize methods allow you to get the values of the mode and size. Our onMeasure method should be able to handle all three modes.

onMeasure example code

In the code snippet above, we expect that at least one parameter — width or height — should be given exactly. If neither are given exactly, onMeasure throws a exception: which is okay, as long as you specify the width and height of the BoardLayout in its layout-params. Given one parameter, the other can be found by proportion as: width:height::columns:rows. Once we have found our width and height, we call measureExact that finds the width and height per cell, and also calls measure on all child views. In the end, we set our measured dimensions by calling a method in ViewGroup.

Implementing onLayout()

onLayout gets five parameters — changed, left, top, right and bottom. The first one — changed — tells whether anything changed since layout was called before and we won’t be using that here. The other four give the sides of this view on the screen, in pixels. Based on that, we are supposed to call layout on each of our children with the position we given them (via setLayoutParams and initializing row and col in a BoardLayout.LayoutParams ).

// Should be LayoutParams inside the BoardLayout class
public static class BoardLayout.LayoutParams {
public int row;
public int col;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int width = right - left;
final int height = bottom - top;
final int cellWidth = width / mCols;// mCols should be defined
final int cellHeight = height / mRows;// mRows too
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getViewAt(i);
final BoardLayout.LayoutParams params = (BoardLayout.LayoutParams) child.getLayoutParams();

final int row = (params != null) ? params.row : 0;
final int col = (params != null) ? params.col : 0;
final int childX = left + params.col * width;
final int childY = top + params.row * height;
final int childWidth = child.getMeasuredWidth();
final int childHeigh = child.getMeasuredHeight();
// these should've initialized in onMeasure when we
// called measure on our children
child.layout(childX, childY, childX + childWidth, childY + childHeight);// again left, top, right, bottom
}

This should be simple enough — all we do is calculated the X and Y (or left and top) positions of our children and then get the right and bottom values by adding their width and height. In the end, we just call layout on them and our job is done.

You have just learnt how to write a custom layout in Android — a custom View in its self. For a more flexible implementation, see my project Board.Android.UI. I’ve added support for padding, cell transformations, and specific positioning within cells (here we set the X and Y positions of each each to the top-left corner of each cell).

--

--