-3

I have two classes pertaining to this issue: AddA and TopicSpinner.

In the AddA class, I am setting an on touch listener for a spinner. When the spinner is selected, I want to call the method loadSpinnerData() of the TopicSpinner class. So I want to call a non-static method of the TopicSpinner class in the AddA class.

I know that starting the TopicSpinner class from the AddA class works with an Intent call so the loadSpinnerData() method does work properly. I tried several approaches in the listener as you see below. However, I cannot get the method to be called without a null pointer or "non-static method cannot be referenced from static method".

Any suggestions of how to call the method loadSpinnerData() of the TopicSpinner class in the listener method of the AddA class?

Here is the current exception:

java.lang.NullPointerException
at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:109)
at TopicSpinner.loadSpinnerData(TopicSpinner.java:56)
at AddA$2.onTouch(AddAlerts.java:117)
at android.view.View.dispatchTouchEvent(View.java:7241)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2168)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1903)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1953)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1405)
at android.app.Activity.dispatchTouchEvent(Activity.java:2410)
at android.support.v7.internal.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:59)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1901)
at android.view.View.dispatchPointerEvent(View.java:7426)
at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3220)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3165)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4292)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4271)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4363)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:179)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:125)
at android.os.Looper.loop(Looper.java:124)
at android.app.ActivityThread.main(ActivityThread.java:5041)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
JohnWilliams
  • 139
  • 1
  • 13
  • Non-static method from a static context means that you need to use the method on an instance of an object. – Michael Queue Dec 23 '15 at 20:27
  • @MichaelQueue Please explain...I believe I tried that with TopicSpinner ts = new TopicSpinner(); ts.loadSpinnerData(); and received null... – JohnWilliams Dec 23 '15 at 20:30
  • Well that would be the correct way to call a method on an instance of an object. However, it looks like there is a problem with your `loadSpinnerData()` method. – Michael Queue Dec 23 '15 at 20:31
  • the 'onCreate()' Method looks like it is allready calling 'loadSpinnerData' and is intended to use by AddAlerts. Why not use that one? – Sebastian Weiß Dec 23 '15 at 20:34
  • @SebastianWeiß I am trying to call loadSpinnerData of the TopicSpinner class in the other class, AddA. – JohnWilliams Dec 23 '15 at 20:40
  • Okay, you're changing the code sample, which is fine, but can you please post the current code and stack-trace of the error that is being thrown? Because I think we're all have problems with the 'target' keep on moving. – mawalker Dec 23 '15 at 21:35
  • @mawalker Understandable. Updated. – JohnWilliams Dec 23 '15 at 21:39
  • does `getApplicationContext()` by itself return null? – mawalker Dec 23 '15 at 21:50
  • @mawalker I would not know because getApplicationContext() is called in the method that I am having troubles with. ts.loadSpinnerData(); and DatabaseHelper db = DatabaseHelper.getInstance(getApplicationContext()); return null. But, I call getApplicationContext() in other classes and not with the loadSpinnerData() method. – JohnWilliams Dec 23 '15 at 21:54
  • My point was to add `Context tempContext = getApplicationContext();` to your code RIGHT before it is used by `DatabaseHelper.getInstance(...)` and throws an exception... and then `Log.d("AddA", "tempContext = " + tempContext);` and see if it returns an address or `null`. – mawalker Dec 23 '15 at 21:56
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/98862/discussion-between-mawalker-and-johnwilliams). – mawalker Dec 23 '15 at 21:58
  • @mawalker Returns a value of tempContext = android.app.Application@428609c8. So yes, getApplicationContext() does return a value. – JohnWilliams Dec 23 '15 at 21:58
  • 2
    Can you please not make [duplicate questions asking similar things?](http://stackoverflow.com/questions/34404493/how-to-call-non-static-method-of-another-class) – OneCricketeer Dec 23 '15 at 22:38

7 Answers7

0

Instead of adding the listener in your AddA class, just add it in your TopicSpinner class. You can do it in your onCreate method. Then you can directly reference your loadSpinnerData method.

public class TopicSpinner extends AddAlerts implements OnItemSelectedListener {

    // Spinner element
    Spinner spinner;

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

        //Spinner element
        spinner = (Spinner) findViewById(R.id.spinner);

        //Spinner click listener
        spinner.setOnItemSelectedListener(this);

        //ADD LISTENER HERE
        spinner.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    loadSpinnerData();
                }
                return false;
            }
        });


        //Loading spinner data from database
        loadSpinnerData();
    }

    /**
     * Function to load the spinner data from SQLite database
     **/
    public void loadSpinnerData() {
        // database handler
        DatabaseHelper db = DatabaseHelper.getInstance(getApplicationContext());

        Cursor topicCursor = db.getAllTopics();
        Log.v("topicCursor", topicCursor.toString());
        db.close();

        String str;

        ArrayList<String> labels = new ArrayList<String>();
        if (topicCursor.moveToFirst()) {
            do {
                str = topicCursor.getString(topicCursor.getColumnIndex("topic_name"));
                Log.v("str", str);

                labels.add(str);
                Log.v("labels", labels.toString());

            } while (topicCursor.moveToNext());

        }
        ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, labels);

        //Drop down layout style - list view with radio button
        dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        Log.v("dataAdapter", dataAdapter.toString());

        //Attaching data adapter to spinner
        spinner.setAdapter(dataAdapter);
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

        // On selecting a spinner item
        String label = parent.getItemAtPosition(position).toString();

    }

    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
        // TODO Auto-generated method stub

    }

}

Josh Chappelle
  • 1,558
  • 2
  • 15
  • 37
  • I cannot have the listener in TopicSpinner only though because I start in AddA and when the spinner is clicked, I go to the method that gets the spinner data in the TopicSpinner class. The way you explained causes an issue where the spinner cannot be selected at all.. – JohnWilliams Dec 23 '15 at 20:39
  • Sorry for wrong Edit, meant to edit my 1st 'answer' not yours. – mawalker Dec 23 '15 at 22:32
0

By your question, I call tell the confusion between a "Class" and an "Object". By calling a static method, your are calling an action on no particular instance of a TopicSpinner. By calling loadSpinnerData() as it is (not static) you are saying to a precise TopicSpinner, "do the job of loadSpinnerData()". See here to learn more.

I can see in TopicSpinner that you have a field "spinner" that is onwed by "an instance" of TopicSpinner. For example, you could have 2 TopicSpinners having a different Spinner each. The method loadSpinnerData() will actually modify that Spinner. In other words, loadSpinnerData() needs to be called via an instance (object) not via a static call, because it needs to know what Spinner it's actually going to play with.

Now why do you get a Nullpointerexception if you call it correctly? If you call new TopicSpinner() manually, then it means, run the loadSpinnerData() on a new instance of the TopicSpinner that I just created. I have a feeling your topic spinner already exists. You might be able to access it by drilling down in the objects View and MotionEvent. Try to debug and look for the TopicSpinner instance. Then, you can call loadSpinnerData() on that instance.

Hope I didn't confuse you too mch, but you really need to differenciate a class and an instance of a class (object).

Community
  • 1
  • 1
pmartin8
  • 1,545
  • 1
  • 20
  • 36
  • I understand the difference already, and have no clue what is wrong. – JohnWilliams Dec 23 '15 at 20:55
  • As I said, if the TopicSpinner aleary exist, you need to find that instance to call loadSpinnerData() on it. Normally, the Event object will include the object targeted by the event. – pmartin8 Dec 23 '15 at 20:58
  • I thought I already tried that in the listener in the second commented out section. – JohnWilliams Dec 23 '15 at 21:01
0

Your issue is that "spinner" is not set (is null) in the following line in loadSpinnerData():

        spinner.setAdapter(dataAdapter);

When you launch the class with an Intent, it executes onCreate, which properly initializes the "spinner" variable. You could declare "spinner" as an instance variable in AddA, then send it as a parameter to the constructor for TopicSpinner, where you capture and save the reference. Something like this:

 public AddA extends Activity {
     private Spinner spinner;
     private Context context;
     //In onCreate or wherever
     spinner = (Spinner) findViewById(R.id.spinner);
     context = this;
     spinner.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                     TopicSpinner ts = new TopicSpinner(spinner, context);
                     ts.loadSpinnerData();
                }
                //etc

Now in TopicSpinner add a constructor:

public class TopicSpinner extends AddA implements OnItemSelectedListener {

    // Spinner element
    private Spinner spinner;
    private Context context;
    public TopicSpinner(Spinner spinner, Context context){
         this.spinner = spinner;
         this.context = context;
    }
    //etc

In your TopicSpinner class replace getContext() with context. The rest of your code remains untouched.

LIProf
  • 436
  • 2
  • 5
  • I am not launching the class with an Intent though. I am trying to call the method loadSpinnerData of the TopicSpinner class in the other class, AddA. – JohnWilliams Dec 23 '15 at 20:51
  • Right. If you did use Intents, spinner would be set. Since you don't want to use Intents, you'll have to "manually" set spinner in the instance of TopicSpinner. I will edit my answer to add more detail. – LIProf Dec 23 '15 at 21:01
  • But I thought that the constructor you suggested for TopicSpinner solved that issue. Please explain. – JohnWilliams Dec 23 '15 at 21:19
  • Sorry, didn't see your updated code. It appears that getContext() is failing, although I am not entirely sure about this. I'll edit my answer some more. – LIProf Dec 23 '15 at 21:26
0

Old Answer, (left because this 'was' true at the time, but the above code/info has changed)

To start with your first mistake is not storing the 'new' TopicSpinner. That is why you can't call 'member methods(might be wrong name, but 'non-static' methods)' of the class.

 final Spinner spinner = (Spinner) findViewById(R.id.spinner);
        spinner.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {

                   // Intent i = new Intent(AddA.this, TopicSpinner.class);
                    //startActivity(i);

                    //TopicSpinner ts = new TopicSpinner();
                    //ts.loadSpinnerData();   //null

                    // the problem is that you aren't saving the reference to the 
                    // newly created TopicSpinner Object. 
                    // Storing that reference here allows you to call
                    // non-static methods.
                    TopicSpinner myTopicSpinner =  new TopicSpinner();
                    // Perhaps this can be above if it follows the builder pattern... 
                    // but if not, then you need to separate these calls.
                    myTopicSpinner.loadSpinnerData(); // loadSpinnerData you have listed returns 'void' so it doesn't follow builder pattern.
                    // if myTopicSpinner is null here then you have bigger issues in THAT class. 


                    // Now you can call non-static methods on the OBJECT.
                    // Since you 'have' an object now.
                    myTopicSpinner.loadSpinnerData(); //non-static method 
                }
                return false;
            }
        });
mawalker
  • 2,072
  • 2
  • 22
  • 34
0

DatabaseHelper db = DatabaseHelper.getInstance(getApplicationContext()); return null.

If this is the case, then your DatabaseHelper class is where your issues actually is.

Here is a question with an (almost) complete answer that will show you what to do (you will need to impl. your own rawQuery(), that they don't do in that link) Looking for Cause of Null Pointer Exception


There are a couple things that you can try. (Hard to tell without seeing source for DatabaseHelper)

1) DatabaseHelper db = new DatabaseHelper(getApplicationContext()); then db.open() right after it. (if you follow the pattern that I show below). Use normal constructor instead of 'getInstance()'. I haven't seen DBHelper classes being singletons or something else that would require 'getInstance()' before. A SQLite DB should be able to handle multiple-concurrent readable DB accesses EASILY (writes have timing issues, but that is 'app logic' not the SQLite DB itself).

2) Start logging within your DatabaseHelper class and see where it fails internally.


Here is (part of) a sample DBHelper class I wrote a long time ago.

You shouldn't be getting a 'null' when you try to instantiate yours. You should make sure that class is operating properly. If you don't have one you should have an internal SQLiteOpenHelper class (very helpful).

// Database open/upgrade helper
private myDbHelper dbHelper; // this is inside my DBAdapter class

// ... 

/**
 * constructor that accepts the context to be associated with
 * 
 * @param _context
 */
public DataDBAdaptor(Context _context) {
    Log.d(LOG_TAG, "MyDBAdapter constructor");

    context = _context;
    dbHelper = new myDbHelper(context, DATABASE_NAME, null,
            DATABASE_VERSION);
}

/**
 * open the DB, and write/read access or
 * just read access if that is all that is possible.
 * 
 * @return this DataDBAdaptor
 * @throws SQLException
 */
public MoocDataDBAdaptor open() throws SQLException {
    Log.d(LOG_TAG, "open()");
    try {
        db = dbHelper.getWritableDatabase();
    } catch (SQLException ex) {
        db = dbHelper.getReadableDatabase();
    }
    return this;
}

Here is the Helper class

/**
 * DB Helper Class.
 * 
 * @author mawalker
 * 
 */
private static class myDbHelper extends SQLiteOpenHelper {

    public myDbHelper(Context context, String name, CursorFactory factory,
            int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d(LOG_TAG, "DATABASE_CREATE: version: " + DATABASE_VERSION);
        // ST:createTable:start
        db.execSQL(DATABASE_CREATE_STORY);
        db.execSQL(DATABASE_CREATE_TAGS);
        // ST:createTable:finish

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // Log version upgrade.
        Log.w(LOG_TAG + "DBHelper", "Upgrading from version " + oldVersion
                + " to " + newVersion + ", which will destroy all old data");

        // **** Upgrade DB ****
        // TODO: migrate data?? from old DB to new DB
        // drop old DB

        // ST:dropTableIfExists:start
        db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_STORY);
        db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_TAGS);
        // ST:dropTableIfExists:finish

        // Create a new one.
        onCreate(db);

    }

}
Community
  • 1
  • 1
mawalker
  • 2,072
  • 2
  • 22
  • 34
0

You could try passing in the spinner or context in as a parameter?

So copy and paste your load spinner data and sub out with the parameterized spinner and context:

 public Spinner loadSpinnerData(Context context, Spinner spinner) {
    // database handler
    DatabaseHelper db = DatabaseHelper.getInstance(context);

    Cursor topicCursor = db.getAllTopics();
    Log.v("topicCursor", topicCursor.toString());
    db.close();

    String str;

    ArrayList<String> labels = new ArrayList<String>();
    if (topicCursor.moveToFirst()) {
        do {
            str = topicCursor.getString(topicCursor.getColumnIndex("topic_name"));
            Log.v("str", str);

            labels.add(str);
            Log.v("labels", labels.toString());

        } while (topicCursor.moveToNext());

    }
    ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, labels);

    //Drop down layout style - list view with radio button
    dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    Log.v("dataAdapter", dataAdapter.toString());

    //Attaching data adapter to spinner
    spinner.setAdapter(dataAdapter);
    return spinner;
}

And then reassign the spinner you passed in to be the new one that comes out of this method. It will hold its previous references as well because we are not assigning it in this method

KoalaKoalified
  • 687
  • 4
  • 15
0

Calling your Activity classes by the names of other classes is seriously confusing people.

AddA is an Activity.

TopicSpinner is also an Activity since you extends AddA.

Calling new TopicSpinner() creates an Activity object, but does not inflate it, so calling setContentView and findViewById are pointless inside of TopicSpinner. It might even return null because the view cannot be found.


As is, you have two options and both get rid of the extends AddA.

Option 1: Just load the data in AddA
Option 2: Create a separate class that loads the data that is not an Activity. You are getting all confused with onCreate and setContentView and findViewById. All you need is the Context and Spinner.

public class AddA extends Activity implements AdapterView.OnItemSelectedListener {

    private Spinner spinner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_a);

        spinner = (Spinner) findViewById(R.id.spinner);

        spinner.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    /** Option 1 **/
                    loadSpinnerData();

                    /** Option 2 **/
                    TopicLoader loader = new TopicLoader(getApplicationContext());
                    loader.loadSpinner(spinner);
                }
                return false;
            }
        });

    }

    private void loadSpinnerData() {
        Context ctx = getApplicationContext();

        // TODO: Implement Option 1 here
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        // On selecting a spinner item
        String label = parent.getItemAtPosition(position).toString();
    }

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

    }
}

public class TopicLoader {
    private Context mCtx;

    public TopicLoader(Context ctx) {
        this.mCtx = ctx;
    }

    public void loadSpinner(Spinner spinner) {
        // Context ctx = this.mCtx;

        // TODO: Implement Option 2 here
    }
}
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245