Android – List View and Contextual Action Mode

In case you want to create list view with option for user to select items of a list and perform certain action on them (example is Gmail application where user is able to select multiple email records and delete them in one step), you have two options to achieve this:

1. Context Menu

2. Contextual Action Mode

First one is defined as floating menu that appears when user performs long click on an element (in this case element in list view).

Contextual action mode is second option and it works in a way when long click on an element is performed view is switched to this action mode so that new bar at the top of the screen appears with action options. Also in this mode you can select more than one element, this is very useful in case of list view. You can see below how this action mode looks like. Google advised for developers to use this contextual action mode in all cases when development is targeting Android versions 3.0 and above.

 

In this article we shall see how to implement this action mode in case of list view. Our example will cover code that is able to handle multiple selection of items and displaying a count of selected records in action bar. So lets first create layout of main activity:

<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   tools:context=".MainActivity"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent">
   <ListView
      android:id="@+id/mainListView"
      android:scrollbars="vertical"
      android:layout_height="match_parent"
      android:layout_width="match_parent"/>

</LinearLayout>

As you can see it is simple layout with just list view vertically oriented. Also we shall create simple menu layout for the case when list view defined above is in action mode,we shall name this file action_menu.xml and put it in res/menu folder of the project. As you can see this action menu will have just two options.


<?xml version="1.0" encoding="utf-8"?>
   <menu xmlns:android="http://schemas.android.com/apk/res/android">
      <item android:id="@+id/option1"
      android:title="Option 1"
      android:showAsAction="withText|ifRoom" />

      <item android:id="@+id/option2"
      android:title="Option 2"
      android:showAsAction="withText|ifRoom" />
</menu>

 

Let’s  take a look at the MainActivity class of our application.


public class MainActivity extends ActionBarActivity {

   SimpleArrayAdapter adapter;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      ListView listView = (ListView)findViewById(R.id.mainListView);
      ArrayList<String> list = new ArrayList<>();
      list.add("Test 1");
      list.add("Test 2");
      list.add("Test 3");

      final SimpleArrayAdapter adapter = new SimpleArrayAdapter(this, android.R.layout.simple_list_item_1, list);
      listView.setAdapter(adapter);
      listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL);
      final SimpleMultiChoiceModeListener cml = new SimpleMultiChoiceModeListener();
      listView.setMultiChoiceModeListener(cml);
      listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {

         @Override
         public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long l) {
             ((ListView)view).setItemChecked(position, !((SimpleArrayAdapter)adapterView.getAdapter()).isPositionChecked(position));
            return false;
         }
      });

}

Let’s go trough the onCreate method of the MainActivity class. At the beginning of the method onCreate we set content view to be based on previously defined layout. Next we fetch ListView object from the defined layout and also create String list that will represent data for ListView adapter. now we come to part where we create adapter (in this case custom adapter SimpleArrayAdapter – details of this adapted will be provided later in this article). Then we enable ListView object to have provision for user to select more than one element of list view in action mode, this is achieved by setting choice mode to AbsListView.CHOICE_MODE_MULTIPLE_MODAL. In the remaining part of this code we instantiate SimpleMultiChoiceListener that extends class MultiChoiceListener that is responsible for handling status of each item in list view when it is checked and also actions from action menu list when view is in action mode. Finally ItemLongClickListener is associated to the list view . This listener checks if the item in the list view is checked (selected) and based on that marks it ask checked/unchecked in underlying model of adapter. This model is defined in adapted code below.


public class SimpleArrayAdapter extends ArrayAdapter<String> {

   private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();

   public SimpleArrayAdapter(Context context, int textViewResourceId,
      List<String> objects) {
      super(context, textViewResourceId, objects);
   }

   public void setNewSelection(int position, boolean value) {
      mSelection.put(position, value);
   }

   public boolean isPositionChecked(int position) {
      Boolean result = mSelection.get(position);
      return result == null ? false : result;
   }

   public Set<Integer> getCurrentCheckedPosition() {
      return mSelection.keySet();
   }

   public void removeSelection(int position) {
      mSelection.remove(position);
   }

   public void clearSelection() {
      mSelection = new HashMap<Integer, Boolean>();
   }
}

Looking at the adapter class that is extending ArrayAdapter class you will notice private HashMap, this map will contain information on positions that are selected (checked), we need to track it in order to have number of selected rows and later when we perform actual action, what rows will be affected with this action. So here I shall make just small comments on each method of this adapter class:

setNewSelection – method will simply set a boolean value on the provided position of the map

isPositionChecked – returns boolean value from the map based on provided position. This method is called on action of the listener defined in MainApplication class above.

removeSelection – removes value from the map on specified position and calls method for the listener used for refreshing data on the view.

clearSelection – creates new HashMap, this means that there will be no selected items by default

Finally let’s take a look at the private inner class of MyApplication  representing the listener for multiple choice selection when action mode is invoked, class is named SimpleMultiChoiceModeListener

 


private class SimpleMultiChoiceModeListener implements AbsListView.MultiChoiceModeListener{
   private int count = 0;
   private ActionMode actionMode = null;

   @Override
   public void onItemCheckedStateChanged(ActionMode mode, int position,
      long id, boolean checked) {
      if (checked) {
         count++;
         adapter.setNewSelection(position, checked);
      } else {
         count--;
         adapter.removeSelection(position);
      }
      mode.setTitle(count + " selected");
}

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

   @Override
   public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
      switch (menuItem.getItemId()) {
      case R.id.option1:
         Toast.makeText(getApplicationContext(),"Option 1 selected!",Toast.LENGTH_LONG).show();
      case R.id.option2:
         Toast.makeText(getApplicationContext(), "Option 2 selected!", Toast.LENGTH_LONG).show();
      }
      closeActionMode();
      return true;
   }

   @Override
   public void onDestroyActionMode(ActionMode actionMode) {
      actionMode=null;
      nr=0;
      adapter.clearSelection();
   }

   public void closeActionMode(){
      if(actionMode!=null){
         actionMode.finish();
      }
      nr=0;
      adapter.clearSelection();
   }
}

Let’s examine methods of above class:

onItemCheckedStateChanged – method, based on the value of boolean variable checked (this is providing information did user selected or un-selected the row in list view) is incrementing or decrementing count variable that shows number of currently selected records and also updates values in the map of adapter trough calling appropriate adapter’s method.

onCreateActionMode – called by the system when action mode is created, in this case this method just sets the layout for the action menu that we have specified at the beginning of this article

onActionItemClicked – this method is called when user selects option from action menu, in this case we are calling simple toast messages and at the end clearing selection by calling method closeActionMode

Finally we have two more methods, onDestroyActionMode and closeActionMode, first one is inherited from original class and called when user “exits” the action mode, while second we created for the purpose to close action mode and clear selections in underlaying adapter.
And now when we run application, you can see how it behaves when we do long press on any item of list view.

screen1

I hope that this article will help you in your development, in case you have additional questions please let me know in below comments. Have a nice day …



					
									
Posted in Android Development Tagged with: , ,
2 comments on “Android – List View and Contextual Action Mode
  1. Zoran says:

    Great post!

    Even a very experienced developer like me can learn from this!

    Keep posting

  2. The Master World says:

    Contextual Action mode in android studio

    if you are beginner and do not know how to create contextual action mode in android then this lecture can help alot

    http://themasterworld.com/working-with-contextual-action-mode-android-studio/

Leave a Reply

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

*

Subscribe for Newsletter

Categories