How to Use Contextual Toolbar and ActionBar in Android

Hello World! In this post, we’re going to be covering contextual toolbars/action bars. They are a structure that will allow us to change the menu items on the toolbar based on the user’s context. Suppose we were looking at our email in the Inbox or Gmail app and wanted to delete an email. We could tap on the email in the list and then click the delete icon in the Toolbar, or we could long-press on the email in the list and the menu items in our Toolbar change! The context is the list of selected emails. What can we do with a list of emails? We can delete them, mark them to be unread, archive them, etc. This the contextual toolbar at work! We’re going to be adding on from our previous app on Toolbars and Menus.

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.

Note: Before reading too much into this post, you should look at the other post on Toolbars and Menus. We’re going to be building off of the same app! The source code for this post can be downloaded here. You’ll need an icon in it so make sure you download it!

Side Note: To save text, I’m going to be abbreviating the contextual Toolbar as CAB, which actually stands for Contextual Action Bar. However, we’ll be using Toolbars instead of the system action bar because it gives us the flexibility we need to implement Material Design.

Let’s open up the previous app and make sure it works. We should see a list with all of the countries and a refresh icon on the Toolbar. We’ll need to add another icon to our drawable folder so follow the same process as before: copy over everything in CABDemo/app/src/main/res/drawable/ folder and copy it to your project’s drawable folder. We’ll get a new icon that we can use in the toolbar’s context menu.

Speaking of context menu, let’s create another menu XML file to serve as the menu that will be inflated when the CAB activates. Let’s right-click on the menu folder and create a new menu resource XML file called toolbar_cab.xml. Put the following in the file:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/action_delete" android:title="Delete" android:icon="@drawable/ic_delete_white_48dp" app:showAsAction="ifRoom" />
</menu>

This is similar to the previous menu with the exception of the icon and the ID. Now we can use this when we start the Toolbar’s contextual action mode. However, before we can get to this, we need to set another property in the styles.xml file to prevent the CAB from being stacked on top of the existing one like in the following image!

CAB – 1

We want the CAB to be an overlay on top of our existing Toolbar. Let’s open up the styles.xml file and append the following line after the windowNoTitle attribute: <item name=”windowActionModeOverlay”>true</item> . This will give us the desired result of having the action mode overlayed on top of the existing Toolbar.

Now that we’ve handled everything there is to do regarding resources, let’s get to the code. We’re going to have to rethink our approach when it comes to our list because of one critical fact: ArrayAdapter’s internal representation of a list is immutable! We’re not allowed to delete items from the ArrayAdapter so we need to think of another way to do this. Luckily, we can use Java’s aliasing to our benefit. Instead of using the utility method on ArrayAdapter, we’re going to grab an ArrayList and pass that reference to ArrayAdapter. It’s important that we use an ArrayList because they are completely mutable. Keep in mind that not all implementing classes of the List interface are mutable! In specific, we’ll use an ArrayList. This way, we can make changes to the underlying list that the adapter displays. Then we can call a method on the adapter called notifyDataSetChanged()  that will refresh our main ListView. This works because when we set the adapter, the ListView registers itself as a listener to the adapter for some methods. Therefore, when the adapter calls that method, the ListView knows to refresh itself because it’s underlying adapter changed. We can also use this to implement refresh functionality by “resetting” our adapter by “resetting” our list by clearing it and adding back a permanent copy of all of the countries.

Let’s begin implementing this fix by adding member variables for the list of current countries and a list of countries that are queued up for deletion:

private ListView listView;
private ArrayAdapter<String> adapter;
private ArrayList<String> toDelete;
private ArrayList<String> countries;

The final two variables we’ll need to keep track of countries to be deleted in the action mode and a running list of countries that may not or may not have all of the countries. Now that we’ve done this, let’s make corresponding changes in our onCreate(Bundle)  to initialize these ArrayLists and use a constructor for our adapter instead of the utility method.

toDelete = new ArrayList<>();
countries = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.countries_array)));
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1, countries);
listView.setAdapter(adapter);

That constructor parameter is simply fetching the array from our app’s resources and converting it into a List, then an ArrayList, more specifically. Now that we have this new approach, let’s change more code in our onMenuItemClick(MenuItem)  method so that the refresh functionality actually works!

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_refresh:
            Toast.makeText(this, "Refreshing...", Toast.LENGTH_SHORT).show();
            countries.clear();
            countries.addAll(Arrays.asList(getResources().getStringArray(R.array.countries_array)));
            adapter.notifyDataSetChanged();
            return true;
        default:
            return false;
    }
}

What we’re doing in this code snippet is clearing out the current list of countries so it becomes an empty list. Then we’re adding back all of the countries. Finally, we’re telling our adapter that our data set has changed and it will tell our ListView to refresh.

Now that we’ve added that core functionality, let’s actually get to the part where we’re going to start our action mode. We can do this in two different ways: register a listener with our ListView to listen for long presses and call Activity’s startActionMode(…) and pass in MainActivity as a listener, or we can take advantage of ListView and use it’s setMultiChoiceModeListener(…) to start the action mode automatically for us. To do this, we first need to tell our ListView to allow the user to select multiple items and enter the action mode at the same time. Then we can set this activity to be the listener and implement those methods. In our onCreate(Bundle) , let’s type in the following code.

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(this);

The last line will make our activity implement the MultiChoiceModeListener interface so we’ll have to do that. The first method we need to implement is the one that actually inflates the menu!

@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.toolbar_cab, menu);
    return true;
}

We can grab a MenuInflater object from our Activity class and very simply inflate the XML file of the menu into the menu object. Note that we have to return true so the action mode knows we’re doing work in this method. In onPrepareActionMode(…) , it’s OK for us to return false since we’re not doing any work in that method. Next, we need to implement the method that will handle list items being checked and unchecked.

@Override
public void onItemCheckedStateChanged(ActionMode actionMode, int position, long id, boolean checked) {
    if (checked) {
        toDelete.add(adapter.getItem(position));
    } else {
        toDelete.remove(adapter.getItem(position));
    }
}

The implementation is simple: if this item is checked, then add it to the list to be deleted! We also need the other condition of the list item being unchecking. However, we don’t need any more contingencies since our design guarantees us that if a ListView item is passed in with the checked flag as false, it must be true that it is in our list since it had to have been checked at some point. Next, we can move on to the method that actually deletes the items. We also have a method that is called when the action mode is finished.

@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
    switch (menuItem.getItemId()) {
        case R.id.action_delete:
            for (String item : toDelete) {
                countries.remove(item);
            }
            actionMode.finish();
            return true;
        default:
            return false;
    }
}

@Override
public void onDestroyActionMode(ActionMode actionMode) {
    toDelete.clear();
}

Our implementation looks strikingly similar to when we implemented the Toolbar’s menu items and it’s intended to be that way. If the delete action was tapped, then we need to iterate through all of the items in the queue of items to delete and remove them from our list of current countries. Then we actually need to end the action mode! Regarding the method that’s called when the action mode is finished, we need to clear our queue of items to delete every time the user exits the action mode.

CAB – 2

Now we’re finished with our implementation and we can run our app! When we long-press on a ListView item, we should see the CAB overlay on top of our Toolbar and we should be able to delete items now! If we’ve removed countries, we can always add them back again by pressing the refresh button and our ListView should reset!

Conclusion

In this post, we learned how to use the contextual Toolbar/action bar to change the menu items based on the context of the user. We did this by declaring another menu item and inflating that when the action mode begins. We also learned more about ArrayLists and dealing with immutable and mutable lists as well. Overall, we created an app changes the menus when the user long-presses on a countries, allow them to delete it. Changing our view to suit the user’s context is always beneficial to your user!