68

How do I create spinner which allows to choose multiple items, i.e spinner with check boxes?

Meenal
  • 2,879
  • 5
  • 19
  • 43

5 Answers5

160

I have written custom implementation of MultiSpinner. It's looking similar to normal spinner, but it has checkboxes instead of radiobuttons. Selected values are displayed on the spinner divided by comma. All values are checked by default. Try it:

package cz.destil.settleup.gui;

public class MultiSpinner extends Spinner implements
        OnMultiChoiceClickListener, OnCancelListener {

    private List<String> items;
    private boolean[] selected;
    private String defaultText;
    private MultiSpinnerListener listener;

    public MultiSpinner(Context context) {
        super(context);
    }

    public MultiSpinner(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);
    }

    public MultiSpinner(Context arg0, AttributeSet arg1, int arg2) {
        super(arg0, arg1, arg2);
    }

    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
        if (isChecked)
            selected[which] = true;
        else
            selected[which] = false;
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        // refresh text on spinner
        StringBuffer spinnerBuffer = new StringBuffer();
        boolean someSelected = false;
        for (int i = 0; i < items.size(); i++) {
            if (selected[i] == true) {
                spinnerBuffer.append(items.get(i));
                spinnerBuffer.append(", ");
                someSelected = true;
            } 
        }
        String spinnerText;
        if (someSelected) {
            spinnerText = spinnerBuffer.toString();
            if (spinnerText.length() > 2)
                spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
        } else {
            spinnerText = defaultText;
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
                android.R.layout.simple_spinner_item,
                new String[] { spinnerText });
        setAdapter(adapter);
        listener.onItemsSelected(selected);
    }

    @Override
    public boolean performClick() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setMultiChoiceItems(
                items.toArray(new CharSequence[items.size()]), selected, this);
        builder.setPositiveButton(android.R.string.ok,
                new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
        builder.setOnCancelListener(this);
        builder.show();
        return true;
    }

    public void setItems(List<String> items, String allText,
            MultiSpinnerListener listener) {
        this.items = items;
        this.defaultText = allText;
        this.listener = listener;

        // all selected by default
        selected = new boolean[items.size()];
        for (int i = 0; i < selected.length; i++)
            selected[i] = true;

        // all text on the spinner
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
                android.R.layout.simple_spinner_item, new String[] { allText });
        setAdapter(adapter);
    }

    public interface MultiSpinnerListener {
        public void onItemsSelected(boolean[] selected);
    }
}

You use it in XML like this:

<cz.destil.settleup.gui.MultiSpinner android:id="@+id/multi_spinner" />

And you pass data to it in Java like this:

MultiSpinner multiSpinner = (MultiSpinner) findViewById(R.id.multi_spinner);
multiSpinner.setItems(items, getString(R.string.for_all), this);

Also you need to implement the listener,which will return the same length array , with true or false to show selected to unselected..

public void onItemsSelected(boolean[] selected);
Akki
  • 775
  • 8
  • 19
David Vávra
  • 18,446
  • 7
  • 48
  • 56
  • 2
    how do you get the selected indexes from your component? I am able to use it but how can i get the selected items? Thank you – Thiago Sep 12 '11 at 18:58
  • 1
    Just expose array selected[] via public getter. – David Vávra Oct 25 '11 at 16:17
  • 2
    Works great! Awesome piece of code. Thanks for posting complete listing. – walkerk Oct 27 '11 at 19:41
  • Hi! Great piece of code! What is the license associated with your class ? Can it be used in commercial code ? – Rafael Nobre Aug 08 '12 at 12:41
  • @Destil forgot to add your name so you are notified of my last post. – Rafael Nobre Aug 08 '12 at 13:36
  • 1
    @nobre: All code on SO is cc-wiki with attribution. See http://meta.stackexchange.com/questions/25956/what-is-up-with-the-source-code-license-on-stack-overflow – David Vávra Aug 09 '12 at 13:25
  • @Destil How to set SimpleCursorAdapter for MultiSpinner – yogeshhkumarr Jul 29 '13 at 09:35
  • @Destil, I've written an alternative version of your code... I would like to hear a feedback from you :) – vault Apr 08 '14 at 15:10
  • @Destil I know this is really old, is there any chance you have a version where the Dialog is actually a dropdown so the look & feel of a spinner would still be the same? or would this be an issue since a dropdown wouldn't have a close button? – Mathijs Segers Jul 10 '14 at 14:14
  • @Destil, your code snippet is very nice! How can I implement on value change listener? – Rokas Aug 17 '14 at 19:20
  • 1
    you have to add some description for getting value so newbie got easily by the way Wonderful work done. – Pratik Butani Oct 09 '14 at 12:16
  • 4
    @Destil I want `DropDown` View instead of Dialog. is it Possible? – Pratik Butani Oct 10 '14 at 12:52
  • @Rokas Use MultiSpinnerListener – David Vávra Dec 26 '14 at 14:35
  • 1
    @ツFellinLovewithAndroidツ It is possible but my answer is about a dialog. – David Vávra Dec 26 '14 at 14:35
  • stuck in same can anyone help http://stackoverflow.com/questions/29446088/how-to-get-spinner-values-in-textview/29487383?noredirect=1#comment47175570_29487383 –  Apr 08 '15 at 13:42
  • I had a small bug with this code. When every choice was selected, it also showed the default text, so I fixed it here: https://gist.github.com/asiviero/54219136dd5cd7341a15 Thanks so much @Destil – asiviero Apr 13 '15 at 02:47
  • 2
    How to give search functionality in that? Is there any link? – Pratik Butani Jun 08 '15 at 05:55
  • how can I get its selection?! – Sheriff Said Elahl May 30 '16 at 18:53
  • 1
    For people in 2020, you will most likely get error at extends Spinner. You should replace the class to: public class MultiSpinner extends androidx.appcompat.widget.AppCompatSpinner implements – Izoman Feb 10 '20 at 14:44
  • Thanks. Is it possible to refresh the spinner view? Notifying the adapter doesn't work. It has to refresh because if someone picks "all" it has to uncheck the other boxes. – pete Aug 13 '20 at 04:37
14

I would just like to show an alternative version of @Destil's MultiSpinner (thank you for your inspiring code) which allows to use "android:entries" in xml, just like a spinner.

It doesn't initially show a default text, like "choose one", but you can easily obtain it by setting an additional ArrayAdapter in the constructor.

MultiSpinner.java

package com.example.helloworld;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

/**
 * Inspired by: http://stackoverflow.com/a/6022474/1521064
 */
public class MultiSpinner extends Spinner {

    private CharSequence[] entries;
    private boolean[] selected;
    private MultiSpinnerListener listener;

    public MultiSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiSpinner);
        entries = a.getTextArray(R.styleable.MultiSpinner_android_entries);
        if (entries != null) {
            selected = new boolean[entries.length]; // false-filled by default
        }
        a.recycle();
    }

    private OnMultiChoiceClickListener mOnMultiChoiceClickListener = new OnMultiChoiceClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which, boolean isChecked) {
            selected[which] = isChecked;
        }
    };

    private DialogInterface.OnClickListener mOnClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // build new spinner text & delimiter management
            StringBuffer spinnerBuffer = new StringBuffer();
            for (int i = 0; i < entries.length; i++) {
                if (selected[i]) {
                    spinnerBuffer.append(entries[i]);
                    spinnerBuffer.append(", ");
                }
            }

            // Remove trailing comma
            if (spinnerBuffer.length() > 2) {
                spinnerBuffer.setLength(spinnerBuffer.length() - 2);
            }

            // display new text
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
                    android.R.layout.simple_spinner_item,
                    new String[] { spinnerBuffer.toString() });
            setAdapter(adapter);

            if (listener != null) {
                listener.onItemsSelected(selected);
            }

            // hide dialog
            dialog.dismiss();
        }
    };

    @Override
    public boolean performClick() {
        new AlertDialog.Builder(getContext())
                .setMultiChoiceItems(entries, selected, mOnMultiChoiceClickListener)
                .setPositiveButton(android.R.string.ok, mOnClickListener)
                .show();
        return true;
    }

    public void setMultiSpinnerListener(MultiSpinnerListener listener) {
        this.listener = listener;
    }

    public interface MultiSpinnerListener {
        public void onItemsSelected(boolean[] selected);
    }
}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MultiSpinner">
        <attr name="android:entries" />
    </declare-styleable>
</resources>

layout_main_activity.xml

<com.example.helloworld.MultiSpinner
    android:id="@+id/multispinner"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:entries="@array/multispinner_entries" />
vault
  • 3,930
  • 1
  • 35
  • 46
9

As far as I know Spinner doesn't have a multiple choice mode. Instead you can create an ImageButton and set a drawable down arrow in the right side and on the click event you can open a Dialog having items with the multiple checkboxes.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Vikas Patidar
  • 42,865
  • 22
  • 93
  • 106
3

Thanks for the post! Great solution. I made a small change to the class (method setItems) to allow users to set already selected items instead of selecting all items to true by default.

public void setItems(
    List<String> items,
    List<String> itemValues, 
    String selectedList,
    String allText,
    MultiSpinnerListener listener) {
        this.items = items;
        this.defaultText = allText;
        this.listener = listener;

        String spinnerText = allText;

        // Set false by default
        selected = new boolean[itemValues.size()];
        for (int j = 0; j < itemValues.size(); j++)
            selected[j] = false;

        if (selectedList != null) {
            spinnerText = "";
            // Extract selected items
            String[] selectedItems = selectedList.trim().split(",");

            // Set selected items to true
            for (int i = 0; i < selectedItems.length; i++)
                for (int j = 0; j < itemValues.size(); j++)
                    if (selectedItems[i].trim().equals(itemValues.get(j))) {
                        selected[j] = true;
                        spinnerText += (spinnerText.equals("")?"":", ") + items.get(j);
                        break;
                }
    }

        // Text for the spinner
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
            android.R.layout.simple_spinner_item, new String[] { spinnerText });
        setAdapter(adapter);
}
ᗩИᎠЯƎᗩ
  • 2,122
  • 5
  • 29
  • 41
2

You can check a simple library MultiSelectSpinner

You can simply do the following steps:

multiSelectSpinnerWithSearch.setItems(listArray1, new MultiSpinnerListener() {
    @Override
    public void onItemsSelected(List<KeyPairBoolData> items) {
        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).isSelected()) {
                Log.i(TAG, i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
            }
        }
    }
});

The listArray1 will be your array.

Check the full example here in How-To

Pratik Butani
  • 60,504
  • 58
  • 273
  • 437