ActionBar

App UI / UX – Part 4

Previously in this series we’ve looks at improving the UI of our app to display temperature and humidity obtained from a TI SensorTag device, but in this article we’ll turn our attention to the UX.

Our app architecture is actually really simple: We have a single Activity which displays either of two different Fragment types depending on whether or not we are connected to a SensorTag device.

The first thing that we’re going to look at is the ActionBar. The ActionBar is actually really straightforward, so what can we improve? If you look app when we are displaying the temperature and humidity (i.e. the DisplayFragment is visible), there is actually a refresh option shown and this is only really relevant when we want to initiate a device scan when showing the DeviceListFragment. In the DisplayFragment we actually want to provide an option to disconnect from the current SensorTag device and return to the DeviceListFragment in order to select a different device.

So we need to rename the menu definition res/menu/main.xml to device_list.xml define a new menu definition which will be used in DeviceFragment:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
	  xmlns:tools="http://schemas.android.com/tools"
	  tools:context="com.stylingandroid.bluetoothle.MainActivity">

	<item
		android:id="@+id/action_disconnect"
		android:orderInCategory="100"
		android:showAsAction="always"
		android:icon="@android:drawable/ic_menu_close_clear_cancel"
		android:title="@string/action_disconnect"/>
</menu>

We now need to remove the menu loading logic from the Activity and move it to the individual Fragments:

@Override
public View onCreateView(LayoutInflater inflater, 
	ViewGroup container,
	Bundle savedInstanceState) {
	.
	.
	.
        setHasOptionsMenu(true);
	return view;
}

@Override
public void onCreateOptionsMenu(Menu menu, 
	MenuInflater inflater) {
	inflater.inflate(R.menu.device_list, menu);
	mRefreshItem = menu.findItem(R.id.action_refresh);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
	if (mRefreshItem != null) {
		mRefreshItem.setEnabled(mListView.isEnabled());
	}
}

I’ll only display the changes to DeviceListFragment here, but you can see the changes to DisplayFragment in the sources.

It is important to remember to add setHasOptionsMenu(true); to onCreateView() in order to enable menu setup from the Fragment.

The one thing that I haven’t moved from the Activity is the onOptionsItemSelected() method which handles MenuItem clicks. This may, at first, seem a little odd, but let’s consider what the two menu options do. The “Refresh” item will initiate a new device scan; and the “Disconnect” will close the connection to the SensorTag. Both of these operations are effectively causing a change to the state machine, and the state machine is managed by the Activity. It doesn’t matter to the Activity which menu items are available – it just handles whatever events are triggered by the user. The currently visible Fragment determines which events the user can trigger in that context by only displaying relevant options to the user.

So the after a little bit of refactoring to create a sendMessage() method to reduce duplicate code (see the source for the sendMessage() implementation) onOptionsItemSelected() method looks like this:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
	// Handle action bar item clicks here. The action bar will
	// automatically handle clicks on the Home/Up button, so long
	/ as you specify a parent activity in AndroidManifest.xml.
	int id = item.getItemId();
	if (id == R.id.action_refresh) {
		if (mService != null) {
			startScan();
		}
		return true;
	}
        if(id == R.id.action_disconnect) {
            Message msg = Message.obtain(null, 
		BleService.MSG_DEVICE_DISCONNECT);
            sendMessage(msg);
        }
	return super.onOptionsItemSelected(item);
}

The code to disconnect is new – we send a message to the BleService to disconnect, and this will trigger a state change which will cause the DeviceListFragment to be displayed instead of DisplayFragment once the connection status changes.

If we run this we can now see that the icons in the ActionBar change depending on the context and the behaviours provide a clean mechanism for the user to cause state changes only in the appropriate places

While the flow is certainly improved, after watching the video, one obvious area which can be improved is that it is not clear when the device scan ends. In the next article we’ll look at how we can indicate to the user that a scan is running.

The source code for this article is available here.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

2 Comments

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.