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) {} }); }}
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