Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Android

Jourdan Dixon
Jourdan Dixon
4,752 Points

Spinner as Multi Select Drop Down List

I'm working to create an app where I'll need to make some spinners as drop down lists on Activity A where the user can multi select values. I'll then pass those values to Activity B where it will display a list filtered based on those values selected. That alone is turning into not an easy task as I'm trying to do it as clean as possible. But I also need each item in the list to have 2 values; an id and title (id to be unseen by the EU). The query will run based on the ids but the EU will only see the titles. Does ANYONE have a resource to help with something like this? I've looked it seems everywhere.

This part is much less important. Bread and butter, but if possible I'd like the app to remember what items were last selected. If that's too much then at least have default selected values (Select All).

I know I'm kind of asking a lot, but any resource help anyone can provide would be awesome.

2 Answers

Jourdan Dixon
Jourdan Dixon
4,752 Points

Thanks. I actually ended up using bits from a few classes I found online and extending them to create Clear All and Select All buttons. I created this class:

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class MultiSelectSpinner extends android.support.v7.widget.AppCompatSpinner implements OnMultiChoiceClickListener {
    private String[] _items = null;
    private boolean[] _selection = null;

    private final ArrayAdapter<String> _proxyAdapter;

    public MultiSelectSpinner(Context context) {
        super(context);

        _proxyAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item);
        super.setAdapter(_proxyAdapter);
    }

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

        _proxyAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item);
        super.setAdapter(_proxyAdapter);
    }

    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
        if (_selection != null && which < _selection.length) {
            _selection[which] = isChecked;

            _proxyAdapter.clear();
            _proxyAdapter.add(buildSelectedItemString());
            setSelection(0);
        }
        else {
            throw new IllegalArgumentException("Argument 'which' is out of bounds.");
        }
    }

    @Override
    public boolean performClick() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setMultiChoiceItems(_items, _selection, this);
        builder.setPositiveButton("Submit", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });
        builder.show();
        return true;
    }

    @Override
    public void setAdapter(SpinnerAdapter adapter) {
        throw new RuntimeException("setAdapter is not supported by MultiSelectSpinner.");
    }

    public void setItems(String[] items) {
        _items = items;
        _selection = new boolean[_items.length];

        Arrays.fill(_selection, false); // true defaults to checked, false defaults to unchecked
        _proxyAdapter.clear();
        _proxyAdapter.add(buildSelectedItemString());
    }

    public void setItemsChecked(String[] items) {
        _items = items;
        _selection = new boolean[_items.length];

        Arrays.fill(_selection, true); // true defaults to checked, false defaults to unchecked
        _proxyAdapter.clear();
        _proxyAdapter.add(buildSelectedItemString());
    }

    public void setItemsSaved(String[] items, String saved) {
        _items = items;
        _selection = new boolean[_items.length];

        StringBuilder sb = new StringBuilder();
        boolean foundOne = false;

        for (int i = 0; i < _items.length; ++i) {
            _selection[i] = saved.toLowerCase().contains(_items[i].toLowerCase());

            if (_selection[i]) {
                if (foundOne) {
                    sb.append(", ");
                }
                foundOne = true;

                sb.append(_items[i]);
            }
        }

        _proxyAdapter.clear();
        _proxyAdapter.add(buildSelectedItemString());
    }

    public void setItems(List<String> items) {
        _items = items.toArray(new String[items.size()]);
        _selection = new boolean[_items.length];

        Arrays.fill(_selection, false); // true defaults to checked, false defaults to unchecked
        _proxyAdapter.clear();
        _proxyAdapter.add(buildSelectedItemString());
    }

    public void setItemsChecked(List<String> items) {
        _items = items.toArray(new String[items.size()]);
        _selection = new boolean[_items.length];

        Arrays.fill(_selection, true); // true defaults to checked, false defaults to unchecked
        _proxyAdapter.clear();
        _proxyAdapter.add(buildSelectedItemString());
    }

    public void setSelection(String[] selection) {
        for (String sel : selection) {
            for (int j = 0; j < _items.length; ++j) {
                if (_items[j].equals(sel)) {
                    _selection[j] = true;
                }
            }
        }
    }

    public void setSelection(List<String> selection) {
        for (String sel : selection) {
            for (int j = 0; j < _items.length; ++j) {
                if (_items[j].equals(sel)) {
                    _selection[j] = true;
                }
            }
        }
    }

    public void setSelection(int[] selectedIndicies) {
        for (int index : selectedIndicies) {
            if (index >= 0 && index < _selection.length) {
                _selection[index] = true;
            }
            else {
                throw new IllegalArgumentException("Index " + index + " is out of bounds.");
            }
        }
    }

    public List<String> getSelectedStrings() {
        List<String> selection = new LinkedList<>();
        for (int i = 0; i < _items.length; ++i) {
            if (_selection[i]) {
                selection.add(_items[i]);
            }
        }
        return selection;
    }

    public List<Integer> getSelectedIndicies() {
        List<Integer> selection = new LinkedList<>();
        for (int i = 0; i < _items.length; ++i) {
            if (_selection[i]) {
                selection.add(i);
            }
        }
        return selection;
    }

    private String buildSelectedItemString() {
        StringBuilder sb = new StringBuilder();
        boolean foundOne = false;

        for (int i = 0; i < _items.length; ++i) {
            if (_selection[i]) {
                if (foundOne) {
                    sb.append(", ");
                }
                foundOne = true;

                sb.append(_items[i]);
            }
        }

        return sb.toString();
    }
}

and this is the Activity:

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.clumsygiant.ultimateimprov.MultiSelectSpinner;
import com.clumsygiant.ultimateimprov.R;

public class GameMainActivity extends Activity {

    Intent mIntent = new Intent();
    Context mContext = GameMainActivity.this;
    String mToastText = "";

    private String mCheckBoxGameCatString;
    private String mCheckBoxGameSubCatString;
    private String mShowRandomString = "false";
    public static boolean mShowRandomStringBoolean;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game_main);

        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
        final SharedPreferences.Editor edit = prefs.edit();
        mCheckBoxGameCatString = prefs.getString(getString(R.string.game_cat), "");
        mCheckBoxGameSubCatString = prefs.getString(getString(R.string.game_sub_cat), "");

        final String[] stringsGameCat = getResources().getStringArray(R.array.game_cat_array);
        final MultiSelectSpinner multiSpinnerGameCat = (MultiSelectSpinner) findViewById(R.id.multiSpinnerGameCat);
        if (mCheckBoxGameCatString.replace("'","").replace(" ","") == "") {
            multiSpinnerGameCat.setItemsChecked(stringsGameCat);
        } else {
            multiSpinnerGameCat.setItemsSaved(stringsGameCat,mCheckBoxGameCatString);
        }

        final String[] stringsGameSubCat = getResources().getStringArray(R.array.game_sub_cat_array);
        final MultiSelectSpinner multiSpinnerGameSubCat = (MultiSelectSpinner) findViewById(R.id.multiSpinnerGameSubCat);
        if (mCheckBoxGameSubCatString.replace("'","") == "") {
            multiSpinnerGameSubCat.setItemsChecked(stringsGameSubCat);
        } else {
            multiSpinnerGameSubCat.setItemsSaved(stringsGameSubCat,mCheckBoxGameSubCatString);
        }

        Button buttonGameCatSelectAll = (Button) findViewById(R.id.buttonGameCatSelectAll);

        buttonGameCatSelectAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                multiSpinnerGameCat.setItemsChecked(stringsGameCat);
            }
        });

        Button buttonGameCatSelectNone = (Button) findViewById(R.id.buttonGameCatSelectNone);

        buttonGameCatSelectNone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                multiSpinnerGameCat.setItems(stringsGameCat);
            }
        });

        Button buttonGameSubCatSelectAll = (Button) findViewById(R.id.buttonGameSubCatSelectAll);

        buttonGameSubCatSelectAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                multiSpinnerGameSubCat.setItemsChecked(stringsGameSubCat);
            }
        });

        Button buttonGameSubCatSelectNone = (Button) findViewById(R.id.buttonGameSubCatSelectNone);

        buttonGameSubCatSelectNone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                multiSpinnerGameSubCat.setItems(stringsGameSubCat);
            }
        });

        Button buttonGameList = (Button) findViewById(R.id.listGameButton);
        Button buttonGameRandom = (Button) findViewById(R.id.randomGameButton);

        buttonGameList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (multiSpinnerGameCat.getSelectedStrings().toString() == "[]" || multiSpinnerGameSubCat.getSelectedStrings().toString() == "[]") {

                    missingMultiSpinnerToast();

                }
                else {

                    mCheckBoxGameCatString = "'" + multiSpinnerGameCat.getSelectedStrings().toString().replace("[", "").replace("]", "").replace(", ", "','") + "'";
                    mCheckBoxGameSubCatString = "'" + multiSpinnerGameSubCat.getSelectedStrings().toString().replace("[", "").replace("]", "").replace(", ", "','") + "'";
                    mShowRandomString = "false";
                    mShowRandomStringBoolean = false;

                    edit.putString(getString(R.string.game_cat), mCheckBoxGameCatString).apply();
                    edit.putString(getString(R.string.game_sub_cat), mCheckBoxGameSubCatString).apply();
                    edit.putString(getString(R.string.show_random_boolean), mShowRandomString).apply();

                    mIntent = new Intent(mContext, GameListActivity.class);
                    startActivity(mIntent);

                }
            }
        });

        buttonGameRandom.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (multiSpinnerGameCat.getSelectedStrings().toString() == "[]" || multiSpinnerGameSubCat.getSelectedStrings().toString() == "[]") {

                    missingMultiSpinnerToast();

                }
                else {

                    mCheckBoxGameCatString = "'" + multiSpinnerGameCat.getSelectedStrings().toString().replace("[", "").replace("]", "").replace(", ", "','") + "'";
                    mCheckBoxGameSubCatString = "'" + multiSpinnerGameSubCat.getSelectedStrings().toString().replace("[", "").replace("]", "").replace(", ", "','") + "'";
                    mShowRandomString = "true";
                    mShowRandomStringBoolean = true;

                    edit.putString(getString(R.string.game_cat), mCheckBoxGameCatString).apply();
                    edit.putString(getString(R.string.game_sub_cat), mCheckBoxGameSubCatString).apply();
                    edit.putString(getString(R.string.show_random_boolean), mShowRandomString).apply();

                    mIntent = new Intent(mContext, GameListActivity.class);
                    startActivity(mIntent);

                }
            }
        });
    }

    private void missingMultiSpinnerToast() {
        mToastText = getString(R.string.missing_game_filters);
        Toast.makeText(mContext, mToastText, Toast.LENGTH_LONG).show();
    }
}

On the detail activity, I use a SQLite database and custom SQL. I inject the values from these multi-select drop down lists into a csv format. It took a bit to get it all worked out, but it runs well now.