android spinner dropdown checkbox
Asked Answered
F

2

20

I have Spinner like this :

<Spinner
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/spinner1"
                android:background="@drawable/spinner_bg"
                android:popupBackground="@drawable/spinner_bg"/>

this is spinner_bg.xml :

<item>
    <layer-list>
        <item>
            <shape>
                <gradient
                    android:startColor="#ffffff"
                    android:centerColor="#111111"
                    android:endColor="#000000"
                    android:angle="-90" />

                <stroke
                    android:width="2dp"
                    android:color="#ffffff" />

                <corners
                    android:radius="2dp" />

                <padding
                    android:left="10dp"
                    android:right="10dp"/>
            </shape>
        </item>
        <item >
            <bitmap
                android:gravity="right"
                android:src="@android:drawable/arrow_down_float" />
        </item>
    </layer-list>
</item>

this is my code to Custom spinner :

ArrayAdapter<ClassId> adapter = new ArrayAdapter<ClassId>(getActivity(),
                        R.layout.list_id, idList);
                adapter.setDropDownViewResource(R.layout.list_id_select);

this is layout of list_id.xml :

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:singleLine="true"
android:ellipsize="end"
android:textColor="#ff0004"
android:textSize="14sp"
android:paddingTop="10dp"
android:paddingBottom="10dp"/>

this is layout of list_id_select.xml :

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:singleLine="true"
android:ellipsize="end"
android:textColor="#0004ff"
android:textSize="14sp"
android:checked="true"
android:checkMark="@drawable/custom_checkbox"
android:paddingTop="10dp"
android:paddingBottom="10dp"/>

and this is custom_checkbox.xml :

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
    android:drawable="@android:drawable/checkbox_on_background" /> 

<item android:state_pressed="true"
    android:drawable="@android:drawable/checkbox_on_background" /> 

<item android:drawable="@android:drawable/checkbox_off_background" /> 

this is my result when dropdown of spinner show : _________________________________________________
__________________________checkbox_______________
____text_________________________________________

that mean text and checkbox not in line (checkbox higher than text).
how to fix it?

Fantast answered 17/7, 2016 at 4:43 Comment(0)
W
50

For that you have to Create Custom Adapter and set TextView and CheckBox inside that below way.

Define Spinner in xml

<Spinner
        android:id="@+id/spinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp" />

Create spinner_item.xml file in layout folder.

<?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/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:text="text"
        android:textAlignment="gravity" />

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true" />
</RelativeLayout>

Now create StateVO.java class that can contain the TextView and CheckBox value.

public class StateVO {
    private String title;
    private boolean selected;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

Now in your Activity inititlize the Spinner and set CustomAdapter below way.

 final String[] select_qualification = {
                "Select Qualification", "10th / Below", "12th", "Diploma", "UG",
                "PG", "Phd"};
        Spinner spinner = (Spinner) findViewById(R.id.spinner);

        ArrayList<StateVO> listVOs = new ArrayList<>();

        for (int i = 0; i < select_qualification.length; i++) {
            StateVO stateVO = new StateVO();
            stateVO.setTitle(select_qualification[i]);
            stateVO.setSelected(false);
            listVOs.add(stateVO);
        }
        MyAdapter myAdapter = new MyAdapter(Main2Activity.this, 0,
                listVOs);
        spinner.setAdapter(myAdapter);

And Finally Create CustomAdapter class like below way.

MyAdapter.java

public class MyAdapter extends ArrayAdapter<StateVO> {
    private Context mContext;
    private ArrayList<StateVO> listState;
    private MyAdapter myAdapter;
    private boolean isFromView = false;

    public MyAdapter(Context context, int resource, List<StateVO> objects) {
        super(context, resource, objects);
        this.mContext = context;
        this.listState = (ArrayList<StateVO>) objects;
        this.myAdapter = this;
    }

    @Override
    public View getDropDownView(int position, View convertView,
                                ViewGroup parent) {
        return getCustomView(position, convertView, parent);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return getCustomView(position, convertView, parent);
    }

    public View getCustomView(final int position, View convertView,
                              ViewGroup parent) {

        final ViewHolder holder;
        if (convertView == null) {
            LayoutInflater layoutInflator = LayoutInflater.from(mContext);
            convertView = layoutInflator.inflate(R.layout.spinner_item, null);
            holder = new ViewHolder();
            holder.mTextView = (TextView) convertView
                    .findViewById(R.id.text);
            holder.mCheckBox = (CheckBox) convertView
                    .findViewById(R.id.checkbox);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.mTextView.setText(listState.get(position).getTitle());

        // To check weather checked event fire from getview() or user input
        isFromView = true;
        holder.mCheckBox.setChecked(listState.get(position).isSelected());
        isFromView = false;

        if ((position == 0)) {
            holder.mCheckBox.setVisibility(View.INVISIBLE);
        } else {
            holder.mCheckBox.setVisibility(View.VISIBLE);
        }
        holder.mCheckBox.setTag(position);
        holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int getPosition = (Integer) buttonView.getTag();

                if (!isFromView) {
                    listState.get(position).setSelected(isChecked);
                }
            }
        });
        return convertView;
    }

    private class ViewHolder {
        private TextView mTextView;
        private CheckBox mCheckBox;
    }
}

Output :

enter image description here

Walkon answered 17/7, 2016 at 5:40 Comment(4)
I'm getting a strange behavior, when I scroll down or up, the checked boxes convert to unchecked. So whenever the checkbox go up or down (hide from the view) it converts to unchecked!!Thorrlow
Checkbox functionality is working fine but selected values is not adding on the spinnerButch
Is there a way to give offset to dropdown so that dropdown starts below spinner instead of dropdown showing on top of spinner? ThanksLevitan
When clicked out side of dropdown window, it closes. I want to retrieve all the values which are checked when dropdown closes its window and set to one textview. Please help to achieve this.Torsk
L
4

This post is a great resource. For anyone interested, I made a generic adapter based on the other answer that has worked well for multiple data types. Also lets you click the text to toggle the checkbox.

public class CheckableSpinnerAdapter<T> extends BaseAdapter {

    static class SpinnerItem<T> {
        private String txt;
        private T item;

        SpinnerItem(T t, String s) {
            item = t;
            txt = s;
        }
    }

    private Context context;
    private Set<T> selected_items;
    private List<SpinnerItem<T>> all_items;
    private String headerText;

    CheckableSpinnerAdapter(Context context,
                            String headerText,
                            List<SpinnerItem<T>> all_items,
                            Set<T> selected_items) {
        this.context = context;
        this.headerText = headerText;
        this.all_items = all_items;
        this.selected_items = selected_items;
    }

    @Override
    public int getCount() {
        return all_items.size() + 1;
    }

    @Override
    public Object getItem(int position) {
        if( position < 1 ) {
            return null;
        }
        else {
            return all_items.get(position-1);
        }
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    @SuppressWarnings({"unchecked"})
    @NonNull
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {

        final ViewHolder holder;
        if (convertView == null ) {
            LayoutInflater layoutInflator = LayoutInflater.from(context);
            convertView = layoutInflator.inflate(R.layout.checkable_spinner_item, parent, false);

            holder = new ViewHolder();
            holder.mTextView = convertView.findViewById(R.id.text);
            holder.mCheckBox = convertView.findViewById(R.id.checkbox);
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        if( position < 1 ) {
            holder.mCheckBox.setVisibility(View.GONE);
            holder.mTextView.setText(headerText);
        }
        else {
            final int listPos = position - 1;
            holder.mCheckBox.setVisibility(View.VISIBLE);
            holder.mTextView.setText(all_items.get(listPos).txt);

            final T item = all_items.get(listPos).item;
            boolean isSel = selected_items.contains(item);

            holder.mCheckBox.setOnCheckedChangeListener(null);
            holder.mCheckBox.setChecked(isSel);

            holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if( isChecked ) {
                        selected_items.add(item);
                    }
                    else {
                        selected_items.remove(item);
                    }
                }
            });

            holder.mTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    holder.mCheckBox.toggle();
                }
            });
        }

        return convertView;
    }

    private class ViewHolder {
        private TextView mTextView;
        private CheckBox mCheckBox;
    }
} 

And the layout checkable_spinner_item.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/text"
        app:layout_constraintBottom_toBottomOf="@id/text"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:textSize="16sp"
        android:textColor="#000000"
        app:layout_constraintStart_toEndOf="@id/checkbox"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:textAlignment="gravity" />

</android.support.constraint.ConstraintLayout>

You would use it by creating a list of items to show, a Set, which will contain the selected items, and the header row text. For example:

private final List<CheckableSpinnerAdapter.SpinnerItem<MyObject>> spinner_items = new ArrayList<>();
private final Set<MyObject> selected_items = new HashSet<>();

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

    // fill the 'spinner_items' array with all items to show
    List<MyObject> all_objects = getMyObjects(); // from wherever
    for(MyObject o : all_objects) {
        spinner_items.add(new CheckableSpinnerAdapter.SpinnerItem<>(o, o.getName()));
    }

    // to start with any pre-selected, add them to the `selected_items` set

    String headerText = "Click an Item";

    Spinner spinner = findViewById(R.id.my_spinner);
    CheckableSpinnerAdapter adapter = new CheckableSpinnerAdapter<>(this, headerText, spinner_items, selected_items);
    spinner.setAdapter(adapter);

    // when you want to see what the user has selected, look in the `selected_items`
    // set anywhere in your activity
}

An example in use:

Example use

EDIT: MyObject can be any class or enum, whatever you want to associate with the spinner items. If you are copying this example directly it would have to implement a String getName() method. Here's a simple example:

class MyObject {
    private String mName;
    private String mAddr;

    MyObject(String name, String address) {
        mName = name;
        mAddr = address;
    }

    String getName() { return mName; }
    String getAddress() { return mAddr; }
}
Lachrymator answered 13/7, 2018 at 19:1 Comment(2)
Can you post your MyObject classButch
MyObject can be anything. For the example activity here it just has to have a getName function, but you can change that to whatever you want to get the string shown in the spinner for that object. I added a simple example class.Lachrymator

© 2022 - 2024 — McMap. All rights reserved.