in Programming

Android: Spinner-Like UI for Selecting Multiple Options

Download the source code files AndroidMultipleChoice.tar.gz (tar.gz, eclipse project)

Android includes a Spinner control that works very much like a desktop drop-down (or combo-box) control. Tapping a Spinner presents a list of options, and allows the user to select one from the list.

However, in building a small research app, I needed to allow users to select multiple items from the Spinner’s list. From what I could see, though, the Spinner class is not capable of doing this, so I built a small throwaway app that implements this oft-requested functionality.

Getting Started

Spin up a new Android application in Eclipse, creating an activity named MultipleChoiceActivity. For reference, I’m targeting the Google Android 2.1 APIs (Level 7).

Once the project is created, open up and edit the main.xml file to display a simple label and button. I’ve used a Button instead of a Spinner as we can change the button’s label to indicate which options have been selected:

<!-- res/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical"
  android:padding="5dip">
 
  <TextView 
    android:text="Select Colours"
    android:id="@+id/textView1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="5dip" />
 
  <Button 
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:text="- None Selected -"
    android:id="@+id/select_colours"
    android:padding="5dip" />
</LinearLayout>

Building the Activity

For this example, I’m using a hard-coded list of colours here in this example (rather than, for example, hooking up to a database). Start by hooking up the interface to properties in MultipleChoiceActivity.java:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
 
// ... package, imports ...
 
public class MultipleChoiceActivity extends Activity implements OnClickListener {
  protected Button selectColoursButton;
 
  protected CharSequence[] colours = { "Red", "Green", "Blue", "Yellow", "Orange", "Purple" };
  protected ArrayList<CharSequence> selectedColours = new ArrayList<CharSequence>();
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    selectColoursButton = (Button) findViewById(R.id.select_colours);
    selectColoursButton.setOnClickListener(this);
  }
 
  @Override
  public void onClick(View view) {
    switch(view.getId()) {
      case R.id.select_colours:
        // TODO: Show the colours dialog
        break;
 
      default:
        break;
    }
  }
}

With that code, your button is now hooked up to listen for clicks. The colours array is a simple list of colours that will be displayed when we click the button. The selectedColours ArrayList is a dynamic array of the colours that have been selected. Using ArrayList lets us easily add and remove colours when the user changes the selection.

Try running the app in the Android emulator (or on a device) to check that everything works so far.

Displaying the Options

Next, we’ll use Android’s AlertDialog to let us display the list of colours, and allow the user to make selections. AlertDialog comes with built-in functionality to allow multiple selections, and automatically adds checkboxes to our list interface.

Add the following method to MultipleChoiceActivity.java:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
protected void showSelectColoursDialog() {
  boolean[] checkedColours = new boolean[colours.length];
  int count = colours.length;
 
  for(int i = 0; i < count; i++)
    checkedColours[i] = selectedColours.contains(colours[i]);
 
  DialogInterface.OnMultiChoiceClickListener coloursDialogListener = new DialogInterface.OnMultiChoiceClickListener() {
   @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
      if(isChecked)
        selectedColours.add(colours[which]);
      else
        selectedColours.remove(colours[which]);
 
      onChangeSelectedColours();
    }
   };
 
  AlertDialog.Builder builder = new AlertDialog.Builder(this);
  builder.setTitle("Select Colours");
  builder.setMultiChoiceItems(colours, checkedColours, coloursDialogListener);
 
  AlertDialog dialog = builder.create();
  dialog.show();
}

showSelectColoursDialog is the method that will be called when tapping the button. It starts by walking through the colours array and checking which colours are already in the selectedColours ArrayList. The results get stored in an array of booleans (checkedColours) that will be used by the AlertDialog to determine which items in the list are to be checked.

Next, we build a DialogInterface.OnMultiChoiceClickListener that will be invoked when the user checks or unchecks a colour in the list. Depending on the state of the check box (supplied via the isChecked parameter), we add or remove the colour from the selectedColours ArrayList.

Notice that when the listener is finished, we call an as-yet undefined method, onChangeSelectedColours. This will be a callback method that we’ll use to update the button’s label:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
protected void onChangeSelectedColours() {
  StringBuilder stringBuilder = new StringBuilder();
 
  for(CharSequence colour : selectedColours)
    stringBuilder.append(colour + ",");
 
  selectColoursButton.setText(stringBuilder.toString());
}

This simple callback method just walks through the ArrayList of selected colours, and builds a string containing the name of each colour.

Finally, all that’s left to do is hook up our button to call showSelectColoursDialog in the onClick method we overrode earlier:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
@Override
public void onClick(View view) {
  switch(view.getId()) {
    case R.id.select_colours:
      showSelectColoursDialog();
      break;
 
    default:
      break;
  }
}

With everything hooked up, it’s time to test out the app. Run the emulator (or install to a device), tap the button and you’ll be able to select multiple colours from the list. If you tap the back button, the list will close, and your button’s label will have updated to show the colours you tapped. Try tapping the button again, and your previously selected colours will still be checked.

Summary

Hopefully this simple example will be useful to anyone looking to implement multiple-choice options. It would be nice to use the Spinner gadget rather than a Button, perhaps by subclassing Spinner and overriding it’s click handler and label drawing methods. I haven’t tried this – let me know in the comments if you decide to give it a go.

Tweet about this on TwitterShare on Google+Share on FacebookShare on LinkedInEmail this to someone
  • http://simpleonlinemarketing.co.uk/category/blog Simon Little

    Wow – that’s why you’re a developer and I’m just a front of house guy! Great stuff, what other apps are on the horizon for Plymouth Software?

    • http://chrisblunt.com/ Chris Blunt

      Thanks for the comments :) Some new Android goodies are on the horizon.

      Join the mailing list to stay up to date at http://plymouthsoftware.com/signup

  • http://pulse.yahoo.com/_VXOVTNXNWT7REMJERLI346ESQU JettiMadhuChowdary

    Hi ,
     I tried the following ways to know when the user dismisses the dialog. I want to know when user press “ESC” key to dismiss the dialog. 
    But i unable to  get this…
    Is there anything wrong with my code.Please check this

    public class ListViewWithDialogExActivity extends Activity implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener{
    Dialog dialog;
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
           showDialog();
        }

    private void showDialog() {
    // TODO Auto-generated method stub
    dialog = new Dialog(this);
    dialog.setTitle(“The Dialog Title:”);
    LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // LayoutInflater layoutInflater = LayoutInflater.from(this);
    LinearLayout view = (LinearLayout) layoutInflater.inflate(R.layout.main, null,false);
    ListView ll = (ListView)view.findViewById(R.id.listView);
    String[] strArray = new String[]{“A”,”B”,”C”,”D”,”E”,”F”,”G”,”H”,”I”,”J”,”K”,”L”,”M”};
    ll.setAdapter(new ArrayAdapter(getApplicationContext(), android.R.layout.simple_list_item_1,strArray));
    dialog.setContentView(view);
    dialog.show();
    dialog.setCancelable(true);
    ll.setOnItemClickListener(listener);
    }
    OnItemClickListener listener = new OnItemClickListener() {

    public void onItemClick(AdapterView arg0, View arg1, int arg2,
    long arg3) {
    // TODO Auto-generated method stub
    System.out.println(“item clicked is : “+arg0.getItemAtPosition(arg2));
    AlertDialog.Builder builder = new AlertDialog.Builder(ListViewWithDialogExActivity.this);
    builder.setMessage(“Edina clicke chesko kani em jaragadu”);

    builder.setPositiveButton(“OK”, new OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
    // TODO Auto-generated method stub
    System.out.println(” OK clicked ” );
    ListViewWithDialogExActivity.this.finish();
    }
    });
    builder.setNegativeButton(“Cancel”, new OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
    // TODO Auto-generated method stub
    System.out.println(“cancel clicked “);
    dialog.dismiss();
    }
    });
    AlertDialog alertDialog = builder.create();
    alertDialog.show();
    }
    };
    public void onBackPressed() {
    Toast.makeText(getApplicationContext(), “onback”, 200).show();
    System.out.println(“onback clicked “);
    };
    /* (non-Javadoc)
    * @see android.content.DialogInterface.OnDismissListener#onDismiss(android.content.DialogInterface)
    */
    public void onDismiss(DialogInterface dialog) {
    // TODO Auto-generated method stub
    Toast.makeText(getApplicationContext(), “dialogInterface”, 200).show();
    System.out.println(“dialogInterface clicked “);
    }
    /* (non-Javadoc)
    * @see android.content.DialogInterface.OnCancelListener#onCancel(android.content.DialogInterface)
    */
    public void onCancel(DialogInterface dialog) {
    // TODO Auto-generated method stub
    Toast.makeText(getApplicationContext(), “dialogInterface cancelm ,,”, 200).show();
    System.out.println(“dialogInterface cancel,,clickedm “);
    }

    public boolean onKeyDown(int keyCode, KeyEvent event) {
       if (keyCode == KeyEvent.KEYCODE_BACK)
       {
           new AlertDialog.Builder(this).setMessage(“Do you want to exit the application?”)
           .setPositiveButton(“YES”, new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int which) {
                   // TODO Auto-generated method stub
                   finish();
               }
           }).setNegativeButton(“NO”, null).show();
       
        Toast.makeText(getApplicationContext(), “dialogInterface cancelm  back,,”, 200).show();
       }
       return super.onKeyDown(keyCode, event);

    }

  • http://www.facebook.com/people/Alex-Busuioc/100000991321764 Alex Busuioc

    Helped me resolve my problem, Thank you !

  • Kelvin Kong

    Hi Chris, thanks for this quick and excellent guide! 

    I’ve been trying to implement an ArrayAdapter or ArrayList into this dialog’s builder, as I need the ability to multiselect, and at same time the list items are going to be dynamic(fetching from database) but it can’t seem to work. Can you please enlighten me a little? Thanks! =)

    • http://chrisblunt.com/ Chris Blunt

      Hi Kelvin,

      Not sure I understand your requirements exactly, but my (untested) approach would be to use a SimpleCursorAdapter (or subclass) to populate the ListView with data, and then store the record ids of each selected item in the ArrayList (i.e. your selectedColours ArrayList is would just be a list of long integers).

      Hope that helps, let me know how you get on.

      Chris

  • tamas

    Hy
    If mí text is more then 2 lines long then I need to style the AlertDialog.
    I did it,but now it doesn’t check its items.
    strings.xml:

        Hello World, MultipleChoiceActivity!
        MultipleChoice
        
            #00FF00
            monospace
            false
            textMultiLine
            10sp
       

    MultipleChoiceActivity.java:
    package com.plymouthsoftware.android.multiplechoice;

    import java.util.ArrayList;

    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.ContextThemeWrapper;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;

    public class MultipleChoiceActivity extends Activity implements OnClickListener {
        protected Button selectColoursButton;

        protected CharSequence[] colours = { “Red The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog”, “Green”, “Blue”, “Yellow”, “Orange”, “Purple” };
        protected ArrayList selectedColours = new ArrayList();

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            selectColoursButton = (Button) findViewById(R.id.select_colours);
            selectColoursButton.setOnClickListener(this);
        }

        public void onClick(View view) {
            switch(view.getId()) {
                case R.id.select_colours:
                    showSelectColoursDialog();
                    break;

                default:
                    break;
            }
        }

        protected void onChangeSelectedColours() {
            StringBuilder stringBuilder = new StringBuilder();

            for(CharSequence colour : selectedColours)
                stringBuilder.append(colour + “,”);
           
            selectColoursButton.setText(stringBuilder.toString());
        }

        protected void showSelectColoursDialog() {
            boolean[] checkedColours = new boolean[colours.length];
            int count = colours.length;

            for(int i = 0; i < count; i++)
                checkedColours[i] = selectedColours.contains(colours[i]);

            DialogInterface.OnMultiChoiceClickListener coloursDialogListener = new DialogInterface.OnMultiChoiceClickListener() {
                public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                    if(isChecked)
                        selectedColours.add(colours[which]);
                    else
                        selectedColours.remove(colours[which]);

                    onChangeSelectedColours();
                }
            };

            AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AlertDialogCustom));
            builder.setTitle("Select Colours");
            builder.setMultiChoiceItems(colours, checkedColours, coloursDialogListener);

            AlertDialog dialog = builder.create();
            dialog.show();
        }
    }

    Any idea?
    onClick is not called in this way.

    • http://chrisblunt.com/ Chris Blunt

      Hi Tamas,

      After a little research, I thought the solution might be to subclass AlertDialog.Builder. Unfortunately, though, this still didn’t trigger onClick. It seems that until API 11, you can’t easily theme AlertDialogs as the theme is hard-coded into the class.

      You might have more luck by styling the list items themselves rather than the AlertDialog, as hinted at in this post http://stackoverflow.com/questions/6698350/android-how-to-set-text-color-for-list-items-in-alertdialog-properly/6701622#6701622

    • tamas

      Hello!
      Thank youm but its not multiple in this way, I can only Select one and there isn’t checkbox.