77

I want to bring up a spinner dialog when the user taps a menu item to allow the user to select an item.

Do I need a separate dialog for this or can I use Spinner directly? I see this link, mentions a MODE_DIALOG option but it doesn't seem to be defined anymore. AlertDialog may be OK but all the options say "clicking on an item in the list will not dismiss the dialog" which is what I want. Any suggestion?

Ideally, the code would be similar to the case where the spinner is shown on the screen:

ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity,
     android.R.layout.simple_spinner_item, items);              
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
myspinner.setAdapter(adapter);  
// myspinner.showAsDialog() <-- what i want             
KRP
  • 294
  • 7
  • 22
Edwin Evans
  • 2,726
  • 5
  • 34
  • 47

11 Answers11

128

You can use an alert dialog

    AlertDialog.Builder b = new Builder(this);
    b.setTitle("Example");
    String[] types = {"By Zip", "By Category"};
    b.setItems(types, new OnClickListener() {
        
        @Override
        public void onClick(DialogInterface dialog, int which) {
            
            dialog.dismiss();
            switch(which){
            case 0:
                onZipRequested();
                break;
            case 1:
                onCategoryRequested();
                break;
            }
        }

    });

    b.show();

This will close the dialog when one of them is pressed like you are wanting.

starball
  • 20,030
  • 7
  • 43
  • 238
Nathan Schwermann
  • 31,285
  • 16
  • 80
  • 91
  • Thanks! How do I set one of the radio buttons to be initially selected? (I'm using this for a user to select a value but there is already a current value so that should be indicated.) – Edwin Evans Jun 09 '11 at 17:43
  • 4
    use `setSingleChoiceItems()` instead has an int argument to denote which item to set selected first. – Nathan Schwermann Jun 09 '11 at 17:59
  • 1
    I get errors in this code, one of which is `The method onClick(DialogInterface, int) of type new View.OnClickListener(){} must override or implement a supertype method` – Michael Sep 22 '13 at 00:09
  • 5
    ah never mind, it was referencing a different OnClickListener. had to use `new DialogInterface.OnclickListener...` instead. – Michael Sep 22 '13 at 00:14
  • Wow I didn't know you could do this :) I used the setItems(@ArrayRes) function which is useful if you have a handful of options that won't change – Daniel Wilson May 15 '17 at 08:07
115

In xml there is option

android:spinnerMode="dialog"

use this for Dialog mode

Gowthaman M
  • 8,057
  • 8
  • 35
  • 54
user2582324
  • 1,220
  • 1
  • 9
  • 10
20

Try this:

Spinner popupSpinner = new Spinner(context, Spinner.MODE_DIALOG);

See this link for more details.

Gowthaman M
  • 8,057
  • 8
  • 35
  • 54
haotang
  • 5,520
  • 35
  • 46
12

MODE_DIALOG and MODE_DROPDOWN are defined in API 11 (Honeycomb). MODE_DIALOG describes the usual behaviour in previous platform versions.

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
adamp
  • 28,862
  • 9
  • 81
  • 69
10

Adding a small attribute as android:spinnerMode="dialog" would show the spinner contents in a pop-up.

San
  • 2,078
  • 1
  • 24
  • 42
8

You can create your own custom Dialog. It's fairly easy. If you want to dismiss it with a selection in the spinner, then add an OnItemClickListener and add

int n = mSpinner.getSelectedItemPosition();
mReadyListener.ready(n);
SpinnerDialog.this.dismiss();

as in the OnClickListener for the OK button. There's one caveat, though, and it's that the onclick listener does not fire if you reselect the default option. You need the OK button also.

Start with the layout:

res/layout/spinner_dialog.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content">
<TextView 
    android:id="@+id/dialog_label" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent"
    android:hint="Please select an option" 
    />
<Spinner
    android:id="@+id/dialog_spinner" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent"
    />
<Button
    android:id="@+id/dialogOK" 
    android:layout_width="120dp"
    android:layout_height="wrap_content" 
    android:text="OK"
    android:layout_below="@id/dialog_spinner"
    />
<Button
    android:id="@+id/dialogCancel" 
    android:layout_width="120dp"
    android:layout_height="wrap_content" 
    android:text="Cancel"
    android:layout_below="@id/dialog_spinner"
    android:layout_toRightOf="@id/dialogOK"
    />
</RelativeLayout>

Then, create the class:

src/your/package/SpinnerDialog.java:

public class SpinnerDialog extends Dialog {
    private ArrayList<String> mList;
    private Context mContext;
    private Spinner mSpinner;

   public interface DialogListener {
        public void ready(int n);
        public void cancelled();
    }

    private DialogListener mReadyListener;

    public SpinnerDialog(Context context, ArrayList<String> list, DialogListener readyListener) {
        super(context);
        mReadyListener = readyListener;
        mContext = context;
        mList = new ArrayList<String>();
        mList = list;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.spinner_dialog);
        mSpinner = (Spinner) findViewById (R.id.dialog_spinner);
        ArrayAdapter<String> adapter = new ArrayAdapter<String> (mContext, android.R.layout.simple_spinner_dropdown_item, mList);
        mSpinner.setAdapter(adapter);

        Button buttonOK = (Button) findViewById(R.id.dialogOK);
        Button buttonCancel = (Button) findViewById(R.id.dialogCancel);
        buttonOK.setOnClickListener(new android.view.View.OnClickListener(){
            public void onClick(View v) {
                int n = mSpinner.getSelectedItemPosition();
                mReadyListener.ready(n);
                SpinnerDialog.this.dismiss();
            }
        });
        buttonCancel.setOnClickListener(new android.view.View.OnClickListener(){
            public void onClick(View v) {
                mReadyListener.cancelled();
                SpinnerDialog.this.dismiss();
            }
        });
    }
}

Finally, use it as:

mSpinnerDialog = new SpinnerDialog(this, mTimers, new SpinnerDialog.DialogListener() {
  public void cancelled() {
    // do your code here
  }
  public void ready(int n) {
    // do your code here
  }
});
Alberto M
  • 1,608
  • 1
  • 18
  • 42
Aleadam
  • 40,203
  • 9
  • 86
  • 108
  • Thanks, but wouldn't this create a dialog with a spinner widget on it and then the user would need to click on the spinner again to get the dialog? I found an answer at http://stackoverflow.com/questions/867518/how-to-make-an-android-spinner-with-initial-text-select-one. See HRJ's answer. – Edwin Evans Jun 09 '11 at 03:04
  • @Edwin, I'm lost with your comment. This would create a dialog with a spinner there. Yes, the user would need to click on the spinner to select the item, but that's how a spinner works. Then, clicking on the OK button (or if you add the onclicklistener as above, just by selecting an item), the selected item index is returned and the dialog is dismissed. If that's not what you need, then I have no idea of what you're asking. – Aleadam Jun 09 '11 at 03:31
  • haha oh good, i was wondering, sometimes i start answering something and then realize to do it properly would probably take a lot more time than it was worth, but it certainly is nice to have it laid out for you. – dylan murphy Jun 09 '11 at 03:39
  • @Aleadam. I edited the question to be a little more clear now. I want it to go directly to the UI that appears after the user has clicked the spinner in its collapsed state. – Edwin Evans Jun 09 '11 at 17:54
  • The constructor DeleteDialog(..) should be called SpinnerDialog(..), I suggested an edit but was unable to log in at the time time leave a comment. – MrCeeJ Nov 30 '12 at 13:47
  • 1
    @Aleadam in the class definition the "DeleteDialog" method name should actually be "SpinnerDialog". Likewise on the usage example it should instantiate "SpinnerDialog.DialogListener" rather than the erroneous "SpinnerDialog.SpinnerListener". – Lord of Scripts Mar 12 '14 at 16:06
3

You can use a spinner and set the spinnerMode to dialog, and set the layout_width and layout_height to 0, so that the main view does not show, only the dialog (dropdown view). Call performClick in the button click listener.

    mButtonAdd.setOnClickListener(view -> {
        spinnerAddToList.performClick();
    });

Layout:

    <Spinner
        android:id="@+id/spinnerAddToList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:prompt="@string/select_from_list"
        android:theme="@style/ThemeOverlay.AppCompat.Light"
        android:spinnerMode="dialog"/>

The advantage of this is you can customize your spinner any way you want.

See my answer here to customize spinner: Overriding dropdown list style for Spinner in Dialog mode

live-love
  • 48,840
  • 22
  • 240
  • 204
2

Here is an Spinner subclass which overrides performClick() to show a dialog instead of a dropdown. No XML required. Give it a try, let me know if it works for you.

public class DialogSpinner extends Spinner {
    public DialogSpinner(Context context) {
        super(context);
    }

    @Override 
    public boolean performClick() {
        new AlertDialog.Builder(getContext()).setAdapter((ListAdapter) getAdapter(), 
            new DialogInterface.OnClickListener() {
                @Override public void onClick(DialogInterface dialog, int which) {
                    setSelection(which);
                    dialog.dismiss();
                }
            }).create().show();
        return true;
    }
}

For more information read this article: How To Make Android Spinner Options Popup In A Dialog

Roger Keays
  • 3,117
  • 1
  • 31
  • 23
0

This is from the Android SDK source code. As you can see you have a special constructor to create a Spinner with the specified mode you wanna use.

Hope it will help you :)

 /**
     * Construct a new spinner with the given context's theme, the supplied attribute set,
     * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or
     * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
     *
     * @param context The Context the view is running in, through which it can
     *        access the current theme, resources, etc.
     * @param attrs The attributes of the XML tag that is inflating the view.
     * @param defStyle The default style to apply to this view. If 0, no style
     *        will be applied (beyond what is included in the theme). This may
     *        either be an attribute resource, whose value will be retrieved
     *        from the current theme, or an explicit style resource.
     * @param mode Constant describing how the user will select choices from the spinner.
     * 
     * @see #MODE_DIALOG
     * @see #MODE_DROPDOWN
     */
    public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
        super(context, attrs, defStyle);
Ektos974
  • 999
  • 10
  • 30
0

If you want to show it as a full screen popup, then you don't even need an xml layout. Here's how do do it in Kotlin.

 val inputArray: Array<String> = arrayOf("Item 1","Item 2")
                val alt_bld =  AlertDialog.Builder(context);
                alt_bld.setTitle("Items:")
                alt_bld.setSingleChoiceItems(inputArray, -1) { dialog, which ->
                    if(which == 0){
                        //Item 1 Selected
                    }
                    else if(which == 1){
                        //Item 2 Selected
                    }
                    dialog.dismiss();

                }

                val alert11 = alt_bld.create()
                alert11.show()
grantespo
  • 2,233
  • 2
  • 24
  • 62
0

Here is a Kotlin version based on the accepted answer.

I'm using this dialog from an adapter, every time a button is clicked.

yourButton.setOnClickListener {
    showDialog(it /*here I pass additional arguments*/)
}

In order to prevent double clicks I immediately disable the button, and re-enable after the action is executed / cancelled.

private fun showDialog(view: View /*additional parameters*/) {
    view.isEnabled = false

    val builder = AlertDialog.Builder(context)
    builder.setTitle(R.string.your_dialog_title)

    val options = arrayOf("Option A", "Option B")

    builder.setItems(options) { dialog, which ->
        dialog.dismiss()

        when (which) {
            /* execute here your actions */
            0 -> context.toast("Selected option A")
            1 -> context.toast("Selected option B")
        }

        view.isEnabled = true
    }

    builder.setOnCancelListener {
        view.isEnabled = true
    }

    builder.show()
}

You can use this instead of a context variable if you are using it from an Activity.

JCarlosR
  • 1,598
  • 3
  • 19
  • 32