I have a ListView in AcitivityA that is populated using a custom SimpleCursorAdapter called RecipeAdapter. The adapter holds data from SQLite
There is a EditText view at the top of the ListView, that filters the listview as the user searches for a recipe. When a user clicks on a item in the filtered ListView, ActivityB starts.
This all works perfectly. However when the user presses the backbutton to resume ActivityB, I get the following error.
java.lang.RuntimeException: Unable to resume activity {ttj.android.quorn/ttj.android.quorn.RecipeActivity}:
java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#418ae5d8
To fix this problem, I modified the onResume() from:
...
c = db.getCursor();
adapter.changeCursor(c);
to
....
Cursor cursor = db.getCursor();
adapter.changeCursor(cursor);
I then get the following exception. In the Logcat, the problem arises with the getId() method in DBHelper. I have added c.moveToFirst() in this method, but this still doesn't solve the problem.
FATAL EXCEPTION: main
android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 70
at android.database.AbstractCursor.checkPosition(AbstractCursor.java:400)
at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
at ttj.android.quorn.DBHelper.getId(DBHelper.java:224)
at ttj.android.quorn.RecipeActivity$RecipeHolder.populateFrom(RecipeActivity.java:650)
at ttj.android.quorn.RecipeActivity$RecipeAdapter.bindView(RecipeActivity.java:572)
at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:256)
at android.widget.AbsListView.obtainView(AbsListView.java:2214)
at android.widget.ListView.makeAndAddView(ListView.java:1774)
at android.widget.ListView.fillDown(ListView.java:672)
at android.widget.ListView.fillFromTop(ListView.java:732)
at android.widget.ListView.layoutChildren(ListView.java:1611)
at android.widget.AbsListView.onLayout(AbsListView.java:2044)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1628)
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2585)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
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:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)
Can anyone help me with my problem?
Here is my code:
In the onCreate, the cursor populate the ListView using c.getCursor and when the user filters the ListView via the EditText, the c.getFilterCursor is used.
public class RecipeActivity extends SherlockListActivity {
private DBHelper db = null;
private Cursor c = null;
private RecipeAdapter adapter = null;
ListView listContent;
private EditText filterText = null;
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
setContentView(R.layout.filter_list);
filterText = (EditText) findViewById(R.id.search_box);
filterText.addTextChangedListener(filterTextWatcher);
ListView listContent = getListView();
db = new DBHelper(this);
db.createDataBase();
db.openDataBase();
c = db.getCursor();
adapter = new RecipeAdapter(c);
listContent.setAdapter(adapter);
adapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
// Search for states whose names begin with the specified letters.
c = db.getFilterCursor(constraint);
return c;
}
});
startManagingCursor(c);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
filterText.removeTextChangedListener(filterTextWatcher);
db.close();
}
#SuppressWarnings("deprecation")
#Override
protected void onResume() {
super.onResume();
Cursor cursor = db.getCursor();
adapter.changeCursor(cursor);
}
#Override
protected void onPause() {
super.onPause();
adapter.notifyDataSetInvalidated();
adapter.changeCursor(null);
}
private TextWatcher filterTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
adapter.getFilter().filter(s);
}
};
RecipeAdapter inner class
class RecipeAdapter extends CursorAdapter {
#SuppressWarnings("deprecation")
public RecipeAdapter(Cursor c) {
super(RecipeActivity.this, c);
}
public void bindView(View row, Context arg1, Cursor arg2) {
RecipeHolder holder = (RecipeHolder) row.getTag();
holder.populateFrom(c, db);
}
public View newView(Context arg0, Cursor arg1, ViewGroup arg2) {
LayoutInflater inflater = getLayoutInflater();
View row = inflater.inflate(R.layout.reciperow, arg2, false);
RecipeHolder holder = new RecipeHolder(row);
row.setTag(holder);
return (row);
}
static class RecipeHolder {
public TextView id = null;
private TextView name = null;
private TextView desc = null;
private TextView preptime = null;
private TextView cooktime = null;
private TextView serves = null;
private TextView calories = null;
private TextView fat = null;
private TextView fav = null;
RecipeHolder(View row) {
id = (TextView) row.findViewById(R.id.id);
name = (TextView) row.findViewById(R.id.recipe);
desc = (TextView) row.findViewById(R.id.desc);
preptime = (TextView) row.findViewById(R.id.preptime);
cooktime = (TextView) row.findViewById(R.id.cooktime);
serves = (TextView) row.findViewById(R.id.serving);
calories = (TextView) row.findViewById(R.id.calories);
fat = (TextView) row.findViewById(R.id.fat);
fav = (TextView) row.findViewById(R.id.fav);
}
void populateFrom(Cursor c, DBHelper r) {
id.setText(r.getId(c));
name.setText(r.getRecipe(c));
name.setTextColor(Color.parseColor("#CCf27c22"));
desc.setText(r.getDesc(c));
preptime.setText(r.getPrepTime(c) + ". ");
cooktime.setText(r.getCookTime(c) + " mins");
serves.setText(r.getServes(c));
calories.setText(r.getCalories(c));
fat.setText(r.getFat(c));
fav.setText(r.getFav(c));
DBHelper class
public Cursor getCursor() {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(DATABASE_TABLE);
String[] columns = new String[] { KEY_ROWID, RECIPE, DESC, PREPTIME,
COOKTIME, SERVES, CALORIES, FAT, CATEGORY, FAV };
Cursor myCursor = queryBuilder.query(myDataBase, columns, null, null,
null, null, RECIPE + " ASC");
return myCursor;
}
public Cursor getFilterCursor(CharSequence constraint) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(DATABASE_TABLE);
String[] columns = new String[] { KEY_ROWID, RECIPE, DESC, PREPTIME,
COOKTIME, SERVES, CALORIES, FAT, CATEGORY, FAV };
if (constraint == null || constraint.length() == 0) {
// Return the full list
return queryBuilder.query(myDataBase, columns, null, null, null,
null, RECIPE + " ASC");
} else {
String value = "%" + constraint.toString() + "%";
return myDataBase.query(DATABASE_TABLE, columns, "RECIPE like ? ",
new String[] { value }, null, null, null);
}
}
public String getId(Cursor c) {
c.moveToFirst();
return (c.getString(0));
}
public String getRecipe(Cursor c) {
return (c.getString(1));
}
public String getDesc(Cursor c) {
return (c.getString(2));
}
public String getPrepTime(Cursor c) {
return (c.getString(3));
}
public String getCookTime(Cursor c) {
return (c.getString(4));
}
public String getServes(Cursor c) {
return (c.getString(5));
}
public String getCalories(Cursor c) {
return (c.getString(6));
}
public String getFat(Cursor c) {
return (c.getString(7));
}
public String getCategory(Cursor c) {
return (c.getString(8));
}
public String getFav(Cursor c) {
return (c.getString(9));
}
#SuppressWarnings("deprecation")
Bad. You should get rid of the deprecation instead of hiding that :)
startManagingCursor(c);
Don't do that. That may have caused the requery on the already closed cursor. Simply remove that line.
adapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
// Search for states whose names begin with the specified letters.
c = db.getFilterCursor(constraint);
return c;
}
});
Don't overwrite your c here. Just return db.getFilterCursor(constraint); is what this should do.
Other things that may have a positive effect
#SuppressWarnings("deprecation")
public RecipeAdapter(Cursor c) {
super(RecipeActivity.this, c);
}
public RecipeAdapter(Cursor c) {
// no requeries and no observer required if you change the cursor yourself
super(RecipeActivity.this, c, 0)
}
Next one:
adapter.notifyDataSetInvalidated();
adapter.changeCursor(null);
// change to
adapter.changeCursor(null);
adapter.notifyDataSetChanged(); // maybe without this
As far as I understand the documentation notifyDataSetInvalidated() means that the data can't be valid afterwards ("Once invoked this adapter is no longer valid and should not report further data set changes.") and you need to create a new Adapter instance. Not sure though. Just doing notifyDataSetChanged() works fine. It might even be the case that doing adapter.changeCursor() will already implicitly do the change notification.
P.S.: c.MoveToFirst() is not required. The CursorAdapter will move the cursor to the required position.
You renamed your variable, as indicated here
....
Cursor cursor = db.getCursor();
adapter.changeCursor(cursor);
correct? But right after that you specify that you tried
c.moveToFirst()
So maybe you should set
c = cursor;
So that the rest of your code works?
Related
I'm making a project in Android/Java. I have an activity with a listview that displays values from a database and a remove button. For the moment, each row of this list view consists of a checktextview. This last one displays the name of an element of the database. I want to select different elements (with the check text view) and then if I press the remove button, all the selected elements have to be removed from the list and from database. In the following I put the essential parts of my classes. I already done most of the work.
This is my activity:
public class ShoppingListActivity extends AppCompatActivity {
// Variables for the management of the database
private DBManagerShoppingList dbShoppingList;
private Cursor crs;
// List view for the shopping list elements
private ListView shoppingListListView;
private ShoppingListViewAdapter shoppingListViewAdapter;
// Generic variables
private String selectedShoppingList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shopping_list);
// Find of the useful widgets
shoppingListListView = findViewById(R.id.listViewSelShoppingList);
shoppingListsSpinner = findViewById(R.id.spinnerShoppingLists);
selectedShoppingList = "List_1";
populateListView();
}
// Populate list view from database
public void populateListView() {
// Database management
dbShoppingList = new DBManagerShoppingList(this, selectedShoppingList);
crs = dbShoppingList.query();
new Handler().post(new Runnable() {
#Override
public void run() {
shoppingListViewAdapter = new ShoppingListViewAdapter(ShoppingListActivity.this, crs, 0);
shoppingListListView.setAdapter(shoppingListViewAdapter);
}
});
}
// Remove selected elements
public void removeSelectedElements(View btnRemove) {
// TODO
}
}
This is my custom adapter:
public class ShoppingListViewAdapter extends CursorAdapter {
private LayoutInflater layoutInflater;
private List<Integer> elementsPosArrayList = new ArrayList<>();
private HashMap<String, Integer> elementsMap = new HashMap<>();
public ShoppingListViewAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
return layoutInflater.inflate(R.layout.list_view_row, viewGroup, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// CheckBox management
final CheckBox checkBoxElementName = view.findViewById(R.id.checkBoxElementName);
String elementName = cursor.getString(cursor.getColumnIndex(DBFieldsShoppingList.FIELD_ELEMENT_NAME));
checkBoxElementName.setText(elementName);
elementsMap.put(elementName, cursor.getPosition());
checkBoxElementName.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if (isChecked) {
elementsPosArrayList.add(elementsMap.get(checkBoxElementName.getText().toString()));
} else {
elementsPosArrayList.remove(elementsMap.get(checkBoxElementName.getText().toString()));
}
}
});
}
public long getElementId(Cursor crs, int position) {
crs.moveToPosition(position);
return crs.getLong(crs.getColumnIndex(DBFieldsShoppingList.FIELD_ID));
}
public List<Integer> getElementsPosArrayList() {
return elementsPosArrayList;
}
}
Classes for the database management are the following:
DBHelper:
public class DBHelperShoppingList extends SQLiteOpenHelper {
private String DBName;
public DBHelperShoppingList(#Nullable Context context, int dbVersion, String dbName) {
super(context, dbName, null, dbVersion);
this.DBName = dbName;
}
#Override
public void onCreate(SQLiteDatabase shoppingListDB) {
String q = "CREATE TABLE " + DBFieldsShoppingList.FIELD_TABLE_NAME +
" ( _id INTEGER PRIMARY KEY AUTOINCREMENT," +
DBFieldsShoppingList.FIELD_ELEMENT_NAME + " TEXT," +
DBFieldsShoppingList.FIELD_ELEMENT_TYPE + " TEXT," +
DBFieldsShoppingList.FIELD_ELEMENT_QUANT + " TEXT)";
shoppingListDB.execSQL(q);
}
#Override
public void onUpgrade(SQLiteDatabase shoppingListDB, int oldVersion, int newVersion) {}
}
Class for the fields of the database:
public class DBFieldsShoppingList {
public static final String FIELD_ID = "_Id";
public static final String FIELD_TABLE_NAME = "Shopping_List";
public static final String FIELD_ELEMENT_NAME = "Element_Name";
public static final String FIELD_ELEMENT_TYPE = "Element_Type";
public static final String FIELD_ELEMENT_QUANT = "Element_Quant";
}
Class database manager:
public class DBManagerShoppingList {
private DBHelperShoppingList dbHelperShoppingList;
public DBManagerShoppingList(Context ctx, String shoppingListName) {
dbHelperShoppingList = new DBHelperShoppingList(ctx, 1, shoppingListName);
}
public void dbSave(String elementName, String elementType, String elementQuantity) {
SQLiteDatabase db = dbHelperShoppingList.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(DBFieldsShoppingList.FIELD_ELEMENT_NAME, elementName);
cv.put(DBFieldsShoppingList.FIELD_ELEMENT_TYPE, elementType);
cv.put(DBFieldsShoppingList.FIELD_ELEMENT_QUANT, elementQuantity);
try {
db.insert(DBFieldsShoppingList.FIELD_TABLE_NAME, null, cv);
} catch (SQLiteException ignored) {}
}
public boolean deleteElement(long id) {
SQLiteDatabase db = dbHelperShoppingList.getWritableDatabase();
try {
return db.delete(DBFieldsShoppingList.FIELD_TABLE_NAME, DBFieldsShoppingList.FIELD_ID + "=?", new String[]{Long.toString(id)})>0;
} catch(SQLiteException exc) {
return false;
}
}
public static void deleteDB(Context ctx, String DBName) {
ctx.deleteDatabase(DBName);
}
public Cursor query() {
Cursor crs;
try {
SQLiteDatabase db = dbHelperShoppingList.getReadableDatabase();
crs = db.query(DBFieldsShoppingList.FIELD_TABLE_NAME, null, null, null, null, null, null, null);
} catch (SQLiteException exc) {
return null;
}
return crs;
}
}
I tought, in bindView method of the adapter class, to save a map between element name and its position. Then, to create a list of the element names of the checked elements. In this way, I can know the positions of the checked elements, from the map. Now, I have to get indexes of the checked elements, in order to remove them from database, with delete method of the database manager class.
How can I solve this problem? If my structure is not correct, say me how to change.
Thank you very much in advance.
Marco
Hy to everybody,
I found the solution. In database manager class, I changed the delete method in the following way:
public boolean deleteElement(String elementName) {
SQLiteDatabase db = dbHelperShoppingList.getWritableDatabase();
try {
return db.delete(DBFieldsShoppingList.FIELD_TABLE_NAME, DBFieldsShoppingList.FIELD_ELEMENT_NAME + "=?", new String[]{(elementName)})>0;
} catch(SQLiteException exc) {
return false;
}
}
This allows, to search by name in database.
In adapter class, I removed the map, and I wrote an arraylist of string, where I put name of the checked elements:
#Override
public void bindView(View view, Context context, Cursor cursor) {
// CheckBox management
final CheckBox checkBoxElementName = view.findViewById(R.id.checkBoxElementName);
String elementName = cursor.getString(cursor.getColumnIndex(DBFieldsShoppingList.FIELD_ELEMENT_NAME));
checkBoxElementName.setText(elementName);
checkBoxElementName.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if (isChecked) {
elementNamesArrayList.add(checkBoxElementName.getText().toString());
} else {
elementNamesArrayList.remove(checkBoxElementName.getText().toString());
}
}
});
}
Finally, in the activity class, the remove method is the following:
public void removeSelectedElements(View btnRemove) {
List<String> elementNamesArrayList = shoppingListViewAdapter.getElementNamesArrayList();
for (String item: elementNamesArrayList) {
dbShoppingList.deleteElement(item);
}
populateListView();
}
In this way I solved my problem.
Marco
I have a Fragment containing ListView. I'm adding some values to the database with a dialog and I want to update this ListView after dialog is dismissed. Also, when I change the tab, the ListView is not updated but when the application is turned on and off, the ListView is updated.
Fragment and Dialog classes are as follows:
Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_teams, container, false);
listViewTeams = rootView.findViewById(R.id.listView_teams);
TeamDatabase teamDatabase = new TeamDatabase(getContext());
teamDatabase.open();
arrayListTeam = teamDatabase.getAllTeams();
teamDatabase.close();
int resID = R.layout.team_list_item;
teamListArrayAdapter = new TeamListArrayAdapter(getContext(), resID, arrayListTeam);
listViewTeams.setAdapter(teamListArrayAdapter);
return rootView;
}
Dialog onClick Method:
#Override
public void onClick(View view) {
int id = view.getId();
switch (id){
case R.id.button_alertDialogAddTeam_cancel:
this.dismiss();
break;
case R.id.button_alertDialogAddTeam_ok:
Team team = new Team();
team.setName(editTextTeamName.getText().toString());
team.setCode(editTextTeamCode.getText().toString());
TeamDatabase teamDatabase = new TeamDatabase(getContext());
teamDatabase.open();
if(teamDatabase.addNewTeam(team)) {
Toast.makeText(getContext(), team.getCode() + " - " +
team.getName() + " was added successfully", Toast.LENGTH_SHORT).show();
}
this.dismiss();
break;
}
}
TeamDatabase class:
public static final String TABLE_NAME = "team";
private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_CODE = "code";
private static final String KEY_EMBLEM = "emblem";
private Context context;
public static final String CREATE_TABLE = "CREATE TABLE "+
TABLE_NAME + " ("+
KEY_ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, "+
KEY_NAME + " TEXT NOT NULL, "+
KEY_CODE + " TEXT NOT NULL, " +
KEY_EMBLEM + " TEXT);";
public TeamDatabase(Context context) {
super(context);
this.context = context;
}
public boolean addNewTeam(Team team){
ContentValues contentValues = new ContentValues();
contentValues.put(KEY_NAME, team.getName());
contentValues.put(KEY_CODE, team.getCode());
return db.insert(TABLE_NAME, null, contentValues) > 0;
}
public ArrayList<Team> getAllTeams()
{
ArrayList<Team> teams = new ArrayList<Team>();
Cursor cursor = db.query(TABLE_NAME, new String[]{KEY_ID,
KEY_NAME,
KEY_CODE}, null, null, null, null, null);
while(cursor.moveToNext()) {
Team team = new Team();
team.setId(cursor.getInt(cursor.getColumnIndex(KEY_ID)));
team.setName(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
team.setCode(cursor.getString(cursor.getColumnIndex(KEY_CODE)));
teams.add(team);
}
return teams;
}
DatabaseHelper class:
private static final String DATABASE_NAME = "fixtureCreator.db";
private static final int DATABASE_VERSION = 1;
public SQLiteDatabase db;
public DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(TeamDatabase.CREATE_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersinon) {
Log.w("TaskDBAdapter", "Upgrading from version " +
oldVersion + " to " + newVersinon + ", which will destroy all old data");
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TeamDatabase.TABLE_NAME);
onCreate(sqLiteDatabase);
}
public DatabaseHelper open() throws SQLException {
try{
db = this.getWritableDatabase();
}catch (SQLException e){
db = this.getReadableDatabase();
}
return this;
}
public void close(){
db.close();
}
On clicking the item in your first Activity, start your second Activity with startActivityForResult()
And then in Second Activity, after dismissing the dialog,in onClick of that button call,
intent.putExtra("new data", "item text");
setResult(RESULT_OK, intent);
finish();
Now you come back to your first Activity and here you have to implement onActivityResult() callback.
You can extract data from that intent's extras and set that respective item in your array and call notifyDataSetChanged().
This is ideally how you should be doing it.
The problem can be solved using fetching the data after saving it each time you click on the Dialog and then call notifyDataSetChanged() on your adapter. However, there is more elegant way of achieving this kind of behaviour, which will solve both of your problems using content observer.
In case of having a content observer in your database table you need to declare a URI first which references your table.
public static final Uri DB_TABLE_TEAM_URI = Uri
.parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TEAM);
// DB_TABLE_TEAM refers to the database table that you have for storing teams.
Now in your addNewTeam function you need to do the following.
public boolean addNewTeam(Team team) {
// .. Save the team in database
// Notify the observer about the change in the content
context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TEAM_URI, null);
}
You need to call notifyChange() function whenever you add or update an entry in your team table.
Now in your Activity or Fragment you need to register your observer on your cursor having the team data fetched from your team table.
cursor = teamDatabase.getAllTeamsInCursor();
this.registerContentObserver(cursor, DBConstants.DB_TABLE_TEAM_URI);
Now populate your ListView using the cursor by passing it to your adapter of ListView. The list will be updated automatically once a new data has been inserted in your team table.
Update
Modify the addNewTeam function in your TeamDatabase class as follows.
public static final Uri DB_TABLE_TEAM_URI = Uri
.parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TEAM);
public boolean addNewTeam(Team team){
ContentValues contentValues = new ContentValues();
contentValues.put(KEY_NAME, team.getName());
contentValues.put(KEY_CODE, team.getCode());
boolean success = db.insert(TABLE_NAME, null, contentValues) > 0;
context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TEAM_URI, null);
return success;
}
To implement the functionalities with LoaderCallbacks you first need to implement the interface of LoaderCallbacks in your Fragment. So the declaration of your Fragment will look like.
public class TeamsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
// .... Code
}
Now you need to override the functions that comes along with the interface of LoaderCallbacks.
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new SQLiteCursorLoader(getActivity()) {
#Override
public Cursor loadInBackground() {
// Initialize your database
TeamDatabase teamDatabase = new TeamDatabase(getActivity());
Cursor cursor = teamDatabase.getAllTeams();
if (cursor != null) {
// Register the content observer here
this.registerContentObserver(cursor, DBConstants.DB_TABLE_TEAM_URI);
}
return cursor;
}
};
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Set the cursor in your adapter. Handle null values in your setCursor function in your adapter. The cursor might return null when the table is empty.
teamAdapter.setCursor(cursor);
teamAdapter.notifyDataSetChanged();
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
Now in your onCreateView function in the Fragment, you need to initiate the loader to fetch data from the table.
getLoaderManager().initLoader(0, null, this).forceLoad();
And destroy the loader and un-register the receiver in the onDestroyView function.
#Override
public void onDestroyView() {
getLoaderManager().destroyLoader(0);
super.onDestroyView();
}
I was missing the SQLiteCursorLoader class to be added here.
package com.wooz.observer.databases;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
private final ForceLoadContentObserver mObserver;
private Uri mUri;
private String[] mProjection;
private String mSelection;
private String[] mSelectionArgs;
private String mSortOrder;
private Cursor mCursor;
/* Runs on a worker thread */
#Override
public abstract Cursor loadInBackground();
/**
* Registers an observer to get notifications from the content provider
* when the cursor needs to be refreshed.
*/
public void registerContentObserver(Cursor cursor, Uri observerUri) {
cursor.registerContentObserver(mObserver);
cursor.setNotificationUri(getContext().getContentResolver(), observerUri);
}
/* Runs on the UI thread */
#Override
public void deliverResult(Cursor cursor) {
try {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Creates an empty unspecified CursorLoader. You must follow this with
* calls to {#link #setUri(Uri)}, {#link #setSelection(String)}, etc
* to specify the query to perform.
*/
public SQLiteCursorLoader(Context context) {
super(context);
mObserver = new ForceLoadContentObserver();
}
/**
* Creates a fully-specified CursorLoader. See
* {#link ContentResolver#query(Uri, String[], String, String[], String)
* ContentResolver.query()} for documentation on the meaning of the
* parameters. These will be passed as-is to that call.
*/
public SQLiteCursorLoader(Context context, Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
super(context);
mObserver = new ForceLoadContentObserver();
mUri = uri;
mProjection = projection;
mSelection = selection;
mSelectionArgs = selectionArgs;
mSortOrder = sortOrder;
}
/**
* Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
* will be called on the UI thread. If a previous load has been completed and is still valid
* the result may be passed to the callbacks immediately.
* <p>
* Must be called from the UI thread
*/
#Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
public Uri getUri() {
return mUri;
}
public void setUri(Uri uri) {
mUri = uri;
}
public String[] getProjection() {
return mProjection;
}
public void setProjection(String[] projection) {
mProjection = projection;
}
public String getSelection() {
return mSelection;
}
public void setSelection(String selection) {
mSelection = selection;
}
public String[] getSelectionArgs() {
return mSelectionArgs;
}
public void setSelectionArgs(String[] selectionArgs) {
mSelectionArgs = selectionArgs;
}
public String getSortOrder() {
return mSortOrder;
}
public void setSortOrder(String sortOrder) {
mSortOrder = sortOrder;
}
#Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.print(prefix);
writer.print("mUri=");
writer.println(mUri);
writer.print(prefix);
writer.print("mProjection=");
writer.println(Arrays.toString(mProjection));
writer.print(prefix);
writer.print("mSelection=");
writer.println(mSelection);
writer.print(prefix);
writer.print("mSelectionArgs=");
writer.println(Arrays.toString(mSelectionArgs));
writer.print(prefix);
writer.print("mSortOrder=");
writer.println(mSortOrder);
writer.print(prefix);
writer.print("mCursor=");
writer.println(mCursor);
//writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
}
private class CursorLoaderContentObserver extends ContentObserver {
public CursorLoaderContentObserver() {
super(new Handler());
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
#Override
public void onChange(boolean selfChange) {
onContentChanged();
}
}
}
You should call notifyDataSetChanged just before dismissing the dialoge
teamListArrayAdapter.notifyDataSetChanged();
You should change you code something like this below
#Override
public void onClick(View view) {
int id = view.getId();
switch (id){
case R.id.button_alertDialogAddTeam_cancel:
this.dismiss();
break;
case R.id.button_alertDialogAddTeam_ok:
Team team = new Team();
team.setName(editTextTeamName.getText().toString());
team.setCode(editTextTeamCode.getText().toString());
TeamDatabase teamDatabase = new TeamDatabase(getContext());
teamDatabase.open();
if(teamDatabase.addNewTeam(team)) {
Toast.makeText(getContext(), team.getCode() + " - " +
team.getName() + " was added successfully", Toast.LENGTH_SHORT).show();
}
arrayListTeam = teamDatabase.getAllTeams();
teamListArrayAdapter.notifyDataSetChanged();
this.dismiss();
break;
}
}
Add this code in onCreateView:
teamListArrayAdapter.notifyDataSetChanged();
I followed the tutorial here : Tutorial todo APP and I would like to customize the code nad build training app. My first change is to delete things by their ID in database ( in the example they are being deleted by names ). Here is my current code responsible for deleting items:
// FUNCTION for DELETING EXERCISE
public void deleteExercise(View view) {
final View parent = (View) view.getParent();
AlertDialog dialog = new AlertDialog.Builder(this)
.setMessage("Are you sure, you want to delete exercise?")
.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
TextView exerciseTextView = (TextView) parent.findViewById(R.id.exercise_name);
String exercise = String.valueOf(exerciseTextView.getText());
SQLiteDatabase db = mHelper.getWritableDatabase();
db.delete(ExerciseContract.ExerciseEntry.TABLE,
ExerciseContract.ExerciseEntry.COL_EXERCISE_NAME + " = ?",
new String[]{exercise});
db.close();
updateUI();
}
})
.setNegativeButton("Cancel", null)
.create();
dialog.show();
}
// UPDATING USER INTERFACE AFTER CHANGES IN DB
private void updateUI() {
ArrayList<String> exerciseList = new ArrayList<>();
SQLiteDatabase db = mHelper.getReadableDatabase();
String[] projection = {
ExerciseContract.ExerciseEntry._ID,
ExerciseContract.ExerciseEntry.COL_EXERCISE_NAME,
//ExerciseContract.ExerciseEntry.COL_EXERCISE_DESCRIPTION
};
Cursor cursor = db.query(
ExerciseContract.ExerciseEntry.TABLE, //tablica do zapytań
projection, //zwracane kolumny
null, //columny dla WHERE
null, //wartosci dla WHERE
null,//nie grupuj wierszy
null,//nie filtruj grup
null); //porządek sortowania
while (cursor.moveToNext()) {
int idx = cursor.getColumnIndex(ExerciseContract.ExerciseEntry.COL_EXERCISE_NAME);
exerciseList.add(cursor.getString(idx));
}
if (mAdapter == null) {
mAdapter = new ArrayAdapter<>(this,
R.layout.item_workout,
R.id.exercise_name,
exerciseList);
mWorkoutListView.setAdapter(mAdapter);
} else {
mAdapter.clear();
mAdapter.addAll(exerciseList);
mAdapter.notifyDataSetChanged();
}
cursor.close();
db.close();
}
What would be the simplest way to do that?
You're not returning the id anywhere, I'd personally recommend a custom adapter. But for a quick way i think the easiest thing to do is delete it when the users click an item in the list view.
Set the lists views onItemClickListener.
mTaskListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Tasks task =(Tasks) mTaskListView.getItemAtPosition(position);
deleteTask(task.getId());
}
});
For the delete tasks pass in the _id of the clicked item.
public void deleteTask(long id) {
//TextView taskTextView = (TextView) parent.findViewById(R.id.task_title);
//String task = String.valueOf(taskTextView.getText());
SQLiteDatabase db = mHelper.getWritableDatabase();
db.delete(TaskContract.TaskEntry.TABLE, TaskContract.TaskEntry._ID + " = ?", new String[]{String.valueOf(id)});
db.close();
updateUI();
}
In the UpdateUI section change this while loop to also retrieve _id.
while (cursor.moveToNext()) {
int title = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE);
int _id = cursor.getColumnIndex(TaskContract.TaskEntry._ID);
Tasks tasks = new Tasks();
tasks.setId(cursor.getInt(_id));
tasks.setTitle(cursor.getString(title));
taskList.add(tasks);
}
Lastly create a model for your tasks.
package com.aziflaj.todolist.db;
public class Tasks {
String title;
int id;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Override
public String toString()
{
return getTitle();
}
}
Also remember to remove the button from the LayoutFile. You could show a dialog before deleting, but this was a quick solution. Ideally i'd recommended creating your own custom adapter. You'll probably have to do that if you wish to keep the button in and delete it that way.
Custom Adapter for List View
Create on common method inside your database helper class.
public boolean deleteRowData(String tableName, String selection, String[] selectionArgs) {
open();
sqLiteDb.delete(tableName, selection, selectionArgs);
close();
return true;
}
// ---opens the database---
public NotesData open() throws SQLException {
DatabaseHelper dbHelper = new DatabaseHelper(context);
sqLiteDb = dbHelper.getWritableDatabase();
sqLiteDb = dbHelper.getReadableDatabase();
if (!sqLiteDb.isReadOnly()) {
// Enable foreign key constraints
sqLiteDb.execSQL("PRAGMA foreign_keys = ON;");
}
return this;
}
// ---closes the database---
public void close() {
if (sqLiteDb != null && sqLiteDb.isOpen()) {
sqLiteDb.close();
}
}
Now onClick of listView Item do this:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showDeleteAlertDialog(position); // Common method for delete alert dialog
}
});
private void showDeleteAlertDialog(int arrayPosition) {
//delete data from
String title = getResources().getString(R.string.warning);
String message = getResources().getString(R.string.warning_for_delete);
final android.app.Dialog dialog = new Dialog(YourActivity.this, R.style.DialogTheme); //this is a reference to the style above
dialog.setContentView(R.layout.custom_dialog); //I saved the xml file above as custom_dialog.xml
dialog.setCancelable(true);
//to set the message
TextView sub_message = (TextView) dialog.findViewById(R.id.tv_Message);
TextView dialogTitle = (TextView) dialog.findViewById(R.id.tv_Title);
Button btn_Negative = (Button) dialog.findViewById(R.id.btn_Negative);
Button btn_Positive = (Button) dialog.findViewById(R.id.btn_Positive);
btn_Negative.setText("Cancel");
btn_Positive.setText("Delete");
sub_message.setText("Are you sure you want to delete this>");
dialogTitle.setText(title);
//add some action to the buttons
btn_Positive.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final String selection = YourColumnName + " LIKE ?";
final String[] selectionArgs = {myArrayList.get(arrayPosition).getID()};
mHelper.deleteRowData(YourTableNAME, selection, selectionArgs);
// Now just remove that array position from your arraylist (from activity & adapter arralist too).
myArrayList.remove(arrayPosition);
yourAdapter.yourArrayList.remove(arrayPosition);
notifyDataSetChanged();
}
});
btn_Negative.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
}
Although this question has been asked many times on here, I just can't find the proper answer to fit my code. I realise it may be something small, but I just can't seem to find the problem as I'm only really new to this.
Here's my code getClientNames in DatabaseHelper class:
public Cursor getSitesByClientname(String id) {
String[] args={id};
Cursor myCursor = db.rawQuery("SELECT client_sitename FROM " + CLIENT_SITES_TABLE + " WHERE client_id=?", args);
String results = "";
/*int count = myCursor.getCount();
String[] results = new String[count + 1];
int i = 0;*/
if (myCursor != null) {
if(myCursor.getCount() > 0)
{
for (myCursor.moveToFirst(); !myCursor.isAfterLast(); myCursor.moveToNext())
{
results = results + myCursor.getString(myCursor.getColumnIndex("client_sitename"));
}
}
}
return results;
}
One problem is that I'm returning a 'String' to the 'Cursor', but I'm not sure what is the best way around this, as I think I should be returning a Cursor.
Here's the ClientSites class where I want to display the data:
public class ClientSites extends Activity {
//public final static String ID_EXTRA="com.example.loginfromlocal._ID";
private DBUserAdapter dbHelper = null;
private Cursor ourCursor = null;
private Adapter adapter=null;
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
public void onCreate(Bundle savedInstanceState) {
try
{
super.onCreate(savedInstanceState);
setContentView(R.layout.client_sites);
Intent i = getIntent();
String uID = String.valueOf(i.getIntExtra("userID", 0));
ListView myListView = (ListView)findViewById(R.id.myListView);
dbHelper = new DBUserAdapter(this);
//dbHelper.createDatabase();
dbHelper.openDataBase();
ourCursor = dbHelper.getSitesByClientname(uID);
Log.e("ALERT", uID.toString());
startManagingCursor(ourCursor);
Log.e("ERROR", "After start manage cursor: ");
//#SuppressWarnings("deprecation")
//SimpleCursorAdapter adapter = new SimpleCursorAdapter(getBaseContext(), R.id.myListView, null, null, null);
CursorAdapter adapter = new SimpleCursorAdapter(this, R.id.myListView, null, null, null, 0);
adapter = new Adapter(ourCursor);
//Toast.makeText(ClientSites.this, "Booo!!!", Toast.LENGTH_LONG).show();
myListView.setAdapter(adapter);
myListView.setOnItemClickListener(onListClick);
}
catch (Exception e)
{
Log.e("ERROR", "XXERROR IN CODE: " + e.toString());
e.printStackTrace();
}
}
private AdapterView.OnItemClickListener onListClick=new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent,
View view, int position,
long id)
{
Intent i=new Intent(ClientSites.this, InspectionPoints.class);
i.putExtra(ID_EXTRA, String.valueOf(id));
startActivity(i);
}
};
class Adapter extends CursorAdapter {
#SuppressWarnings("deprecation")
Adapter(Cursor c) {
super(ClientSites.this, c);
}
//#Override
public void bindView(View row, Context ctxt,
Cursor c) {
Holder holder=(Holder)row.getTag();
holder.populateFrom(c, dbHelper);
}
#Override
public View newView(Context ctxt, Cursor c,
ViewGroup parent) {
LayoutInflater inflater=getLayoutInflater();
View row = inflater.inflate(R.layout.row, parent, false);
Holder holder=new Holder(row);
row.setTag(holder);
return(row);
}
}
static class Holder {
private TextView name=null;
Holder(View row) {
name=(TextView)row.findViewById(R.id.ingredientText);
}
void populateFrom(Cursor c, DBUserAdapter r) {
name.setText(r.getName(c));
}
}
}
Here is the code I'm now using to try and display the data in the Listview. I have altered it somewhat from my original attempt, but still not sure what I'm doing wrong.
public void onCreate(Bundle savedInstanceState) {
try
{
super.onCreate(savedInstanceState);
//setContentView(R.layout.client_sites);
Intent i = getIntent();
String uID = String.valueOf(i.getIntExtra("userID", 0));
//int uID = i.getIntExtra("userID", 0);
//ListView myListView = (ListView)findViewById(R.id.myListView);
dbHelper = new DBUserAdapter(this);
dbHelper.createDatabase();
dbHelper.openDataBase();
String[] results = dbHelper.getSitesByClientname(uID);
//setListAdapter(new ArrayAdapter<String>(ClientSites.this, R.id.myListView, results));
//adapter = new ArrayAdapter<String>(ClientSites.this, R.id.myListView, results);
setListAdapter(new ArrayAdapter<String>(ClientSites.this, R.layout.client_sites, results));
//ListView myListView = (ListView)findViewById(R.id.myListView);
ListView listView = getListView();
listView.setTextFilterEnabled(true);
//ourCursor = dbHelper.getSitesByClientname(uID);
//Log.e("ALERT", uID.toString());
//startManagingCursor(ourCursor);
//Log.e("ERROR", "After start manage cursor: ");
//#SuppressWarnings("deprecation")
//SimpleCursorAdapter adapter = new SimpleCursorAdapter(getBaseContext(), R.id.myListView, null, null, null); // LOOK AT THIS IN THE MORNING!!!!!!!!!!!
//CursorAdapter adapter = new SimpleCursorAdapter(this, R.id.myListView, null, null, null, 0);
//adapter = new Adapter(ourCursor);
//Toast.makeText(ClientSites.this, "Booo!!!", Toast.LENGTH_LONG).show();
//myListView.setAdapter(adapter);
//myListView.setOnItemClickListener(onListClick);
I Created a Listview from a database. Here is the code I used for my app
Here is part of the database handler. It will return a List of Categories for my listview. It can return a list of Strings if that is what you need.
public List<Category> getAllCategorys() {
ArrayList<Category> categoryList = new ArrayList<Category>();
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_CATEGORY;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
try{
if (cursor.moveToFirst()) {
do {
Category category = new Category();
category.setID(Integer.parseInt(cursor.getString(0)));
category.setCategory(cursor.getString(1));
// Adding category to list
categoryList.add(category);
} while (cursor.moveToNext());
}
}finally{
cursor.close();
}
db.close();
// return category list
return categoryList;
Here is how I fill the ListView by calling the database.
int size = db.getCategoryCount();
List<Category> categoryList = db.getAllCategorys();
category_data = new String[size-1];
int i=0;
for(Category cn : categoryList)
{
category_data[i] = cn.getCategory(); // get the name of the category and add it to array
i++;
}
listAdapter = new ArrayAdapter<String>(this, R.layout.categoryrow, category_data);
listViw.setAdapter(listAdapter);
EDIT: Here is something that should work for you
public List<String> getSitesByClientname(String id) {
String[] args={id};
ArrayList<String> result = new ArrayList<String>();
SQLiteDatabase db = this.getWritableDatabase();
Cursor myCursor = db.rawQuery("SELECT client_sitename FROM " + CLIENT_SITES_TABLE + " WHERE client_id=?", args);
try{
if (myCursor.moveToFirst()){
do{
result.add(myCursor.getString(myCusor.getString(myCursor.getColumnIndex("client_sitename"));
}while(myCursor.moveToNext());
}
}finally{
myCursor.close();
}
db.close();
return result;
}
Use it like this
List<String> sites_data = dbHelper.getSitesByClientname(uID);
result_data = new String[sites_data.size()];
int i=0;
for(String s : sites_data)
{
result_data[i] = s; // get the name of the category and add it to array
i++;
}
listAdapter = new ArrayAdapter<String>(this, R.layout.client_sites, result_data);
listViw.setAdapter(listAdapter);
If you want to return the Cursor from dbHelper you can do something like this...
public Cursor getSitesByClientname(String id) {
String[] args={id};
return db.rawQuery("SELECT client_sitename FROM " + CLIENT_SITES_TABLE + " WHERE client_id=?", args);
}
I'd also take some time to read the listview tutorial
I'm calling the object here.
public class TestDetails extends ListActivity {
protected TextView testNameText;
protected SQLiteDatabase db;
protected TextView testvalueText;
protected List<TestAction> actions;
protected TestItemAdapter adapter;
protected int testId;
protected int categoryId;
#Override
//adds options menu
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.list_search: onSearchRequested();
break;
}
return true;
}
//end of add options menu
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_details);
// Get the intent, verify the action and get the query
db = (new DatabaseHelper(this)).getWritableDatabase();
Intent intent1 = getIntent();
SimpleSearch SSearch = new SimpleSearch();
if (Intent.ACTION_SEARCH.equals(intent1.getAction())) {
String query = intent1.getStringExtra(SearchManager.QUERY);
SSearch.testSearch(query);
}
testId = getIntent().getIntExtra("EMPLOYEE_ID", 0);
SQLiteDatabase db = (new DatabaseHelper(this)).getWritableDatabase();
Cursor cursor = db.rawQuery("SELECT emp._id, emp.firstName, emp.lastName, emp.title, emp.officePhone, emp.cellPhone, emp.email, emp.managerId, mgr.firstName managerFirstName, mgr.lastName managerLastName FROM employee emp LEFT OUTER JOIN employee mgr ON emp.managerId = mgr._id WHERE emp._id = ?",
new String[]{""+testId});
if (cursor.getCount() == 1)
{
cursor.moveToFirst();
testNameText = (TextView) findViewById(R.id.employeeName);
testNameText.setText(cursor.getString(cursor.getColumnIndex("firstName")) + " " + cursor.getString(cursor.getColumnIndex("lastName")));
actions = new ArrayList<TestAction>();
String officePhone = cursor.getString(cursor.getColumnIndex("officePhone"));
if (officePhone != null) {
actions.add(new TestAction("Call office", officePhone, TestAction.ACTION_CALL));
}
String cellPhone = cursor.getString(cursor.getColumnIndex("cellPhone"));
if (cellPhone != null) {
actions.add(new TestAction("Call mobile", cellPhone, TestAction.ACTION_CALL));
actions.add(new TestAction("SMS", cellPhone, TestAction.ACTION_SMS));
}
String email = cursor.getString(cursor.getColumnIndex("email"));
if (email != null) {
actions.add(new TestAction("Email", email, TestAction.ACTION_EMAIL));
}
categoryId = cursor.getInt(cursor.getColumnIndex("managerId"));
if (categoryId>0) {
actions.add(new TestAction("View manager", cursor.getString(cursor.getColumnIndex("managerFirstName")) + " " + cursor.getString(cursor.getColumnIndex("managerLastName")), TestAction.ACTION_VIEW));
}
cursor = db.rawQuery("SELECT count(*) FROM employee WHERE managerId = ?",
new String[]{""+testId});
cursor.moveToFirst();
int count = cursor.getInt(0);
if (count>0) {
actions.add(new TestAction("View direct reports", "(" + count + ")", TestAction.ACTION_REPORTS));
}
adapter = new TestItemAdapter();
setListAdapter(adapter);
}
}
class TestItemAdapter extends ArrayAdapter<TestAction> {
TestItemAdapter() {
super(TestDetails.this, R.layout.action_list_item, actions);
}
#Override
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
return false;
}
public View getView(int position, View convertView, ViewGroup parent) {
TestAction action = actions.get(position);
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.action_list_item, parent, false);
TextView label = (TextView) view.findViewById(R.id.label);
label.setText(action.getLabel());
TextView data = (TextView) view.findViewById(R.id.data);
data.setText(action.getData());
return view;
}
}
}
This is the class from which I'm calling the object.(part of the class)
public class SimpleSearch extends ListActivity {
protected SQLiteDatabase db;
protected Cursor cursor;
protected ListAdapter adapter;
protected String query;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
db = (new DatabaseHelper(this)).getWritableDatabase();
// Get the intent, verify the action and get the query
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
testSearch(query);
}
else TestListAll();
}
But I'm getting a force close on running the application. Stack trace shows the error to be in SSearch.testSearch(query); statement. What am I missing here?
Stack trace:
app_vercode:1
device_model:umts_jordan
build_version:1.11.18
condition:1
processName:com.simple.search
pid:3529
uid:10063
tag:null
shortMsg:java.lang.NullPointerException
longMsg:java.lang.NullPointerException: Unable to start activity ComponentInfo{com.simple.search/com.simple.search.TestDetails}: java.lang.NullPointerException
stackTrace:java.lang.RuntimeException: Unable to start activity ComponentInfo{com.simple.search/com.simple.search.TestDetails}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1664)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1680)
at android.app.ActivityThread.access$1500(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3703)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.simple.search.SimpleSearch.testSearch(SimpleSearch.java:68)
at com.simple.search.TestDetails.onCreate(TestDetails.java:59)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1628)
... 11 more
Most likely the line
String query = intent1.getStringExtra(SearchManager.QUERY);
Is assigning a null value to query - are you sure the correct extra is there? I usually get extras in this manner:
public static final String TAG = "MyActivity";
public static final String DATA_KEY = "DataKey";
public static final String MY_CUSTOM_ACTION = "MyCustomSearchAction";
public void onCreate(Bundle savedInstanceState) {
Intent intent1 = getIntent();
SimpleSearch SSearch = new SimpleSearch();
if (intent1.getAction().equals(MY_CUSTOM_ACTION)
&& intent1.hasExtra(SearchManager.QUERY)
&& intent.hasExtra(DATA_KEY)) {
String query = intent1.getStringExtra(SearchManager.QUERY);
DataObject data = intent1.getParcelableExtra(DATA_KEY);
if (query != null && data != null)
SSearch.testSearch(query, data);
else {
//invalid query
Log.d(TAG,"Activity started with invalid query data - closing");
this.finish();
return;
}
} else {
//Invalid Intent
Log.d(TAG,"Activity started with invalid intent - closing");
this.finish();
return;
}
}
A data object can look like this:
public class DataObject implements Parcelable {
public String someData;
public String someMoreData;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(someData);
dest.writeString(someMoreDate);
}
//Constructor for parceler
public DataObject(Parcel src) {
someData = src.readString();
someMoreData = src.readString();
}
public static final Parcelable.Creator<DataObject> CREATOR =
new Parcelable.Creator<DataObject>() {
public DataObject createFromParcel(Parcel in) {
return new DataObject(in);
}
public DataObject[] newArray(int size) {
return new DataObject[size];
}
};
}
To start your activity just go:
DataObject data = new DataObject();
data.someData = "test";
data.someMoreData = "test2";
Intent intent = new Intnent(this, MyActivity.class);
intent.setAction(MyActivity.MY_CUSTOM_ACTION);
intent.putExtra(MyActivity.DATA_KEY,data);
intent.putExtra(SearchManager.QUERY, "Query");
startActivity(intent);