I'm trying to insert data into the database but it tells me there's no such table. I also have another sqlite database inside the application, i'm not sure if that affects this one, but I don't think so. It uses the same database name, but a different table name. If you think it does affect it, tell me and I'll post up the code for that too.
The logcat gives me these messages:
(1) no such table: notes
Error inserting title=Bleh desc=bleh
android.database.sqlite.SQLiteException: no such table: notes (code 1): , while compiling: INSERT INTO notes(title,desc) VALUES (?,?)
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:889)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:500)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1467)
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1339)
at com.example.stepsaway.NoteActivity.addEntry(NoteActivity.java:102)
at com.example.stepsaway.NoteActivity.onClick(NoteActivity.java:75)
at android.view.View.performClick(View.java:4240)
at android.view.View$PerformClick.run(View.java:17721)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5103)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
The Activity java is:
public class NoteActivity extends Activity implements OnClickListener {
Button buttonLeaveNote;
private EditText mTitle;
private EditText mDesc;
protected NoteDBHelper noteDB = new NoteDBHelper(NoteActivity.this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note);
buttonLeaveNote = (Button) findViewById(R.id.buttonLeaveNote);
buttonLeaveNote.setOnClickListener(this);
mTitle = (EditText)findViewById(R.id.etitle);
mDesc = (EditText)findViewById(R.id.edesc);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.note, menu);
return true;
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.buttonLeaveNote:
String title = mTitle.getText().toString();
String desc = mDesc.getText().toString();
boolean invalid = false;
if(title.equals(""))
{
invalid = true;
Toast.makeText(getApplicationContext(), "Please enter a title", Toast.LENGTH_SHORT).show();
}
else
if(desc.equals(""))
{
invalid = true;
Toast.makeText(getApplicationContext(), "Please enter description", Toast.LENGTH_SHORT).show();
}
else
if(invalid == false)
{
addEntry(title, desc);
Intent i_note = new Intent(NoteActivity.this, JustWanderingActivity.class);
startActivity(i_note);
//finish();
}
break;
}
}
public void onDestroy()
{
super.onDestroy();
noteDB.close();
}
private void addEntry(String title, String desc)
{
SQLiteDatabase notedb = noteDB.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("title", title);
values.put("desc", desc);
//values.put("lati", lati);
//values.put("lng", lng);
try
{
long newRowId;
newRowId = notedb.insert(NoteDBHelper.DATABASE_TABLE_NAME, null, values);
Toast.makeText(getApplicationContext(), "Note successfully added", Toast.LENGTH_SHORT).show();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}
And the Database java is:
public class NoteDBHelper extends SQLiteOpenHelper
{
private SQLiteDatabase notedb;
public static final String NOTE_ID = "_nid";
public static final String NOTE_TITLE = "title";
public static final String NOTE_DESC = "desc";
public static final String NOTE_LAT = "lati";
public static final String NOTE_LONG = "lng";
NoteDBHelper noteDB = null;
private static final String DATABASE_NAME = "stepsaway.db";
private static final int DATABASE_VERSION = 2;
public static final String DATABASE_TABLE_NAME = "notes";
private static final String DATABASE_TABLE_CREATE =
"CREATE TABLE " + DATABASE_TABLE_NAME + "(" +
"_nid INTEGER PRIMARY KEY AUTOINCREMENT," +
"title TEXT NOT NULL, desc LONGTEXT NOT NULL);";
public NoteDBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
System.out.println("In constructor");
}
#Override
public void onCreate(SQLiteDatabase notedb) {
try{
notedb.execSQL(DATABASE_TABLE_CREATE);
}catch(Exception e){
e.printStackTrace();
}
}
#Override
public void onUpgrade(SQLiteDatabase notedb, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
public Cursor rawQuery(String string, String[] strings) {
// TODO Auto-generated method stub
return null;
}
public void open() {
getWritableDatabase();
}
public Cursor getDetails(String text) throws SQLException
{
Cursor mCursor =
notedb.query(true, DATABASE_TABLE_NAME,
new String[]{NOTE_ID, NOTE_TITLE, NOTE_DESC},
NOTE_TITLE + "=" + text,
null, null, null, null, null);
if (mCursor != null)
{
mCursor.moveToFirst();
}
return mCursor;
}
}
Any help would be appreciated, thank you.
Edit: Looks like the problem is creating a second table within the same database name. It won't let me create a second one. The first time creating the DB works fine, but it gives the SQLite Exception with no such table when trying to create another table. Is there some code I need to alter or add to create a second table? Because all i did was create another sqlite database java with the same DATABASE_NAME, but a different DATABASE_TABLE_NAME.
My best guess is when you create nodeDB object, the activity is still not available. Therefore you will failed creating the table.
You can try moving how you initialise the noteDB into inside onCreate():
protected NoteDBHelper noteDB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note);
noteDB = new NoteDBHelper(NoteActivity.this);
buttonLeaveNote = (Button) findViewById(R.id.buttonLeaveNote);
buttonLeaveNote.setOnClickListener(this);
mTitle = (EditText)findViewById(R.id.etitle);
mDesc = (EditText)findViewById(R.id.edesc);
}
Your onUpgrade() is empty and database schema version is 2. My guess is that your initial version of the database didn't have the notes table and when it was later added, the empty upgrade code couldn't add it. But SQLiteOpenHelper is satisfied as it is now running version 2 of the database.
To fix it once, clean your app data. E.g. in settings app, go to manage apps -> downloaded -> select your app and click on the "Clear application data" button. Or just uninstall and reinstall the app. This approach is good enough during development.
To fix it for released versions, implement onUpgrade() so that is updates the database schema and does any necessary data migration. If you're not concerned about data loss, you can just call DROP TABLE on the old tables and then call onCreate() to recreate the tables.
To your follow-up question regarding multiple tables: Just use the same helper class to manage the database. One helper per database file. Create all tables in onCreate() and do any required migrations in onUpgrade().
Regarding to First issue : as I explained in my comment >
If you have created the DB before and adding a table afterwards this can cause the problem. Tables are created for the first time DB created. So uninstall or clean the data in the app from device and run it again.
For the Second Issue :
If you want to Create a table, do the same that you are already doing for the table you have.
#Override
public void onCreate(SQLiteDatabase notedb) {
try{
notedb.execSQL(DATABASE_TABLE_CREATE);
notedb.execSQL(DATABASE_TABLE_CREATE_STRING_2);
}catch(Exception e){
e.printStackTrace();
}
}
Related
I have a crud app project in android studio. I have done insert, update, delete and view all. The only one I'm having error with is the search function. The search code looks good to me, so it would be great if you can help me find my mistake. Thank you!
The table is created this way:
public void onCreate(SQLiteDatabase DB) {
DB.execSQL("create Table ProdDetails(id INTEGER primary key, name TEXT, description TEXT, price NUMERIC, quantity INTEGER)");
}
The search method, accepts input from EditText which was converted to int:
public Cursor searchData (int id)
{
SQLiteDatabase DB = this.getWritableDatabase();
Cursor cursor = DB.rawQuery("Select * from ProdDetails where id = ?", new String[]{String.valueOf(id)});
return cursor;
}
The Main class:
public class RUDActivity extends AppCompatActivity {
EditText prodId, name, description, price, quantity;
Button update, delete, view, search;
DBHelper DB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_retrieve);
prodId = (EditText) findViewById(R.id.prodId);
name = (EditText) findViewById(R.id.name);
description = (EditText) findViewById(R.id.description);
price = (EditText) findViewById(R.id.price);
quantity = (EditText) findViewById(R.id.quantity);
search = findViewById(R.id.btnSearch);
DB = new DBHelper(this);
search.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int prodTXT = Integer.parseInt(prodId.getText().toString());
Cursor res = DB.searchData(prodTXT);
if(res.getCount()==0){
Toast.makeText(RUDActivity.this, "Product ID Not Found", Toast.LENGTH_SHORT).show();
return;
}
// app crash/restart after this line, am I doing this wrong?
prodId.setText("Product ID: "+res.getString(0));
name.setText("Name: "+res.getString(1));
description.setText("Description: "+res.getString(2));
price.setText("Price: "+res.getString(3));
quantity.setText("Quantity: "+res.getString(4));
}
});
}
}
When I search prodId that does not exist it would show toast message "Product ID Not Found" and the app won't crash/restart. But if I search a prodId that does exist the app will crash.
You are missing a call to the method moveToFirst() before you retrieve the values of the columns:
res.moveToFirst();
prodId.setText("Product ID: "+res.getString(0));
................................................
because the cursor's index is initially positioned before the 1st row of the results.
Or, instead of using getCount() to check if the cursor returned any rows use moveToFirst():
if (!res.moveToFirst()) {
Toast.makeText(RUDActivity.this, "Product ID Not Found", Toast.LENGTH_SHORT).show();
} else {
prodId.setText("Product ID: "+res.getString(0));
................................................
}
I have a switch which has a boolean value. I'm trying to parse the boolean to an int but it is not working. When I remove the boolean, all the values add to the database so I know that I'm misunderstanding something about how to implement this boolean value from my switch.
Here is the part of my code where I try to implement this
public class MainActivity extends AppCompatActivity {
public static DataBaseHelper dataBaseHelper;
Button btn_submit, btn_users;
EditText et_gender,et_age;
Switch sw_diagnosed;
dataBaseHelper.queryData("CREATE TABLE IF NOT EXISTS USER(Id INTEGER PRIMARY KEY AUTOINCREMENT, gender VARCHAR, age INT, diagnosed INT DEFAULT 0, image BLOB)");
int numDiagnosed = sw_diagnosed.isChecked() ? 1 : 0;
String bla = Integer.toString(numDiagnosed);
btn_submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
dataBaseHelper.insertData(
et_gender.getText().toString().trim(),
//et_gender.getText().toString(), Integer.parseInt(et_age.getText().toString()
Integer.parseInt(et_age.getText().toString()),
Integer.parseInt(bla),
imageViewToByte(ivFinger)
);
Toast.makeText(getApplicationContext(), "Added Successfully", Toast.LENGTH_SHORT).show();
}
catch (Exception e){
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Error submitting user", Toast.LENGTH_SHORT).show();
}
}
});
}
Here is the method in my SQL helper
public void insertData(String gender, int age, int isDiagnosed, byte[] image){
SQLiteDatabase database = getWritableDatabase();
String sql = "INSERT INTO USER VALUES (NULL, ?,?,?,?)";
SQLiteStatement statement = database.compileStatement(sql);
statement.clearBindings();
statement.bindString(1, gender);
statement.bindString(2, Integer.toString(age));
statement.bindString(3, Integer.toString(isDiagnosed));
statement.bindBlob(3, image);
statement.executeInsert();
}
IF you want to get value from switch when you are trying to save you should put that code in OnClickListener:
int numDiagnosed = sw_diagnosed.isChecked() ? 1 : 0;
and then try to save it. Furthermore, you are converting int to String and then String to Integer. Just use value which you got from above code like below:
dataBaseHelper.insertData(
et_gender.getText().toString().trim(),
//et_gender.getText().toString(), Integer.parseInt(et_age.getText().toString()
Integer.parseInt(et_age.getText().toString()),
numDiagnosed,
imageViewToByte(ivFinger)
);
In my App i have a ListView, if i add some new Items, the ListView does not shown the Items. The items will be shown if i close the app and go back in it.
So my new Items are saved in my Database, but the list will not be synchronized.
i followed a Tutorial on youtube, if he tries it, it works fine, but not in my app. i hope someone can help me to find my problem.
In my newItem_Activity i have an Add-Button with following Code:
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(todo.getName() == null){
Toast.makeText(ToDoCreateNew.this, "Please insert some value.", Toast.LENGTH_LONG).show();
return;
}
ToDoDatabaseHelper.getInstance(ToDoCreateNew.this).createTodo(todo);
finish();
}
});
My Database looks like this:
public ToDo createTodo(final ToDo todo) {
SQLiteDatabase database = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(NAME_COLUMN, todo.getName());
contentValues.put(DUEDATE_COLUMN, todo.getDueDate() == null ? null : todo.getDueDate().getTimeInMillis() / 1000);
contentValues.put(FAVORITE_COLUMN, todo.isFavorite() ? 1 : 0);
contentValues.put(DESCRIPTION_COLUMN, todo.getDescription());
contentValues.put(DUETIME_COLUMN, String.valueOf(todo.getDueTime() == null ? null : todo.getDueTime().getTime()));
long newID = database.insert(TABLE_NAME, null, contentValues);
database.close();
return readToDo(newID);
}
public List<ToDo> readAllToDos(){
List<ToDo> todos = new ArrayList<>();
SQLiteDatabase database = this.getReadableDatabase();
Cursor c = database.rawQuery("SELECT * FROM " + TABLE_NAME, null);
if (c.moveToFirst()){
do {
ToDo todo = readToDo(c.getLong(c.getColumnIndex(ID_COLUMN)));
if (todo != null){
todos.add(todo);
}
} while (c.moveToNext());
}
database.close();
return todos;
}
and this Code is on my Activity with my ListView:
List<ToDo> dataSource;
ToDoOverviewListAdapter adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_to_do_overview);
lv = (ListView) findViewById(R.id.listToDo);
dataSource = ToDoDatabaseHelper.getInstance(this).readAllToDos();
adapter = new ToDoOverviewListAdapter(this, dataSource);
lv.setAdapter(new ToDoOverviewListAdapter(this, dataSource));
}
// Go to Activity to Add new Item - Add Button is in newItem_Activity
public void createToDo(){
startActivity(new Intent(ToDoOverview.this, ToDoCreateNew.class));
refreshListView();
}
private void refreshListView(){
dataSource.clear();
dataSource.addAll(ToDoDatabaseHelper.getInstance(this).readAllToDos());
adapter.notifyDataSetChanged();
}
Do one thing when you add lists in adapter , wrap it in a method and call that method in both onCreate and OnStart Method . So basically you have to add list in adapter two times On OnCreate method and OnStart method. So When you update the data and move back to your original activity it will trigger OnStart method and it will show updated data.
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)
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
}
}
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).
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.
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
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;
}
});
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
}
}
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);
}
}
I have a ListFragment that shows the names of all the shopping lists stored in my database table.
The problem is that when I add a new shopping list row to the table, the ListFragment on the UI is not automatically updated. (The change can only be seen if I close and restart the app.)
Firstly, this is the code that is executed in my DbContentProvider class when I add a shopping list:
`
// Insert the values into the table
id = db.insert(SHOPPING_LISTS_META_TABLE, null, values);
if (id > -1) {
// Construct and return the URI of the newly inserted row.
Uri insertedId = ContentUris.withAppendedId(CONTENT_URI_SHOPPING_LISTS_META, id);
// Notify any observers of the change in the data set.
Log.d(LOG_TAG, "................. notifyChange(\"" + insertedId + "\", null)");
getContext().getContentResolver().notifyChange(insertedId, null);
Log.d(LOG_TAG, "................. notifyChange() done");
return insertedId;
}
else {
return null;
}
`
...and here is the LogCat output for it...
10-28 12:29:41.133: D/SQLiteOpenHelper(19401): ................. notifyChange("content://org.example.myapp.DbContentProvider/shopping_lists_meta/12", null)
10-28 12:29:41.143: D/SQLiteOpenHelper(19401): ................. notifyChange() done
10-28 12:29:41.153: D/HomeActivity(19401): Shopping list, "My Test Shopping List" created: content://org.example.myapp.DbContentProvider/shopping_lists_meta/12
10-28 12:29:41.183: D/AbsListView(19401): unregisterIRListener() is called
10-28 12:29:41.193: E/ViewRootImpl(19401): sendUserActionEvent() mView == null
10-28 12:29:41.503: D/AbsListView(19401): unregisterIRListener() is called
In LogCat, there is no output at all from my ListFragment class when I add the new shopping list row. Here is my ListFragment class...
`
public class ShoppingListNamesListFragment extends ListFragment implements LoaderManager.LoaderCallbacks {
private final static String LOG_TAG = ShoppingListNamesListFragment.class.getSimpleName();
// This is the Adapter being used to display the list's data
public static SimpleCursorAdapter mAdapter;
// These are the Contacts rows that we will retrieve
static final String[] PROJECTION = {DbContentProvider.KEY_ID,
DbContentProvider.KEY_SHOPPING_LIST_NAME,
DbContentProvider.KEY_IS_SHOPPING_LIST_SELECTED};
// This is the select criteria
static final String SELECTION = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(LOG_TAG, "................. onCreate()");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(LOG_TAG, "................. onActivityCreated()");
// For the cursor adapter, specify which columns go into which views
String[] fromColumns = {DbContentProvider.KEY_SHOPPING_LIST_NAME};
int[] toViews = {android.R.id.text1}; // The TextView in simple_list_item_1
// Create an empty adapter we will use to display the loaded data.
// We pass null for the cursor, then update it in onLoadFinished()
mAdapter = new SimpleCursorAdapter(this.getActivity(),
android.R.layout.simple_list_item_1, null,
fromColumns, toViews, 0);
setListAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(LOG_TAG, "................. onCreateView()");
return super.onCreateView(inflater, container, savedInstanceState);
}
// Called when a new Loader needs to be created
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.d(LOG_TAG, "................. onCreateLoader()");
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(getActivity(), DbContentProvider.CONTENT_URI_SHOPPING_LISTS_META,
PROJECTION, SELECTION, null, null);
}
// Called when a previously created loader has finished loading
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.d(LOG_TAG, "................. onLoaderFinished()");
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
// Called when a previously created loader is reset, making the data unavailable
#Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.d(LOG_TAG, "................. onLoaderReset()");
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
makeToast("shopping list clicked: " + position);
Log.d(LOG_TAG, "shopping list clicked: " + position);
}
private void makeToast(String msg) {
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
}
}
NB - In my DbContentProvider class, I have...
public static final Uri CONTENT_URI_SHOPPING_LISTS_META = Uri.parse("content://org.example.myapp.DbContentProvider/shopping_lists_meta");
public static final String SHOPPING_LISTS_META_TABLE = "shopping_lists_meta";
I have based my code on this Android ListFragment / LoaderManager example. And none of the similar questions to this that I have found offer a solution that fixes my problem.
I am fairly new to Android, so it could be a simple mistake I've made. But, essentially, it seems to me that when notifyChange() is called in my DbContentProvider class, my ListFragment is not being notified (or there is some other error in this area). Can anyone help?
After spending hours and hours on this, I created a TestListActivity that hooked up to the native Contacts content provider - and that all worked/updated as it should, so I knew the issue was probably in my own content provider that I'd written.
I found the answer here. Turns out I had not called setNotificationUri(ContentResolver cr, Uri uri) on the cursor returned by the query() method of my content provider. (I'm sure this was never mentioned in the Reto Mauer book I was working from...) :/
Anyway, all sorted now! :)