How do I create spinner which allows to choose multiple items, i.e spinner with check boxes?
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);
DropDown
View instead of Dialog. is it Possible? –
Microdont 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: https://mcmap.net/q/277692/-android-spinner-with-multiple-choice
*/
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" />
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.
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);
}
itemValues
? –
Titus 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
- Kotlin version based on this answer.
- Extends
AppCompatSpinner
instead of Spinner. - Added
onTouchEvent
otherwise the click was not being recognized. - Made some refactoring to improve readability.
- Added
unselectAll
method. - Fix trailing comma using
joinToString
.
class MultiSelectionSpinner @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) :
androidx.appcompat.widget.AppCompatSpinner(context, attrs, defStyleAttr),
DialogInterface.OnMultiChoiceClickListener,
DialogInterface.OnCancelListener {
private var items: List<String> = arrayListOf()
lateinit var selected: BooleanArray
private var defaultText: String? = null
override fun onClick(dialog: DialogInterface, which: Int, isChecked: Boolean) {
selected[which] = isChecked
}
override fun onCancel(dialog: DialogInterface?) {
updateSpinnerText()
}
/**
* Refresh text on spinner when closing the dialog.
*/
private fun updateSpinnerText() {
val spinnerText: String? = if (selected.any { it }) {
getSelectedItemsText()
} else {
defaultText
}
spinnerText?.let {
setSpinnerText(it)
}
}
/**
* Get a comma separated string for the selected items.
*/
private fun getSelectedItemsText(): String {
val selectedItems = items.filterIndexed { index, _ ->
selected[index]
}
return selectedItems.joinToString(", ")
}
/**
* Achieve that setting just one element to the adapter.
*/
private fun setSpinnerText(spinnerText: String) {
val adapter = ArrayAdapter(
context,
android.R.layout.simple_spinner_item,
arrayOf(spinnerText)
)
setAdapter(adapter)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
super.onTouchEvent(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
return true
}
MotionEvent.ACTION_UP -> {
// For this particular app we want the main work to happen
// on ACTION_UP rather than ACTION_DOWN. So this is where
// we will call performClick().
performClick()
return true
}
}
return false
}
@SuppressLint("ClickableViewAccessibility")
override fun performClick(): Boolean {
val builder = AlertDialog.Builder(context)
builder.setMultiChoiceItems(
items.toTypedArray(),
selected,
this
)
builder.setPositiveButton(android.R.string.ok) {
dialog, _ -> dialog.cancel()
}
builder.setOnCancelListener(this)
builder.show()
return true
}
fun setItems(
items: List<String>,
defaultText: String?
) {
this.items = items
this.defaultText = defaultText
// By default none of the items will be selected
selected = BooleanArray(items.size) { false }
// Text to show initially, before selecting items
defaultText?.let {
setSpinnerText(it)
}
}
fun unselectAll() {
this.selected = BooleanArray(selected.size) { false }
updateSpinnerText()
}
}
© 2022 - 2024 — McMap. All rights reserved.
Spinner
dosen't have a multiple choice mode. – Upbeat