Additional
Following on from the answer using an ArrayList is not really the best approach.
For example you select ProvinceX from the list and then insert this value into another table, then say that ProvinceX changes it's name to ProvinceY then you have to update ProvinceX to ProvinceY wherever it's been used in other tables. Not that hard, but inefficient and it's not normalised (aka the data is duplicated).
With SQLite, with the exception of special tables defined using the WITHOUT ROWID, all tables have a special, normally hidden column, called rowid. This containing an integer (up to 64 bit signed so potentially 9223372036854775807 rows (twice as many if you use negative values)).
The rowid column (generally aliased (see later)), has some advantages. It can be considered as the MASTER index (better than the PRIMARY index). Searches for a rowid or alias thereof are faster.
The data for rowid tables is stored as a B-Tree structure containing
one entry for each table row, using the rowid value as the key. This
means that retrieving or sorting records by rowid is fast. Searching
for a record with a specific rowid, or for all records with rowids
within a specified range is around twice as fast as a similar search
made by specifying any other PRIMARY KEY or indexed value.
The storage used to store the rowid will be less than a for a String, thus more rows can be buffered when retrieving data.
Typically a table will have an alias of the rowid specifically using INTEGER PRIMARY KEY (INT PRIMARY KEY for example doesn't create an alias of the rowid). If you check the answer you can see that the _id column (i.e. BaseColumns._ID) used INTEGER PRIMARY KEY.
As such using ArrayList can be inefficient as the only way to subsequently access data from the database requires a less efficient than optimal search for that string.
ArrayList
If you were to create a Province object, which includes a member variable (field) for the rowid (typically an alias thereof), and use ArrayList as the source for the spinner then using that value can have efficiency benefits (less data to store, faster access to the data, more buffered data).
If that value is what is stored in the related table then changing ProvinceX to ProvinceY in the Province table will result in the value being changing throughout. So just the 1 update.
As such the working Example below includes a Spinner and Button that utilises an ArrayList (the Province class is also included).
CursorAdapater (SimpleCursorAdapter)
As you would have experienced, with the Android SQLite SDK/API, you get data into a Cursor and as the title of this section suggests, there are adapters for Cursors for ListViews and Spinners. Using these, in my opinion, is even easier and they have a few advantages. They have methods that return the _id (should be an alias of the rowid column) IF, THIS IS IMPORTANT FOR CURSOR ADAPTERS, the column is specifically named _id (hence the constant BaseColumns._ID). A Cursor adapter will crash if there is no such column. Results are unpredictable if the columns is not an alias of the rowid column.
- Infact ArrayAdapters also have handling of id's e.g. there is a getSelectedItemId(). However, this will return the position not as an integer but as a long the onItemClickListeners also include the value as the 4th parameter (again as there is no Cursor the value is the position of the selected item).
As such you may wish to consider the following example/demonstration that has 3 Spinners and Buttons (the original ArrayList Adapter, an ArrayList Aapter and a CursorAdapter).
All three output results to the log.
The Example Demo Code
The Province Class Province.java
public class Province {
private long provinceId;
private String provinceName;
public Province(long id, String name) {
this.provinceId = id;
this.provinceName = name;
}
public Province(String name) {
this(-1,name);
}
public Province() {
}
public long getProvinceId() {
return provinceId;
}
public void setProvinceId(long provinceId) {
this.provinceId = provinceId;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
@Override
public String toString() {
return this.provinceName; // NOTE overridden to avoid using Custom adapter
}
}
- nothing beyond basic Java in the above
The activity's layout activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Spinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Button
android:id="@+id/ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK">
</Button>
<Spinner
android:id="@+id/spinner2"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Button
android:id="@+id/ok2_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK2 (Province ArrayList)">
</Button>
<Spinner
android:id="@+id/spinner3"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Button
android:id="@+id/ok3_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK3 (Cursor)">
</Button>
</LinearLayout>
- Two extra Spinners and Buttons
Spinner Layout (unchanged so not shown)Database Helper
The database helper from the original answer has two additional method added as per
//<<<<<<<<<< ADDITIONAL FOR ArrayList<Province> adapter
public ArrayList<Province> getAllProvicesAsProvinceObjects() {
ArrayList<Province> rv = new ArrayList<>();
Cursor csr = mDB.query(TABLENAME_PROVINCE,null,null,null,null,null,COL_PROVINCE_NAME);
while (csr.moveToNext()) {
rv.add(
new Province(
csr.getLong(csr.getColumnIndex(COL_PROVINCE_ID)),
csr.getString(csr.getColumnIndex(COL_PROVINCE_NAME))
)
);
}
csr.close(); //<<<<<<<<<< should always close cursors when done with them
return rv;
}
//<<<<<<<<<< ADDITIONAL FOR Cursor adapter
public Cursor getAllProvincesAsCursor() {
return mDB.query(TABLENAME_PROVINCE,null,null,null,null,null,null);
}
- The first getAllProvicesAsProvinceObjects() returns an ArrayList
- The Second return a Cursor (so no need to build an intermediate ArrayList) +1 for a Cursor Adapter it's easier to code.
The activity MainActivity.java
This has three of most parts of the code.
Of note the manage?????? methods, retrieve the latest data, build the Spinner (once), and manage (by being called as per in the onResume method at the end) updating the list (tell the adapter that the data has changed).
public class MainActivity extends AppCompatActivity {
DatabaseHelper datahelper;
Spinner spinner1,
spinner2, //<<<<<<<<<<ADDITONAL For ArrayList<Province> adapter
spinner3 //<<<<<<<<<<ADDITIONAL for Cursor adapter
;
Button ok_button,
ok2_button, //<<<<<<<<<<ADDITIONAL For ArrayList<Province> adapter
ok3_button //<<<<<<<<<<ADDITIONAL for Cursor adapter
;
ArrayAdapter<String> adapter; //<<<<<<<<<< declares the adapter at class level
ArrayList<String> list; //<<<<<<<<<< declares at the class level
ArrayAdapter<Province> adapter2; //<<<<<<<<<<ADDITIONAL For ArrayList<Province> adapter
ArrayList<Province> list2; //<<<<<<<<<< ADDITIONAL For ArrayList<Province> adapter
SimpleCursorAdapter adapter3; //<<<<<<<<<<ADDITIONAL for Cursor adapter
Cursor mCsr; //<<<<<<<<<<ADDITIONAL for Cursor adapter
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
datahelper = new DatabaseHelper(this);
loadDataForDemo(); //<<<<<<<<<< load some data for the demo
spinner1=(Spinner)findViewById(R.id.spinner);
spinner2 =(Spinner) findViewById(R.id.spinner2); //<<<<<<<<<< For ArrayList<Province> adapter
spinner3 = (Spinner) findViewById(R.id.spinner3); //<<<<<<<<<<ADDITIONAL for Cursor adapter
list=datahelper.getAllProvinces(); //<<<<<<<<<< CHANGED to use already decalred
adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list); // <<<<<<<<< CHANGED to use already decalred
spinner1.setAdapter(adapter);
ok_button=findViewById ( R.id.ok_button );
ok_button.setOnClickListener ( new View.OnClickListener () {
@Override
public void onClick(View v) {
Ok_button_onClick (v);
}
} );
manageSpinner2(); //<<<<<<<<<< For ArrayList<Province> adapter
ok2_button = findViewById(R.id.ok2_button);
ok2_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Ok_button2_onClick(v);
}
});
manageSpinner3(); //<<<<<<<<<<ADDITIONAL for Cursor adapter
ok3_button = findViewById(R.id.ok3_button);
ok3_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Ok_button3_onClick(v);
}
});
}
//<<<<<<<<<< For ArrayList<Province> adapter >>>>>>>>>>
private void manageSpinner2() {
list2 = datahelper.getAllProvicesAsProvinceObjects();
if (adapter2 == null) {
adapter2 = new ArrayAdapter<>(this,R.layout.spinner_layout,R.id.text,list2);
spinner2.setAdapter(adapter2);
} else {
// Note this applies any changed data (updates, insert, deletes)
adapter2.clear();
adapter2.addAll(list2);
adapter2.notifyDataSetChanged();
}
}
//<<<<<<<<<<ADDITIONAL for Cursor adapter >>>>>>>>>>
private void manageSpinner3() {
mCsr = datahelper.getAllProvincesAsCursor();
if (adapter3 == null) {
adapter3 = new SimpleCursorAdapter(
this,
R.layout.spinner_layout,mCsr,
// Note following arrays should have matching number of elements
new String[]{DatabaseHelper.COL_PROVINCE_NAME}, // columns to get data from
new int[]{R.id.text}, // view id's (TextViews) into which data goes
// Note SimpleCursorAdapter quite flexible as layout can have multiple column/view matchings
0
);
spinner3.setAdapter(adapter3);
} else {
adapter3.swapCursor(mCsr);
}
}
private void Ok_button_onClick(View v) {
String value_via_position = list.get(spinner1.getSelectedItemPosition());
String value_from_selected_item = (String) spinner1.getSelectedItem();
Log.d("SELECTED","Selected item is " + value_via_position + " according to position.");
Log.d("SELECTED","Selected item is " + value_from_selected_item + " as per the selected item method");
long id_of_province1 = datahelper.getProvinceIdFromName(value_via_position);
long id_of_province2 = datahelper.getProvinceIdFromName(value_from_selected_item);
Log.d ("SELECTED","ID (via position) is " + String.valueOf(id_of_province1) + " ID (via selected item) is " + String.valueOf(id_of_province2));
}
private void loadDataForDemo() {
// Only load provinces if none exist
if (DatabaseUtils.queryNumEntries(datahelper.getWritableDatabase(),DatabaseHelper.TABLENAME_PROVINCE) > 0) return;
String[] provinces_to_load = new String[]{
"Hereington",
"Nothereington",
"Nowherington",
"Somewhereington",
"Overthereington",
"Inthehaystackington",
"Noweheretobefoundington",
"Atsomeplaceington",
"Zeroington",
"Beforetheotherplacington"
};
for (String province: provinces_to_load) {
datahelper.addProvince(province);
}
}
//<<<<<<<<<< For OK2 button
private void Ok_button2_onClick(View v) {
Province thisProvince = (Province) spinner2.getSelectedItem();
long id = ((Province) spinner2.getSelectedItem()).getProvinceId();
//spinner2.getSelectedItemId()
String name = ((Province) spinner2.getSelectedItem()).getProvinceName();
Log.d("SELECTED2",
"You selected Province:-" +
" ID=" + String.valueOf(thisProvince.getProvinceId()) +
" Name=" + thisProvince.getProvinceName());
}
private void Ok_button3_onClick(View v) {
long id = spinner3.getSelectedItemId(); // Cursor Adapter knows the id
String name = mCsr.getString(mCsr.getColumnIndex(DatabaseHelper.COL_PROVINCE_NAME));
Log.d("SELECTED3","You clicked ID=" + String.valueOf(id) + " Name="+name);
}
@Override
protected void onResume() {
super.onResume();
manageSpinner2(); //<<<<<<<<<< will apply changed data when returning to the activity
manageSpinner3(); //<<<<<<<<<<< likewise for Cursor adapter
}
}
spinner1.getSelectedItem().toString()
– Dhaiyur