Skip to content

HexGridView

gonodono edited this page Jun 29, 2024 · 2 revisions

Package: com.gonodono.hexgrid.view

HexGridView is the View version of the library's grid UI. It's actually a ViewGroup, and is designed to allow adding children either in layout XML, or in code through a specialized interface.

XML setup

HexGridView can be set up completely through its layout XML, and it recognizes several special attributes on itself and its children in order to allow for that. All attributes in the snippet below belong to the library, and are shown with their default values. I'm not a big fan of projects that slap a common prefix and underscore on every custom attribute, so I've avoided that here, but several of them do share somewhat descriptive prefixes that have been added in an attempt to avoid name collisions with the platform and other standard libraries.

<com.gonodono.hexgrid.view.HexGridView
    …
    app:gridRowCount="0"
    app:gridColumnCount="0"
    app:insetEvenLines="false"
    app:enableEdgeLines="false"
    app:fitMode="fitColumns"
    app:crossMode="alignCenter"
    app:hexOrientation="horizontal"
    app:cellStrokeWidth="0dp"
    app:cellStrokeColor="@color/black"
    app:cellFillColor="@color/transparent"
    app:cellSelectColor="@color/light_gray"
    app:cellIndicesShown="none">

    <com.gonodono.hexgrid.view.CellStateView
        …
        app:layout_cellRowAndColumn="0,0"
        app:layout_cellIsVisible="true"
        app:layout_cellIsSelected="false"
        app:layout_hexBackgroundEnabled="false"
        app:layout_hexBackgroundColor="@color/transparent"
        app:layout_hexBackgroundInset="0dp" />

  </com.gonodono.hexgrid.view.HexGridView>

The attributes on the <HexGridView> should be rather self-explanatory, given the information already provided. The layout_ attributes on the child are in two groups: those that start with cell that denote and affect the relevant grid cell, and those that start with hexBackground, which allow for a HexDrawable to be set as the child's background.

HexDrawable is a simple class that sets a hexagonal Outline – which allows it to cast appropriately shaped shadows – and fills it with a solid color. The class is open to permit further customizations; e.g., using a Shader instead of a solid color, inserting a RippleDrawable, etc.

Any kind of normal child View can be used with HexGridView. The library also offers the custom CellStateView, which can be used to apply cell attributes to the grid without adding an actual View instance at runtime. Since no child is added, the hexBackground attributes are useless on a <CellStateView>. The XML above is only for show.

Code setup

Setup in code is a little different than you might expect from the available attributes. As previously mentioned, the row count, the column count, which lines to inset, and whether to enable edge lines are the four things that define a Grid, and HexGridView handles those things altogether through its single var grid: Grid property, since changing any of them defines a new grid anyway.

The View version's implementation of Grid is MutableGrid, which allows (only) the Grid.States to be changed for each of the predefined Grid.Addresses. (Mutable probably isn't the most appropriate descriptor for this, but Changeable or something similar would be unwieldy and confusing.) To effect this functionality, MutableGrid has an additional set indexed accessor.

val mutableGrid = MutableGrid(3, 3, insetEvenLines = true)
mutableGrid.change(Grid.Address(0, 1), isSelected = true)
hexGridView.grid = mutableGrid
hexGridView.viewProvider = HexGridView.ViewProvider { address, current ->
    val image = current as? ImageView ?: ImageView(context).apply {
        setImageResource(R.drawable.example)
    }
    image.imageTintList = when {
        mutableGrid[address].isSelected -> ColorStateList.valueOf(Color.CYAN)
        else -> null
    }
    image
}

Grids behave like Lists used with ListViews or RecyclerViews: they are inert collections that simply hold state. The HexGridView needs to be notified if you change a Grid externally, and since it's only a draw change, calling invalidate() is sufficient, so I leave that to the user. I'm not going to complicate things by defining some relay function like notifyDataSetChanged() that calls invalidate() and does nothing else. To illustrate:

hexGridView.onClickListener = HexGridView.OnClickListener { address ->
    mutableGrid.toggle(address)
    hexGridView.invalidate()
}

The MutableGrid.toggle(address: Grid.Address) extension function replaces the Grid.State at address with a copy that has the opposite isSelected value. You can do it manually, if you like, but it's tedious.

Drawable

The library also includes the HexGridDrawable class in the view package to allow a non-interactive version of the hex grid to be used wherever a Drawable can be applied. It has the same basic functionality as HexGridView, with a few differences:

  • It does not support child Views in its cells.
  • It has no click handler interface, since Drawables are inherently non-interactive.
  • It always uses its assigned bounds; i.e., it cannot wrap_content in the cross dimension.

NB: Don't confuse HexGridDrawable with HexDrawable. The latter is for use only with HexGridView, to provide shaped backgrounds for its child items.


Next: HexGrid (Compose) →

Clone this wiki locally