Android Listview with spinner and a checkbox

32,724

Solution 1

EDIT - please see comments, this solution may not be correct

I know this question ancient, but it is the first result on Google and I am working on an application that uses Spinners in a ListView as well. I used some sample code from here to get started. I hope this example answers your question. I didn't implement the CheckBoxes but they're very similar to the Spinner - much easier, in fact. This example has a ListView with a TextView and a Spinner. Whenever the user changes a selection in the spinner, the TextView changes to reflect this.

I divided this project up into 3 classes:

  • ListViewTestActivity - main activity
  • DataAdapter - extends ArrayAdapter and works to display the elements in the ListView
  • DataHolder - simple object that just holds some information about the element. This could be implemented in many other ways to suit your needs.

There are also 3 key Android XML files I modified / created:

  • main.xml - modified - the main layout
  • rowview.xml - added - the layout for each element in the ListView
  • strings.xml - modified - the default Android strings file

To start from the bottom up, this main.xml file only contains a single ListView, and nothing else:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" 
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <ListView android:id="@+id/listView1" android:layout_height="match_parent" android:layout_width="match_parent" />
</LinearLayout>

And here is the rowview.xml. Remember that this view is duplicated for each row in the ListView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content" android:weightSum="1">
    <TextView android:layout_width="wrap_content"
        android:layout_height="match_parent" android:id="@+id/text"
        android:layout_weight="0.5" android:textSize="25sp" />
    <Spinner android:layout_width="0dp" android:layout_height="wrap_content"
        android:id="@+id/spin" android:prompt="@string/choice_prompt"
        android:layout_weight="0.5" />
</LinearLayout>

The strings.xml file. All I added was an array for the contents of the spinner:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, ListViewTestActivity!</string>
    <string name="app_name">ListViewTest</string>
    <string name="choice_prompt">Select a choice</string>
    <string-array name="choices">
        <item>Alpha</item>
        <item>Bravo</item>
        <item>Charlie</item>
    </string-array>
</resources>

Now for the fun stuff. The ListViewActivity class:

public class ListViewTestActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ListView listView = (ListView) findViewById(R.id.listView1);

        DataHolder data = new DataHolder(this);
        DataHolder data1 = new DataHolder(this);
        DataHolder data2 = new DataHolder(this);
        DataHolder data3 = new DataHolder(this);
        DataHolder data4 = new DataHolder(this);

        DataAdapter d = new DataAdapter(this, R.layout.rowview, new DataHolder[] { data, data1, data2, data3, data4 });

        listView.setAdapter(d);
    }
}

It's pretty simple, you just get the list, make a new adapter, and set the ListView's adapter to the one you made. This is the DataHolder class:

public class DataHolder {

    private int selected;
    private ArrayAdapter<CharSequence> adapter;

    public DataHolder(Context parent) {
        adapter = ArrayAdapter.createFromResource(parent, R.array.choices, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    }

    public ArrayAdapter<CharSequence> getAdapter() {
        return adapter;
    }

    public String getText() {
        return (String) adapter.getItem(selected);
    }

    public int getSelected() {
        return selected;
    }

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

}

All the DataHolder class does is hold the Spinner's adapter and whatever other information you might want to store for each entry in the ListView (you may want to store whether it is checked or not, for example). And finally the real "meat" of the app, the DataAdapter class:

public class DataAdapter extends ArrayAdapter<DataHolder> {

    private Activity myContext;

    public DataAdapter(Activity context, int textViewResourceId, DataHolder[] objects) {
        super(context, textViewResourceId, objects);
        myContext = context;
    }

    // We keep this ViewHolder object to save time. It's quicker than findViewById() when repainting.
    static class ViewHolder {
        protected DataHolder data;
        protected TextView text;
        protected Spinner spin;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = null;

        // Check to see if this row has already been painted once.
        if (convertView == null) {

            // If it hasn't, set up everything:
            LayoutInflater inflator = myContext.getLayoutInflater();
            view = inflator.inflate(R.layout.rowview, null);

            // Make a new ViewHolder for this row, and modify its data and spinner:
            final ViewHolder viewHolder = new ViewHolder();
            viewHolder.text = (TextView) view.findViewById(R.id.text);
            viewHolder.data = new DataHolder(myContext);
            viewHolder.spin = (Spinner) view.findViewById(R.id.spin);
            viewHolder.spin.setAdapter(viewHolder.data.getAdapter());

            // Used to handle events when the user changes the Spinner selection:
            viewHolder.spin.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
                    viewHolder.data.setSelected(arg2);
                    viewHolder.text.setText(viewHolder.data.getText());
                }

                @Override
                public void onNothingSelected(AdapterView<?> arg0) {
                }

            });

            // Update the TextView to reflect what's in the Spinner
            viewHolder.text.setText(viewHolder.data.getText());

            view.setTag(viewHolder);

            Log.d("DBGINF", viewHolder.text.getText() + "");
        } else {
            view = convertView;
        }

        // This is what gets called every time the ListView refreshes
        ViewHolder holder = (ViewHolder) view.getTag();
        holder.text.setText(getItem(position).getText());
        holder.spin.setSelection(getItem(position).getSelected());

        return view;
    }
}

Here's a screenshot of the final app (it's not very pretty, but it does work):

AppPic

And that's it! I hope I answered your question and helped anyone else who stumbled upon it like I did. If you want to dynamically change the data in the list, use the DataAdapter's add(), remove(), get(), and set() methods. To change the data for each individual spinner, you need to modify the DataHolder class. The SpinnerAdapter is created there, so you just need to dynamically generate the adapters depending on the database response.

Solution 2

In order to make things like that work one has to store all data in adapter, not in ListView items. Any view that represents a ListView item could be reused to display another item, making any data stored in itself irrelevant until the proper data is set again in getView.

Share:
32,724
Anji
Author by

Anji

Updated on January 08, 2020

Comments

  • Anji
    Anji over 4 years

    I am a newbie to android development. I am trying to create a List which has a spinner, a edit text and a check box. The data for spinner and check box come from data base. I have the following files.

    NewTransac class which extends ListActivity {
    
    private PayDbAdapter mDbHelper;
    private  Spinner paySpinner;
    private CheckBox mCheckBox;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.new_transac_listview);
         mDbHelper = new PayDbAdapter(this);
         mDbHelper.open();
    
         populatedata();
    }
    
    private void populatedata() {
    
        paySpinner = (Spinner)findViewById(R.id.payerspinner);
        mCheckBox = (CheckBox)findViewById(R.id.paidforcheckboxname);
    
        Cursor mCursor = mDbHelper.fetchAllTransactionValue();
        startManagingCursor(mCursor);
    
        // Create an array to specify the fields we want to display in the list.
        String[] from = new String[]{PayDbAdapter.KEY_NAME};
    
        int[] to = new int[]{android.R.id.text1};
        int[] cbto = new int[]{R.id.paidforcheckboxname};
    
        // Now create a simple cursor adapter and set it to display
        SimpleCursorAdapter adapter =
            new SimpleCursorAdapter(this, android.R.layout.simple_spinner_item, mCursor, from, to );
    
        adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
        paySpinner.setAdapter(adapter);
    
        SimpleCursorAdapter cbAdapter =
            new SimpleCursorAdapter(this, R.layout.show_new_transac_data, mCursor, from, cbto );
        setListAdapter(cbAdapter);
    }
    

    The list view xml

    <ListView android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:drawSelectorOnTop="false"
        android:textSize="14sp"
    />
    
    <TextView android:id="@android:id/empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/no_friends"
        android:textSize="14sp"
    />
    
    <Button android:id="@+id/confirmpay" 
        android:text="@string/confirm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:gravity="center_vertical|center_horizontal" 
        android:layout_gravity="center_vertical|center_horizontal|center">
    </Button>
    

    list view filled xml

    <TextView
        style="?android:attr/listSeparatorTextViewStyle"
        android:text="@string/listSeparatorPay"
        android:layout_marginTop="5dip"
        android:layout_marginBottom="5dip"
    />
    
    <Spinner android:id="@+id/payerspinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawSelectorOnTop="true"
        android:prompt="@string/selectpayer"
    />
    
    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="@string/paytext"
    />
    
    <EditText android:id="@+id/payamount" 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:layout_weight="1" 
        android:inputType="text"
    />
    
    <TextView
        style="?android:attr/listSeparatorTextViewStyle"
        android:text="@string/listSeparatorPayedFor"
        android:layout_marginTop="5dip"
        android:layout_marginBottom="5dip"
    />
    
    <CheckBox android:id="@+id/paidforcheckboxname"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    />
    
    <EditText android:id="@+id/paidforamount"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
    />
    

    Problem
    I get multiple spinners, checkboxes and edittext based on the number of fields in the database. I see that we cannot set the adapter for the checkbox as i set for the spinner. I need to get only one spinner with one edit text and multiple checkboxes(total number of database rows). please help!