Android - Multi Select Spinner Example

In this tutorial, I'm going to show ou how to create a spinner in Android that supports multiple selection. In the end, the spinner we're going to create is look like the one on the picture above. If you click on the spinner, it will display a popup dialog where users can tick on the item they want to choose.

The idea of implementing custom spinner in Android is by creating a class that extends AppCompatSpinner and implements DialogInterface.OnMultiChoiceClickListener.

In the class, we need to store the items we want to display on the spinner, list of selected items and the ArrayAdapter which will be passed to the parent class AppCompatSpinner. In this example, I store the item list in an ArrayList. But you can use any other data types such as HashMap or array of string by modifying the code in this tutorial.

First, we create the Item class which has two variables, name and value. To make it easy, I use Lombok annotations.

Item.java

  package com.woolha.example.models;

  import lombok.AllArgsConstructor;
  import lombok.Builder;
  import lombok.Data;
  import lombok.Getter;
  import lombok.NoArgsConstructor;
  import lombok.Setter;

  @Data
  @Builder
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public class Item {
      private String name;
      private Boolean value;
  }

Below is the initial code of our MultiSelectionSpinner class which consists of the variables I've mentioned before and the constructors. As for the list item layout, we use Android's simple_spinner_item.xml.

MultiSelectionSpinner.java

  public class MultiSelectionSpinner extends android.support.v7.widget.AppCompatSpinner implements
          DialogInterface.OnMultiChoiceClickListener {
  
      ArrayList<Item> items = null;
      boolean[] selection = null;
      ArrayAdapter adapter;
  
      public MultiSelectionSpinner(Context context) {
          super(context);
  
          adapter = new ArrayAdapter(context,
                  android.R.layout.simple_spinner_item);
          super.setAdapter(adapter);
      }
  
      public MultiSelectionSpinner(Context context, AttributeSet attrs) {
          super(context, attrs);
  
          adapter = new ArrayAdapter(context,
                  android.R.layout.simple_spinner_item);
          super.setAdapter(adapter);
      }
  }

Next we're going to implement the methods we have to override. First let's implement onClick. It's used to handle when the user click on an item to select or unnselect it. What we need to do is updating selection value.

MultiSelectionSpinner.java

  @Override
  public void onClick(DialogInterface dialog, int idx, boolean isChecked) {
      if (selection != null && idx < selection.length) {
          selection[idx] = isChecked;

          adapter.clear();
          adapter.add(buildSelectedItemString());
      } else {
          throw new IllegalArgumentException(
                  "'idx' is out of bounds.");
      }
  }

The next thing we have to override is performClick. It's called when the user click on the spinner by which a popup dialog will be shown. Inside, we can also handle what should happen if the user click on the positive button.

MultiSelectionSpinner.java

  @Override
  public boolean performClick() {
      final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
      String[] itemNames = new String[items.size()];

      for (int i = 0; i < items.size(); i++) {
          itemNames[i] = items.get(i).getName();
      }

      builder.setMultiChoiceItems(itemNames, selection, this);

      builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface arg0, int arg1)
          {
              // Do nothing
          }
      });

      builder.show();

      return true;
  }

We also need to override setAdapter. Because we don't use it for now, just throw an error.

MultiSelectionSpinner.java

  @Override
  public void setAdapter(SpinnerAdapter adapter) {
      throw new RuntimeException(
              "setAdapter is not supported by MultiSelectSpinner.");
  }

Apart from the methods that must be overriden, there are some other methods we have to implement to make the spinner support basic functionalities.

setItems is used to set the options that will be shown to user. In this implementation, everytime we set the spinner options, all selections will be cleared.

MultiSelectionSpinner.java

  public void setItems(ArrayList<Item> items) {
      this.items = items;
      selection = new boolean[this.items.size()];
      adapter.clear();
      adapter.add("");
      Arrays.fill(selection, false);
  }

setSelection is used to set the initial condition which options are selected even before the user ever clicked on the spinner.

MultiSelectionSpinner.java

  public void setSelection(ArrayList selection) {
      for (int i = 0; i < this.selection.length; i++) {
          this.selection[i] = false;
      }

      for (Item sel : selection) {
          for (int j = 0; j < items.size(); ++j) {
              if (items.get(j).getValue().equals(sel.getValue())) {
                  this.selection[j] = true;
              }
          }
      }

      adapter.clear();
      adapter.add(buildSelectedItemString());
  }

The last one is getSelectedItems which returns the list of selected Item object.

MultiSelectionSpinner.java

  public ArrayList<Item> getSelectedItems() {
      ArrayList<Item> selectedItems = new ArrayList<>();

      for (int i = 0; i < items.size(); ++i) {
          if (selection[i]) {
              selectedItems.add(items.get(i));
          }
      }

      return selectedItems;
  }

The full code will look like this

MultiSelectionSpinner.java

  package com.woolha.example.views;
  
  import android.content.Context;
  import android.content.DialogInterface;
  import android.support.v7.app.AlertDialog;
  import android.util.AttributeSet;
  import android.widget.ArrayAdapter;
  import android.widget.SpinnerAdapter;
  
  import com.woolha.example.models.Item;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  
  public class MultiSelectionSpinner extends android.support.v7.widget.AppCompatSpinner implements
          DialogInterface.OnMultiChoiceClickListener {
  
      ArrayList<Item> items = null;
      boolean[] selection = null;
      ArrayAdapter adapter;
  
      public MultiSelectionSpinner(Context context) {
          super(context);
  
          adapter = new ArrayAdapter(context,
                  android.R.layout.simple_spinner_item);
          super.setAdapter(adapter);
      }
  
      public MultiSelectionSpinner(Context context, AttributeSet attrs) {
          super(context, attrs);
  
          adapter = new ArrayAdapter(context,
                  android.R.layout.simple_spinner_item);
          super.setAdapter(adapter);
      }
  
      @Override
      public void onClick(DialogInterface dialog, int which, boolean isChecked) {
          if (selection != null && which < selection.length) {
              selection[which] = isChecked;
  
              adapter.clear();
              adapter.add(buildSelectedItemString());
          } else {
              throw new IllegalArgumentException(
                      "Argument 'which' is out of bounds.");
          }
      }
  
      @Override
      public boolean performClick() {
          final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
          String[] itemNames = new String[items.size()];
  
          for (int i = 0; i < items.size(); i++) {
              itemNames[i] = items.get(i).getName();
          }
  
          builder.setMultiChoiceItems(itemNames, selection, this);
  
          builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface arg0, int arg1)
              {
                  // Do nothing
              }
          });
  
          builder.show();
  
          return true;
      }
  
      @Override
      public void setAdapter(SpinnerAdapter adapter) {
          throw new RuntimeException(
                  "setAdapter is not supported by MultiSelectSpinner.");
      }
  
      public void setItems(ArrayList<Item> items) {
          this.items = items;
          selection = new boolean[this.items.size()];
          adapter.clear();
          adapter.add("");
          Arrays.fill(selection, false);
      }
  
      public void setSelection(ArrayList<Item> selection) {
          for (int i = 0; i < this.selection.length; i++) {
              this.selection[i] = false;
          }
  
          for (Item sel : selection) {
              for (int j = 0; j < items.size(); ++j) {
                  if (items.get(j).getValue().equals(sel.getValue())) {
                      this.selection[j] = true;
                  }
              }
          }
  
          adapter.clear();
          adapter.add(buildSelectedItemString());
      }

      private String buildSelectedItemString() {
          StringBuilder sb = new StringBuilder();
          boolean foundOne = false;
  
          for (int i = 0; i < items.size(); ++i) {
              if (selection[i]) {
                  if (foundOne) {
                      sb.append(", ");
                  }
  
                  foundOne = true;
  
                  sb.append(items.get(i).getName());
              }
          }
  
          return sb.toString();
      }
  
      public ArrayList<Item> getSelectedItems() {
          ArrayList<Item> selectedItems = new ArrayList<>();
  
          for (int i = 0; i < items.size(); ++i) {
              if (selection[i]) {
                  selectedItems.add(items.get(i));
              }
          }
  
          return selectedItems;
      }
  }

Below is the example of how to add a MultiSelectionSpinner component in an XML layout.

layout_example.xml

  <com.woolha.example.MultiSelectionSpinner
                android:id="@+id/spn_items"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:spinnerMode="dropdown" />

And this is the example of how to use the spinner in the activity or fragment where you want to use it.

ExampleActivity.java

  MultiSelectionSpinner mySpinner;

  ...

  ArrayList<Item> items = new ArrayList<>();
  items.add(Item.builder().name('Item 1').value('item-1').build());
  items.add(Item.builder().name('Item 2').value('item-2').build());
  items.add(Item.builder().name('Item 3').value('item-3').build());

  mySpinner = (MultiSelectionSpinner) rootView.findViewById(R.id.spn_items);
  mySpinner.setItems(items);

  ...

  // To set selected items
  ArrayList<Item> items = new ArrayList<>();
  items.add(Item.builder().name('Item 1').value('item-1').build());

  mySpinner.setSelection(selectedItems);

  ...

  // To get the selected Item list
  ArrayList<Item> selectedItems = mySpinner.getSelectedItems();

Having done all the steps above, now you can try to run your application whether it works