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. Becaus
e 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