0
votes

I cannot put the selected item of my spinner into my sqlite database. When I click Button my app auto close.

// Get sample data from the database and display them in the spinner
    spinner1=(Spinner)findViewById(R.id.spinner);
    ArrayList <String> list=datahelper.getAllProvinces();
    ArrayAdapter <String> adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list);
    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 );

        }
    } );


}

private void Ok_button_onClick(View v) {
    spinner db = new spinner ( getApplicationContext () );
    String firstspin= (String) spinner1.getItemAtPosition ( spinner1.getSelectedItemPosition () );
    db.insertLabel ( firstspin );
    Toast.makeText ( Main2Activity.this,"Selected Item" + firstspin, Toast.LENGTH_SHORT).show ();

error what i get..

D/AndroidRuntime: Shutting down VM E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.newlogin, PID: 14079 java.lang.NullPointerException: Attempt to invoke virtual method 'int android.widget.Spinner.getSelectedItemPosition()' on a null object reference at com.example.newlogin.loginSQLite.Main2Activity.Ok_button_onClick(Main2Activity.java:64) at com.example.newlogin.loginSQLite.Main2Activity.access$000(Main2Activity.java:24) at com.example.newlogin.loginSQLite.Main2Activity$1.onClick(Main2Activity.java:49) at android.view.View.performClick(View.java:5610) at android.view.View$PerformClick.run(View.java:22265) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6077) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

2
you can get selected item of spinner using this spinner1.getSelectedItem().toString()Dhaiyur

2 Answers

0
votes

Your issue is due to the scope of the list and the spinner1 variables. That is the scope doesn't allow them to be reached and thus they are null.

Both variables are declared and instantiated in the onCreate method of the activity and thus only available with that activity.

Although you set the onClickListener of the button in the onCreate method, when the button is clicked the onCreate method will have finished before you get a chance to click the button and the variables will then not be available.

What you need to do is declare the variables that you need access to at a higher (Class level) scope. You can then access them within the class.

That is, instead of using :-

protected void onCreate(Bundle savedInstanceState) {
    ..........
    spinner1=(Spinner)findViewById(R.id.spinner);
    ArrayList <String> list=datahelper.getAllProvinces();
    ArrayAdapter <String> adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list); // included as you might well want to use this
    ..........
}
  • .......... is used to indicate other lines of code not shown for brevity.
  • Note see comment re adapter variable.

You could have :-

public class Main2Activity extends AppCompatActivity {

    .......... // may be lines here

    Spinner spinner1;
    Button ok_button; //<<<<<<<< included as you may want to access this as well
    ArrayAdapter<String> adapter; //<<<<<<<<<< declares the adapter at class level
    ArrayList<String> list; //<<<<<<<<<< declares at the class level

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        ..........

        spinner1=(Spinner)findViewById(R.id.spinner);
        list=datahelper.getAllProvinces(); 
        adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list);

        ..........
}

Now you could use :-

private void Ok_button_onClick(View v) {
    //spinner db = new spinner ( getApplicationContext () ); //<<<<<<<<<< A new spinner? Why? Probably not hence commented out, you you mean new Database Helper (see demo below)
    String firstspin= (String) spinner1.getItemAtPosition ( spinner1.getSelectedItemPosition () );
    //db.insertLabel ( firstspin ); //<<<<<<<<<<commented out re above
    Toast.makeText ( Main2Activity.this,"Selected Item" + firstspin, Toast.LENGTH_SHORT).show (); //could this on it's own (see demo)
  • Important see the commented out lines.

Demonstration/ Working Example

The following is a working example, based upon the code made available in the question.

However, instead of inserting a row upon click (which as per the comments would likely fail), it gets the rowid (a number unique to the row and thus a means of identifying a specific row) according to the selected name. Note to do this the underlying table has the column defined using UNIQUE so no two provinces can be the same.

  • A better practice is to not use ArrayList but to use ArrayList (or a CursorAdapter such as SimpleCursorAdapter that works with a Cursor (hece why in the demo the rowid alias uses BaseColumns._ID)).
  • a_suitable_object could be a Province object (new class) that includes the province and id as variables.

The Activity (MainActivity.java)

  • Note the loadDataForDemo, is just to load some data for the demonstration.

:-

public class MainActivity extends AppCompatActivity {

    DatabaseHelper datahelper;
    Spinner spinner1;
    Button ok_button;
    ArrayAdapter<String> adapter; //<<<<<<<<<< declares the adapter at class level
    ArrayList<String> list; //<<<<<<<<<< declares at the class level

    @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);
        list=datahelper.getAllProvinces(); //<<<<<<<<<< CHANGED to use already declared
        adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list); // <<<<<<<<< CHANGED to use already declared
        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);
            }
        } );
    }

    private void Ok_button_onClick(View v) {
         // <<<<<<<<< shows 2 ways of getting the value from the spinner >>>>>>>>>
         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);
        }
    }
}

The Database helper class (DatabaseHelper.java)

public class DatabaseHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "mydb";
    public static final int DBVERSION = 1;

    public static final String TABLENAME_PROVINCE = "province";
    public static final String COL_PROVINCE_ID = BaseColumns._ID; // use stock id column name
    public static final String COL_PROVINCE_NAME = "province";

    SQLiteDatabase mDB;

    public DatabaseHelper(Context context) {
        super(context, DBNAME, null, DBVERSION);
        mDB = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLENAME_PROVINCE + "(" +
                COL_PROVINCE_ID + " INTEGER PRIMARY KEY," +
                COL_PROVINCE_NAME + " TEXT UNIQUE" +
                ")"
        );
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public long addProvince(String provinceName) {
        ContentValues cv = new ContentValues();
        cv.put(COL_PROVINCE_NAME,provinceName);
        return mDB.insert(TABLENAME_PROVINCE,null,cv);
    }

    public ArrayList<String> getAllProvinces() {
        ArrayList<String> rv = new ArrayList<>();
        Cursor csr = mDB.query(TABLENAME_PROVINCE,null,null,null,null,null,COL_PROVINCE_NAME);
        while (csr.moveToNext()) {
            rv.add(csr.getString(csr.getColumnIndex(COL_PROVINCE_NAME)));
        }
        csr.close(); //<<<<<<<<<< should always close cursors when done with them
        return rv;
    }

    public long getProvinceIdFromName(String name) {
        long rv = -1; //default value to indicate not found (just in case)
        String whereclause = COL_PROVINCE_NAME + "=?";
        String[] wherearguments = new String[]{name};
        Cursor csr = mDB.query(TABLENAME_PROVINCE,null,whereclause,wherearguments,null,null,null);
        if (csr.moveToFirst()) {
            rv = csr.getLong(csr.getColumnIndex(COL_PROVINCE_ID));
        }
        csr.close(); //<<<<<<<<<< should always close cursors when done with them
        return rv;
    }
}
  • This may well differ quite substantially from yours

The Spinner layout (very basic)

:-

<?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="match_parent">
    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

The Activity's layout

<?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>
</LinearLayout>

Result

The App looks like :-

enter image description here

With the Spinner selected (the highlighted item being the item that is selected) :-

enter image description here

The resultant output in the log is then :-

2019-05-15 11:07:39.300 D/SELECTED: Selected item is Noweheretobefoundington according to position.
2019-05-15 11:07:39.300 D/SELECTED: Selected item is Noweheretobefoundington as per the selected item method
2019-05-15 11:07:39.302 D/SELECTED: ID (via position) is 7 ID (via selected item) is 7
0
votes

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
    }
}