Displaying items in a grid with a header

The problem

Let’s say you want a layout that is a grid of items with a header. Something like this:

fashiolista profile

Displaying items in a grid is easy, we have GridView for that.

Displaying a list of items with a header is easy, we have setHeaderView on ListView for that.

The problem is when we want to show items in a grid with a header, since GridView does not support headers and ListView does not support columns.

This is a common problem and there are a few suggestion on StackOverflow, but none of them goes further than some guidelines. I did implement it and I want to share it so you don’t need to reinvent the wheel

From the architectural point of view, the solution is to use a ListView with a special adapter that displays the entries as separated columns.

How the code should look like

The code at activity level when you configure the view is like this:

ListView listView = (ListView) findViewById(R.id.listView);
listView.addHeaderView(createHeaderView());

adapter = new GidViewWithHeaderExampleAdapter(this);
adapter.setNumColumns(2);
listView.setAdapter (adapter);

Note that you add the header to the list view as a normal header, but set the number of  columns to the Adapter.

Extending from the right adapter

The adapter itself has to extend from GridViewWithHeaderBaseAdapter and implement some methods that are slightly different from the ones in a normal adapter.

Integer[] mArray = new Integer[] {1,2,3,4,5,6,7,8,9,10,11,12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};

private LayoutInflater mInflater;

public GridViewWithHeaderExampleAdapter(Context context) {
    super(context);
    mInflater = LayoutInflater.from(context);
}

@Override
public Integer getItem(int position) {
    return mArray[position];
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public int getItemCount() {
    return mArray.length;
}

@Override
protected View getView(int position, View v) {
    if (v == null) {
        v = mInflater.inflate(R.layout.simple_list_item, null);
    }
    TextView tv = (TextView) v.findViewById(R.id.text);
    tv.setText(String.valueOf(getItem(position)));
    return v;
}

So getItem and getItemId are the same as for normal adapters.

On the other hand getItemCount is a replacement method for getCount and getItemView is a replacement for getView. both are implemented on the GridViewWithHeaderBaseAdapter and are the methods that do the magic.

Handling click on items

The adapter creates a LinearLayout and adds child views to it for each row, creating a single list item for each row.
Then, of course, OnItemClickListener is broken, because each list item is a row. You have to use GridItemClickListener instead.

@Override
public void onGridItemClicked(View v, int position, long itemId) {
    // TODO: Handle item click here
}

Advantages versus TableLayout

What is the advantage of this solution versus just a TableLayout on a ScrollView?

The advantage is that the Adapter recycles the views and it is a lot more efficient in memory and in execution.
The other advantage is that the Adapter is dynamic. You can not hardcode a TableView without knowing the items beforehand. Sure, you can build it programatically, but if you are getting that far, you probably want to get one step further and build an adapter, which is what I made.

GridViewWithHeaderBaseAdapter is about 150 lines of code, so feel free to look at the source for inspiration and/or modify it for your own purposes, it is under BSD license.

You can check the project on github. It includes the example.

MTG Tracker 5.2 Released

A new set of Magic the Gathering is out, and a new version of MTG Tracker comes with it. This time it is Magic 2014 Core Set.

With this version the playtesting feature includes Library manipulation, the key part that was missing (and that many people kept asking on the reviews on Google Play)

Also, a subtle change in the UI of the advanced search. It was like this:

Advanced search example

The icons for color selection were bad, it was not trivial to see when they were selected and they had a very old design and did not fit with the new tendency towards flat design, so I updated them.

The sort of change that is barely noticeable, but it works. It looks like this now:

new search

Full ChangeLog of MTG Tracker 5.2:

  • Magic M14 Core Set
  • Library manipulation on playtest
  • Fixed reset of playtest on device rotation
  • Fixed problems with format search on Spanish / German / French
  • UI update on Advanced search

cardshark_header

Codemon 3.0 – The Arcanes

It’s been a long time without updates on Codemon, finally, the wait is over.

Players asked for more Codemons and evolutions… well, players always ask for more creatures and evolutions in games like this.

There were a few limitations to give them what they were asking for.

In first place, the barcodes always generate the same result, by design. That makes impossible to make more creatures from scans.

Secondly, Codemons were already able to reach level 100 without any evolution, so that discards the easy way. In fact making the Codemons evolve will make it look more like a copy of Pokemon, and the idea is not to copy, but to just be inspired.

framed_new_home_screen_with_summon

We found a quire interesting solution: The Arcane Codemons.

Those were the original Codemons and they are extinct. That is why they can’t be scanned. Arcane Codemons need to be summoned.

To perform a summon, you need to use 2 Pure (a.k.a. having only one element) Codemons, they will be sacrificed and the Arcane will be summoned. Well, it is not that simple, there are several types of summoning and it depends on the Codemons involved.

framed_arcane_summon

This is a great mechanic, it puts more Codemons into the game, and it does it in something that “resembles” an evolution and at the same time does not break the existing game mechanics and is also different from other games.

The other big improvement of this version is cloud sync of accounts. That sounds easy, but it has been quite a nightmare. I needed to rewrite all the communication layer, the server side and the client cache… pretty much half of the game has been rewritten to make it work.

But all the coding and testing is done and it is available on Google Play, so grab it and play!