Xamarin and Android – Specific Solutions to Specific Problems #2

Welcome to the second post in my Specific Solutions to Specific Problems series. Hopefully somebody is getting some benefit out of these but even if nobody is reading it, the process is pretty cathartic anyway…

These are a handful of issues that I encountered in my second week on Monodroid. These are the solutions I discovered to my problems.

1. When I am loading a ListView, how do I show a loading message before I actually fill the element with items?

There’s a few ways to do this. One choice is to use an entirely different list adapter while loading which will give you the most control over the look and feel of the UI elements. The other solution which I use regularly is to use the android-empty-id-view and set the list view source to an empty list while loading.

The list view and associated empty view look like the following. Remember to set the visibility of the empty view element to gone. Android will take care of showing or hiding the element at the correct times:


<ListView
    android:id="@+id/drugs_list"
    android:layout_width="fill_parent"
    android:layout_below="@+id/patientName"
    android:layout_height="wrap_content" />
<TextView
    android:id="@+id/empty"
    android:text="No drugs found"
    android:layout_width="fill_parent"
    android:layout_height="120dp"
    android:layout_below="@+id/patientName"
    android:gravity="center"
    android:visibility="gone" />

In order to show the empty view I simply assign the list adapter with an empty collection. This will cause Android to show the empty view with the text “Loading items…”:


var itemsList = FindViewById<ListView>(Resource.Id.items_list);
var emptyTextView = FindViewById<TextView>(Resource.Id.empty);

emptyTextView.Text = "Loading items...";
itemsList.EmptyView = _emptyTextView;
itemsList.Adapter = new MyListAdapter(this, new List<Item>());

// Perform some long running operation to get items
var items = await ItemService.GetAllItems();

itemsList.Adapter = new MyListAdapter(this, items);

// Set empty to no drugs in case items.Count == 0
emptyTextView.Text = "No items found";

After I execute my long running operation to retrieve the data I set adapter again with the new collection. I also set the empty view message to be “No items found” so if necessary the system can actually show the existence of an empty view.

2. Every time I debug or deploy my application from Visual Studio I lose all of the application state on the device. How do I deploy without losing my state?

The application we are developing stores state in an SQL Lite database. Every time I debugged or deployed the application, the state would be wiped and the program would insist it was being run for the first time. In order to tell Xamarin to not wipe the state every time I checked the following option under Tools > Options > Xamarin > Android Settings:

fastdeploy

Once this setting is checked the application state won’t be reset between debugging sessions. In order to reset the state I simply uninstall the app and the next deployment will have a fresh start.

3. I seem to be using the exact same code everywhere to show a progress dialog. Can I work some lambda and func magic so I can have a reusable piece of logic that I call throughout the application?

No problem. I have created a static function that returns a Task so I can await on it. It takes the alert title, the message and an awaitable Func representing the long running operation.


async public static Task ShowProgressDialog(Context context, string title, string message, Func func)
{
    using (ProgressDialog progressDialog = ProgressDialog.Show(context, title, message, true))
    {
        try
        {
            await func.Invoke();
        }
        finally
        {
            // Now hide the progress dialog
            progressDialog.Dismiss();
        }
    }
}        

I would then call this like so:


await AppHelper.ShowProgressDialog(this,
"Please wait...",
"Logging in...",
async () => await auth.LoginUser(authInfo));

I use this throughout the application and there is no need to repeat the same boiler plate code everywhere I want to show a progress dialog.

4. When I use a custom theme to change the window title any progress dialogs I create look like they have no theme applied. How can I force a progress dialog to use a specific theme?

We have several activities where I have re-defined the style to have a custom title bar. When I show a progress dialog from these activities the dialog will not be Holo and instead appear as if no theme has been applied. I found several posts that suggested the use of a ContextThemeWrapper:


Context dialogContext = new ContextThemeWrapper(this, AlertDialog.ThemeHoloDark);
ProgressDialog dialog = ProgressDialog.Show(dialogContext, "Important...", "Logging in...", true);

I couldn’t get this to work as expected however and instead I had to instantiate a ProgressDialog as the constructor allows for the specification of a theme. When I updated the static method I had created earlier, it looked like this:


async public static Task ShowProgressDialog(Context context, string title, string message, Func<Task> func)
{
    using (ProgressDialog progressDialog = new ProgressDialog(context, AlertDialog.ThemeHoloDark))
    {
        progressDialog.Indeterminate = true;
        progressDialog.SetTitle(title);
        progressDialog.SetMessage(message);

        progressDialog.Show();
        try
        {
            await func.Invoke();
        }
        finally
        {
            // Now hide the progress dialog
            progressDialog.Dismiss();
        }
    }
}

The progress dialog now has the Holo Dark theme applied to it everywhere in the application that I use this function from.

Leave a Reply

Your email address will not be published. Required fields are marked *