Previously in this series we’ve looked at improving the ActionBar behaviour of our temperature app, but there’s another annoyance that we can fix by tinkering with the ActionBar: When the user performs a bluetooth device discovery scan there is no visual indication that the scan is in progress, and more importantly when the scan completes. In this article we’ll add a simple animation to the refresh ActionBar item to provide this visual indication that a scan is occurring.
Animating an ActionBar item is actually pretty easy. We need to use a custom ActionView which we’ll then be able to animate. Let’s first create this custom layout:
?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_menu_refresh" style="@android:style/Widget.Holo.ActionButton" android:contentDescription="@null"> </ImageView>
It’s important to match the correct style to ensure that the paddings are the same otherwise we’ll see the refresh icon jump when the animation starts and stops.
Next we need to create the animation:
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:toDegrees="360" android:pivotX="50%" android:pivotY="50%" android:repeatMode="restart" android:repeatCount="infinite" android:duration="@android:integer/config_longAnimTime" android:interpolator="@android:anim/linear_interpolator"> </rotate>
This will perform a full rotation around the centre point, and will repeat indefinitely.
Next we need to inflate the layout in onCreateView()
method of DeviceListFragment:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { refreshActionView = inflater.inflate( R.layout.refresh_action_view, null); . . . }
The only thing remaining is to actually set things running.
The refresh action item in the ActionBar is a simple layout which is inflated from the menu resource which we defined in XML and inflated in the onCreateOptionsMenu()
method of DisplayListFragment. We’re just using a default behaviour built in to the ActionBar which creates a simple layout which offers the appearance and behaviour of an icon button view. What we’re going to do is replace this layout with an identical one that we have control of, and which we can then animate. We do this by setting a custom action view on the MenuItem in question.
So we can modify the setScanning()
method of DisplayListView:
public void setScanning(boolean scanning) { mListView.setEnabled(!scanning); setEmptyText(scanning ? mScanning : mNoDevices); if(scanning) { Animation rotateAnimation = AnimationUtils.loadAnimation( getActivity(), R.anim.rotate); refreshActionView.startAnimation(rotateAnimation); mRefreshItem.setActionView(refreshActionView); } else { View actionView = mRefreshItem.getActionView(); if(actionView != null) { actionView.clearAnimation(); } mRefreshItem.setActionView(null); } }
When scanning, this loads the animation and sets it running on the custom layout, and then sets the custom layout as the action view on the MenuItem; and when not scanning stops the animation and removes the custom action item on the MenuItem.
So, if we run this we can see that we now get an animated refresh icon when the scan is in progress, and the animation stopping indicates that the scan is compete:
This is certainly a much better experience for the user because it is not possible to select a device to connect to until the scan is complete, and previously there was no visual indication that the scan had finished.
In the next article we’ll look at streamlining the app start process for our user to ensure that he or she can get to the temperature display with the minimum of effort.
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.
Any chance to modify this to use with two Fragments (each with own refresh button) inside ViewPager in one Activity? Both refresh buttons overlaps between swiping. Also OnCreateOptionsMenu is called every time user swipe. Thanks
Doing that would completely break the information architecture of the app. The two Fragments correspond to two distinct app states which are implicit in the app behaviour and are not directly controlled by the user. The switch between the two Fragments is triggered when the app state changes as a result of user actions. To put the Fragments inside a ViewPager would be completely wrong in this instance, IMO.