Using the Android GridView as a DataGrid

As part of the app I developed for AngelHack I wanted to display some data in a classic grid style. Android offers a GridView as part of the basic SDK, however if you look at all the samples they only show how to use it as a grid of images, essentially they only demonstrate usages where all columns show the same thing, usually columns of photographs. It’s not too tricky to use the GridView as classic “data grid” but it did require a wee bit of thinking, so I thought I put together a quick example to show how it worked.

In many ways working with Android controls reminds me of working with WFP. They both allow a user to either create controls using an xml based designer or create them directly in code. The way data-binding works is similar too, for Android controls you plug in a “data adapt” which controls the creation of the child controls that will handle displaying the data. To use the GridView we need to create an adapter to which is responsible for telling the GridView how the data source should be rendered. Initially for the data source I created mine own “DataTable like object”, but then after saying d’oh and slapping my head a couple of times I realized there was no need to create “DataTable like object” when I could just use a DataTable.

The first step is to create the adapter, to do this you derive from BaseAdapter<T> where T is the type of element that will appear in the data cells, for simplicity I made T string. Next you need to provide a constructor that takes a Activity (which is an Androidy type thing for accessing the gui context) and the data source itself, in our case a DataTable. This means the top of the adapter class looks something like:

public class DataTableGridAdapter : BaseAdapter<string> {
    private Activity context = null;
    private DataTable itemTable = null;
    public DataTableGridAdapter(Activity context, DataTable itemTable) : base ()
    {
        this.context = context;
        this.itemTable = itemTable;
    }

Next you need to override the count property to return the number of cells (the rows plus one gives room for column headers):

public override int Count
{
    get { return (itemTable.Rows.Count + 1) * itemTable.Columns.Count; }
}

Then you need to override the GetView method, which is the method that creates the control that will be displayed for the cell:

public override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
{
    // find the current row number, need to test we're in the header
    int row = GetRow(position);
   

    // Get our text for position
    var item = PositionToString(position);

    // find if we have a control for the cell, if not create it
    var txtName = convertView as EditText ?? new EditText (context);
   

    // if the row is a header give it a different colour
    if (row == 0) {
        txtName.SetBackgroundColor( new Color(0x0d,0x12,0xf5));
        txtName.SetTextColor (new Color(0x0,0x0,0x0));
    }
   

    //Assign item's values to the various subviews
    txtName.SetText (item, TextView.BufferType.Normal);
   

    //Finally return the view
    return txtName;
}

The important line is '”var txtName = convertView as EditText ?? new EditText (context);” here we see if the control to display the cell has been created or not, if not we create one. In this case we use an EditView, a control build-in to the Android SDK for editing text. This is because we want our grid to be editable, if we wanted a readonly grid we’d use a TextView instead. We we wanted to display complex, more structured data we could return a user defined control instead. The rest of the method is just about setting up the various properties of this control, including it’s content, which we obtain from our data source via the PositionToString method. The position is the identifier of the cell in the grid, the data grid doesn’t provide a row and column id, just one continuous position id, fortunate with just a little maths we can translate a position to a row and column pair. Below shows how we do this and also the implementation of “PositionToString”.

private int GetCol(int pos){
    int col = pos % (itemTable.Columns.Count);
    return col;
}
private int GetRow(int pos){
    int row = pos / (itemTable.Columns.Count);
    return row;
}
private string PositionToString(int pos)
{
    int row = GetRow(pos);
    int col = GetCol(pos);
    if (row == 0) {
        return itemTable.Columns[col].ColumnName;
    }
    if (itemTable.Rows [row - 1][col] != DBNull.Value) {
        return itemTable.Rows [row - 1][col].ToString();
    }
    return "";
}

There’s a few wall-dressing method that need to be implement, but I’m put the code on github, so you can take a look at them here.

To use the “DataTableGridAdapter”, simply add a GridView to one of you’re layouts, a .axml file, then you’ll need to configure the data adapter in the Activity that uses the layout. To do this first recover a reference to the GridView and store it as a member:

gridView = FindViewById<GridView> (Resource.Id.offerGridView);

Once you’ve created your DataTable and populated it, binding to the grid as so:

var itemListAdapter = new Adapters.DataTableGridAdapter (this, dataTable);
gridView.NumColumns = dataTable.Columns.Count;
//Hook up our adapter to our ListView
gridView.Adapter = itemListAdapter;

The slightly annoying thing about this approach is you need to explicitly set the number of columns the grid has and you do need to keep updating the number of columns each time a new column is added or removed.

And that’s basically it, take a look at the full working example here, and feel free to send a pull request if you have an improvements.

Developing For Kindle Fire with Mono C#/Xamarin Studio

As I’ve already mentioned last weekend I took part in AngelHack and took the opportunity to learn how to develop for an Android device (in this case a Kindle Fire) using C# and Xamarin Studio. The experience was a bit mixed, overall I think the folks at Xamarin have done a good job in smoothing the experience when targeting mobile devices, but there’s still a few rough edges. Here’s a run though of what I thought:

The installer for Xamarin Studio works well and does a good job of downloading and installing the large number of different components need to develop for various devices. However, it’s slow, not so much the download which came down surprisingly quickly but the installation took ages with the window’s installer promising “6 seconds remaining” but in reality I was still waiting minutes later.

Once I was past the installer I downloaded the tasky sample and tried to open in Visual Studio but all the projects refused to load. I tried both with VS2012 and the “Shell” version of VS2010. I think this is because I’d installed the Indie version of Xamarin Studio, which doesn’t have Visual Studio integration, so in this case my fault, but one might have reasonable expected a clearer error message about why the projects weren’t loading. This is especially true as the Visual Studio 2010 shell seems to have been installed by the Xamarin Studio installer (at least I have no memory of trying to install VS2010 shell on this machine).

Once I open the tasky project files in Xamarin Studio things started looking up. I soon got tasky compiling and working the emulator. However by default the Android emulator is setup to emulate a phone and I wanted to target a Kindle Fire, there are various other configurations of the emulator but none of them appropriate for Kindle development. Amazon has detailed instructions about how to setup your development environment to target kindle fire, however there only for folks using eclipse. Xamarin Studio installs the same set of tools described in the article but there installed under C:\Users\<username>\AppData\Local\Android\android-sdk in Windows, so it took me a while to figure out where they were (C:\Users\<username>\AppData\Local seems an odd place to install them to).

UPDATE: I’ve just installed XamarinStudio on another box and I noticed the installer asks you were you want to put the Android SDK, obviously this didn’t strike me as important information at the time and I just did “next > next > next”.

Once you’ve found the tools, you need to use the “SDK Manager.exe” to installing the following extra bits for Kindle Fire development (you’ll need to add a reference to “http://kindle-sdk.s3.amazonaws.com/addon.xml” under “Tools > Manage Add-on Sites … > User Defined Sites” before they are available). Then install:

- “Kindle Fire Device Definitions”

- “Kindle Fire USB Driver” (this doesn’t really install the driver it just downloads it, you then need to find the exe and double click it)

- “Intel x86 Emulator Accelerator (HAXM)” (again doesn’t install automatically, you need to double click the exe, and it wasn’t supported by my processor, but well worth doing if your processor does support it)

Before I had installed all of this a I tried “side loading” (directly dragging and dropping my compiled .apk via a USB connection) on to a kindle I’d borrowed. This didn’t work and the app just closed without display an error message. I strongly suspect that this was because I hadn’t uncheck the “Use shared runtime” option, but I haven’t tried “side loading” since. After I installed the “Kindle Fire USB Driver” I just let Xamarin Studio handle the deployment and it just worked.

Working with the Android emulator is slow and the emulator is a little crashy. The emulator seems especially prone to crashing after sleeping/unsleeping my laptop by closing/opening the lid. Although my laptop doesn’t support the “HAXM” accelerator checking the the “Use Host GPU” under the “AVD Options” ( AVD is Android Virtual Device) did seem to speed things up a lot. Starting the emulator and deploying to it is the slowest bit. There is a “Quick deploy” option which is check by default but that just produced strange error messages for me and after a bit of googling the suggested fix seemed to be “if it ain’t working turn it off”. Unchecking the “Use shared runtime” also slows things enormously, so make sure it’s checked during development. I had one other problem with the emulator which turned out to be a user error. When I first use the emulator it would go straight to the start screen of the app. Later, for reason I don’t understand, it started to show the lock screen of the Android OS, along with a “50% Loaded” message. I wasted a lot of time waiting of the “50% Loaded” to disappear, but after several restarts I realized the “50% Loaded” message was a red herring the emulator was fully loaded, I just need to unlock it by dragging the flashy circle over the lock sign.

Another problem that gave me a slow start to my development was that although the tasky project worked fine on both really hardware and the emulator I couldn’t for the life of me get a project I’d created via “File” > “New Project” to loaded into the emulator (I never bothered trying real hardware). When I tried to deploy, the app would seem to deploy but then exit straight away, I tried putting a break point in the first screen, it was never hit. I’m not sure what the problem was, I noticed the app didn’t have a manifest so I tried creating one, but it didn’t seem to make any difference. In the end I just gave up and used a copy of the tasky project to create my app.

The final glitch was related to libraries. I wanted to parse some JSON, easy right? As it turns out a bit of a struggle. I wanted to use newtonsoft.json.dll to do the job, but it you can’t use any old .NET binary to target an Android app, if the binary hasn’t been build specifically for Mono/Android you end up with weird error messages about object being from the wrong mscorlib.dll. I looked around for a version of newtonsoft.json.dll that targeted Android, but only found some old source, no binaries. Next I tried to use “system.runtime.serialization.json.datacontractjsonserializer” but this isn’t included in the Android libs. The one that I found that actually did the job was “ServiceStack.Text”, it’s true that service stack doesn’t provide binaries for Android, but it does at least supply a csproj in the main repo to build the source to target Android, so a big thanks to Demis Bellot for providing this.

UPDATE: Dave Thomas just pointed out that newtonsoft.json.dll is available in the Xamarin “Component Store”: http://components.xamarin.com/

Despite the problems I’ve laid out here overall I was very pleased with the experience of using Xamarin studio to target Android and now I have some Android hardware I may go on to build some apps. It’s true that I did encounter a lot of problems, but I hope to create bug reports and help get these solved. I didn’t add F# into the mix this time around, but I shall definitely be trying it out when it comes to building my next app.