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.

Why piracy in Android is not that important

Many people complain about piracy on software. A lot of mobile developers complain about piracy on Android. I was never too worried myself, this is why.

It is true, piracy exists. As a matter of fact, when we launched Chalk Ball at The Pill Tree, it got cracked in a couple of weeks. We were checking for blog reviews at a time and we found it, so it was even easy to find.

Cracking an Android app is not very complicated, but it is not trivial either. If someone takes the effort to do it, it means you are already in the way to succeed. Having your apps cracked is something to be proud of. So if it happens to you, don’t get angry, be proud of your work.

The cracked version was almost complete, just facebook integration was broken. There you have a tip: Since the signature can not be recreated, If you want your app to be really hard to crack, use facebook single sign in to login into your app.

Now think about the effort the user has to make to install an app from somewhere that is not Android Market.

First you have to seek and check an option labeled “Unknown Sources”. Many users will not do that, simply because is scary, and it is true, you are installing apps from untrusted sources anyway, if they cracked a game they can put a trojan inside it as well, right?

Secondly, downloading it and installing from your Android device is usually not trivial, you may need to download it in your computer, copy it to the sdcard, and then open a file browser to install it. Not complicated, but long and boring.

It is all about the cost of opportunity. If someone is about to install an application from an unknown distributor on the internet -having a free version that is enough for a few hours of play- and the process is going to take you more time than using the official channel, to save scarcely 2$, then is not a lost sale. This person was not going to buy it anyway.

Given that the problem with mobile games is mainly the visibility, each install -even if it is from a piracy source- is going to help you. That person is going to show the app to other people, and since there was an extra effort required, he is more likely to show it than those who just downloaded the free version. In the end, it is some sort of advertising.

These are my reasons to state that it is not a lost sale, and also that it is some sort of advertising. However, you may not be convinced by my arguments, then I’ll tell you about the ultimate tool to battle piracy on Android. Ready?

It is called the GetJar Gold program. They give paid applications for free, and you know what? They pay you per download. Sounds crazy, I know, but it works. They are a well known source and they have the latest updates since they work with the developers.

Once your app is part of GetJar Gold, piracy is meaningless. You just need to install their App Store and from there on, you just download the gold apps for free, almost as easily as from Android Market.

Using GetJar has a higher opportunity cost for a user than using the market, but less than downloading an apk from the Internet. So still, many people will get your app from Android Market. You are offering the people that is up to make an extra effort to get the app for free a legal alternative to piracy.

Everyone wins, piracy loses.