Android RSS Reader – Part 3: Refresh – ActionBar item with animation
Again this is in continuation with the RSS Reader Part 2 in the RSS Reader Series. In this tutorial we will introduce the ‘ActionBar’ and have a refresh item which refreshes the List with a rotating animation effect, a share action bar item in the description view to share the the news. Here’s the video demo of this tutorial:
Note: Please note that you cant test the share functionality in the emulator so find a device running API 14+.
here a bug reported on the same: http://code.google.com/p/android/issues/detail?id=25467
‘ActionBar’ will be shown by default when you are running your app built with SDK (API Level 11 +) on the compatible emulator or the device.
So lets first start with adding the Items to the ‘ActionBar’. In the List Activity we have two Action bar items they are 1. Refresh and 2. About.
Steps in creating the Listview action items:
step 1: Create a ‘menu’ folder under the resources (res/menu). Create an xml file (activity_main.xml) with the following content:
activity_main.xml:
1
2
3
4
5
6
7
8
9
10
11
12
|
< item android:id = "@+id/refresh_option" android:icon = "@drawable/action_refresh" android:showAsAction = "ifRoom" android:title = "Refresh" /> < item android:id = "@+id/about_option" android:icon = "@drawable/action_about" android:showAsAction = "ifRoom" android:title = "About Me" /> </ menu > |
Here you can see if the ‘showAsAction’ for a Menu item is set then it will appear on the ‘Action Bar’. The option ‘ifRoom’ tells the item that it should to be shown in ActionBar only when there is a room (space) or else it will be shown as a general Menu item.
Also refer: http://developer.android.com/design/patterns/actionbar.html
step 2: Open the Activity (List Activity) in which we want to add these ActionBar items and add the following code:
1
2
3
4
5
|
@Override public boolean onCreateOptionsMenu(Menu menu) { new MenuInflater( this ).inflate(R.menu.activity_main, menu); return ( super .onCreateOptionsMenu(menu)); } |
In the above code we are just inflating the menu views from the xml we just created. When we run the app now we can see the items added to the ActionBar.
step 3: Now we need to implement those ActionBar items.
Here’s the method to implement the ActionBar items:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.refresh_option: refreshList(item); return ( true ); case R.id.about_option: Toast.makeText( this , "RSS Reader!" , Toast.LENGTH_SHORT).show(); return ( true ); } return super .onOptionsItemSelected(item); } |
So in the above method we will returned with the item clicked and we will switch the id of the item and perform actions based on the item clicked.
step 4: When the refresh item clicked, we first have to start the Animation followed by the feed refresh, then once after feed is refreshed and set, we need to stop the animation.
we achieve this by creating a method which takes in the Menu Item (Refresh Menu – Item) as an argument, and replacing this item with a custom view and applying the animation to that view. Bit tricky but you can get the full picture when you see the final code.
step 5: So first we need to create a layout for the ActionBar item with just a ImageView hodling the refresh image:
1
2
3
4
5
6
|
style = "@android:style/Widget.ActionButton" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:contentDescription = "@string/app_name" android:src = "@drawable/action_refresh" /> |
step 6: Now we need to create a rotating animation, for this first lets create a folder called ‘anim’ under resources (res/anim), then create a new xml file (refresh_rotate.xml) with the below content:
1
2
3
4
5
6
7
|
android:duration = "1000" android:fromDegrees = "0" android:interpolator = "@android:anim/linear_interpolator" android:pivotX = "50%" android:pivotY = "50%" android:toDegrees = "360" /> |
step 7: Now in the refresh method we need to inflate the layout (action_refresh.xml) which we just created and set the animation to the layout then set the item view to the new animated View.
1
2
3
4
5
6
7
8
9
10
11
|
LayoutInflater inflater = (LayoutInflater) getApplication() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); ImageView iv = (ImageView) inflater.inflate(R.layout.action_refresh, null ); Animation rotation = AnimationUtils.loadAnimation(getApplication(), R.anim.refresh_rotate); rotation.setRepeatCount(Animation.INFINITE); iv.startAnimation(rotation); item.setActionView(iv); |
step 8: Ok, now that the animation view is set up and running we need to start the refresh process. In this post what i mean to refresh is nothing but getting the new feed from the URL and then setting the List back. So we start the refresh process (parsing) in a new thread and then after we get the new feed, we notify the adapter with the change, then the adapter will refresh the views with the new data.
The final refresh Method is here:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public void refreshList( final MenuItem item) { /* Attach a rotating ImageView to the refresh item as an ActionView */ LayoutInflater inflater = (LayoutInflater) getApplication() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); ImageView iv = (ImageView) inflater.inflate(R.layout.action_refresh, null ); Animation rotation = AnimationUtils.loadAnimation(getApplication(), R.anim.refresh_rotate); rotation.setRepeatCount(Animation.INFINITE); iv.startAnimation(rotation); item.setActionView(iv); // trigger feed refresh: Thread thread = new Thread( new Runnable() { @Override public void run() { DOMParser tmpDOMParser = new DOMParser(); feed = tmpDOMParser.parseXml(feedLink); ListActivity. this .runOnUiThread( new Runnable() { @Override public void run() { if (feed != null && feed.getItemCount() > 0 ) { adapter.notifyDataSetChanged(); // lv.setAdapter(adapter); item.getActionView().clearAnimation(); item.setActionView( null ); } } }); } }); thread.start(); } |
We can’t notify the adapter with the change in the Background thread so first we create a UI thread and then call the ‘adapter.notifyDataSetChanged()’ inside the thread. Once the list is updated we clear the animation and set the view to null.
Now will move to the description view. In the description view we need to set these things:
1. Make the logo, on click take back to the List view.
2. Add a share ActionBar item.
Here’s the snapshot of the final picture:
So first lets make the logo as the navigation button which takes us back to the List View. Here is how we achieve this:
step 1: Open the description activity and then add these two lines:
1
2
|
ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled( true ); |
In the above code we are just getting the ActionBar reference and then setting the setDisplayHomeAsUpEnabled(true).
step 2: Not done yet, now we just made the ActionBar logo as the navigation button with a arrow marker shown to the left (Run the app and check for your understanding) but we need to add the implementation. Here’s how we add the implementation:
1
2
3
4
5
6
7
8
9
10
11
|
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // app icon in action bar clicked; finish activity to go home finish(); return true ; } return super .onOptionsItemSelected(item); } |
step 3: When we set the ‘setDisplayHomeAsUpEnabled’ to true and we press the logo in the description view, then the ‘onOptionsItemSelected’ method method will be invoked with the id: android.R.id.home. So we check for the id and in the implementation we simply finish the activity to go back to the List view.
Finally now we need to have a share Action item to share the particular news item.
step 1: So create the required menu and when creating the item to set it to default share action as shown in the figure above, we need to add an extra attribute: android:actionProviderClass=”android.widget.ShareActionProvider”
Here’s the ‘activity_desc.xml’ created in the menu folder (res/menu):
1
2
3
4
5
6
7
|
< item android:id = "@+id/share_option" android:actionProviderClass = "android.widget.ShareActionProvider" android:showAsAction = "ifRoom" android:title = "Share" /> </ menu > |
step 2: And in the description Activity add this implementation to the onCreateOptionsMenu():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Override public boolean onCreateOptionsMenu(Menu menu) { new MenuInflater( this ).inflate(R.menu.activity_desc, menu); // Locate MenuItem with ShareActionProvider MenuItem shareItem = menu.findItem(R.id.share_option); // Fetch and store ShareActionProvider mShareActionProvider = (ShareActionProvider) shareItem .getActionProvider(); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType( "text/plain" ); shareIntent.putExtra(Intent.EXTRA_SUBJECT, "RSS Reader" ); String shareBody = feed.getItem(pos).getTitle() + "\n" + feed.getItem(pos).getDescription(); shareIntent.putExtra(Intent.EXTRA_TEXT, shareBody); // Set the share intent mShareActionProvider.setShareIntent(shareIntent); return true ; } |
Note: Please note that you cant test the share functionality in the emulator so find a device running API 14+.
here a bug reported on the same: http://code.google.com/p/android/issues/detail?id=25467
In the above code we just got the action provider which we set from the xml and then we are setting the ‘ShareActionProvider’ with an intent having information to share like news title and description. (you can also parse for the link from the RSS to make the share more informatic).
With this, the tutorial came to an end and in the next we will see how to make that long list containing on average of 100+ items to load in small chunks where we show a loader at the end of list while it gets the next items.