Populating Spinner From SQLite Database Android Populating Spinner From SQLite Database Android database database

Populating Spinner From SQLite Database Android


This is an old question but the first one I found when figuring out this issue. Here is a detailed explanation with full source which may cut some legwork.

The answer is indeed to use a SimpleCursorAdapter which handles a list of strings but also has special handling for a matched ID field that gets returned when a row is selected. The key to making this work is to know the two following obscure bits of information:

1) When creating the cursor make sure the query returns a field titled "_id". This field need not be displayed anywhere but it's value will be passed back when a list item is selected.

2) When creating a SimpleCursorAdapter you need to supply the TextView layout IDs where the row text will be placed. If using the android supplied layout android.R.layout.simple_spinner_item the text id you need to use is android.R.id.text1.

Main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    >    <Spinner        android:id="@+id/spinner1"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_alignParentTop="true"        ></Spinner></RelativeLayout>

Activity code:

public class TesterActivity extends Activity {public Context mContext;@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    // just for this example:    // create database table with an id field and a text field and add some data    class MyDBHelper extends SQLiteOpenHelper {        public MyDBHelper(Context context) {            super(context, "someDB", null, 2);        }        @Override        public void onCreate(SQLiteDatabase db) {            db.execSQL("CREATE TABLE someTable (someIDF INTEGER, someTextF TEXT)");        }        @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            db.execSQL("DROP TABLE IF EXISTS someTable");            onCreate(db);            db.execSQL("INSERT INTO someTable (someIDF, someTextF) VALUES (54, 'Some text')");            db.execSQL("INSERT INTO someTable (someIDF, someTextF) VALUES (99, 'Some more text')");            db.execSQL("INSERT INTO someTable (someIDF, someTextF) VALUES (173, 'Even more text')");        }    }    SQLiteDatabase db = new MyDBHelper(this).getWritableDatabase();    // get a cursor from the database with an "_id" field    Cursor c = db.rawQuery("SELECT someIDF AS _id, someTextF FROM someTable", null);    // make an adapter from the cursor    String[] from = new String[] {"someTextF"};    int[] to = new int[] {android.R.id.text1};    SimpleCursorAdapter sca = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_item, c, from, to);    // set layout for activated adapter    sca.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);     // get xml file spinner and set adapter     Spinner spin = (Spinner) this.findViewById(R.id.spinner1);    spin.setAdapter(sca);    // set spinner listener to display the selected item id    mContext = this;    spin.setOnItemSelectedListener(new OnItemSelectedListener() {        public void onItemSelected(AdapterView<?> parent, View view, int position, long id){            Toast.makeText(mContext, "Selected ID=" + id, Toast.LENGTH_LONG).show();        }        public void onNothingSelected(AdapterView<?> parent) {}        });    }}


Try using a SimpleCursorAdapter instead of copying all the data by hand into an ArrayAdapter.


Here's another answer with loaders and cursors.

In the activity/fragment creation (said fragment/activity must implement LoaderManager.LoaderCallbacks<Cursor>):

final Spinner spinner = (Spinner) findViewById(R.id.spinner);mAdapter = new MyCursorAdapter(getActivity());spinner.setAdapter(mAdapter);getLoaderManager().initLoader(SOME_INT_CONSTANT, null, this);

In your activity/fragment:

@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {    return new MyCursorLoader(getActivity(), args);}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {    mAdapter.swapCursor(data);}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {    mAdapter.swapCursor(null);}

Here's the cursor adapter:

class MyCursorAdapter extends CursorAdapter {    class ViewsHolder {        TextView text1, text2;    }    public MyCursorAdapter(Context context, Bundle args) {        super(context, null, false);        // do something with args    }    @Override    public View newView(Context context, Cursor cursor, ViewGroup parent) {        View v = LayoutInflater.from(context).inflate(R.layout.your_item_layout, parent, false);        ViewsHolder holder = new ViewsHolder();        holder.text1 = (TextView) v.findViewById(R.id.text1);        holder.text2 = (TextView) v.findViewById(R.id.text2);        v.setTag(holder);        return v;    }    @Override    public void bindView(View view, Context context, Cursor cursor) {        ViewsHolder holder = (ViewsHolder) view.getTag();        String text1 = cursor.getString(cursor.getColumnIndex(KEY_TEXT1));        String text2 = cursor.getString(cursor.getColumnIndex(KEY_TEXT2));        holder.text1.setText(text1);        holder.text2.setText(text2);    }}

Here's the cursor loader:

public class MyCursorLoader extends CursorLoader {    private final YourSQLiteDbAdapter mHelper;    public MyCursorLoader(Context context) {        super(context);        mHelper = new YourSQLiteDbAdapter(context);        mHelper.openReadOnly();    }    @Override    public Cursor loadInBackground() {        return mHelper.selectYourDataAsACursor();    }    @Override    protected void onStopLoading() {        super.onStopLoading();        mHelper.close();    }}

Using this you get:

  • no use of deprecated APIs
  • use of the loader API
  • customized adapter / layouts
  • view recycling
  • API level 4 backward compatible (through support lib)
  • background thread data loading