I'm making an application for android and I'm using CursorTreeAdapter as ExpandableListView. I want to use a search box for displaying the filtered ExpandableListView items. Like this:
Here's the code what I've written so far:
MainActivity.java:
package com.example.cursortreeadaptersearch;
import java.util.HashMap;
import android.app.SearchManager;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.widget.ExpandableListView;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
public class MainActivity extends SherlockFragmentActivity {
private SearchView search;
private MyListAdapter listAdapter;
private ExpandableListView myList;
private final String DEBUG_TAG = getClass().getSimpleName().toString();
/**
* The columns we are interested in from the database
*/
static final String[] CONTACTS_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_ID,
ContactsContract.CommonDataKinds.Email.DATA,
ContactsContract.CommonDataKinds.Photo.CONTACT_ID };
static final String[] GROUPS_SUMMARY_PROJECTION = new String[] {
ContactsContract.Groups.TITLE, ContactsContract.Groups._ID,
ContactsContract.Groups.SUMMARY_COUNT,
ContactsContract.Groups.ACCOUNT_NAME,
ContactsContract.Groups.ACCOUNT_TYPE,
ContactsContract.Groups.DATA_SET };
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
search = (SearchView) findViewById(R.id.search);
search.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
search.setIconifiedByDefault(false);
search.setOnQueryTextListener(new OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
listAdapter.filterList(query);
expandAll();
return false;
}
#Override
public boolean onQueryTextChange(String query) {
listAdapter.filterList(query);
expandAll();
return false;
}
});
search.setOnCloseListener(new OnCloseListener() {
#Override
public boolean onClose() {
listAdapter.filterList("");
expandAll();
return false;
}
});
// get reference to the ExpandableListView
myList = (ExpandableListView) findViewById(R.id.expandableList);
// create the adapter
listAdapter = new MyListAdapter(null, MainActivity.this);
// attach the adapter to the list
myList.setAdapter(listAdapter);
Loader<Cursor> loader = getSupportLoaderManager().getLoader(-1);
if (loader != null && !loader.isReset()) {
runOnUiThread(new Runnable() {
public void run() {
getSupportLoaderManager().restartLoader(-1, null,
mSpeakersLoaderCallback);
}
});
} else {
runOnUiThread(new Runnable() {
public void run() {
getSupportLoaderManager().initLoader(-1, null,
mSpeakersLoaderCallback).forceLoad();
;
}
});
}
}
#Override
public void onResume() {
super.onResume();
getApplicationContext().getContentResolver().registerContentObserver(
ContactsContract.Data.CONTENT_URI, true,
mSpeakerChangesObserver);
}
#Override
public void onPause() {
super.onPause();
getApplicationContext().getContentResolver().unregisterContentObserver(
mSpeakerChangesObserver);
}
// method to expand all groups
private void expandAll() {
int count = listAdapter.getGroupCount();
for (int i = 0; i < count; i++) {
myList.expandGroup(i);
}
}
public LoaderManager.LoaderCallbacks<Cursor> mSpeakersLoaderCallback = new LoaderCallbacks<Cursor>() {
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.d(DEBUG_TAG, "onCreateLoader for loader_id " + id);
CursorLoader cl = null;
HashMap<Integer, Integer> groupMap = listAdapter.getGroupMap();
if (id != -1) {
int groupPos = groupMap.get(id);
if (groupPos == 0) { // E-mail group
String[] PROJECTION = new String[] {
ContactsContract.RawContacts._ID,
ContactsContract.CommonDataKinds.Email.DATA };
String sortOrder = "CASE WHEN "
+ ContactsContract.Contacts.DISPLAY_NAME
+ " NOT LIKE '%#%' THEN 1 ELSE 2 END, "
+ ContactsContract.Contacts.DISPLAY_NAME + ", "
+ ContactsContract.CommonDataKinds.Email.DATA
+ " COLLATE NOCASE";
String selection = ContactsContract.CommonDataKinds.Email.DATA
+ " NOT LIKE ''";
cl = new CursorLoader(getApplicationContext(),
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
PROJECTION, selection, null, sortOrder);
} else if (groupPos == 1) { // Name group
Uri contactsUri = ContactsContract.Data.CONTENT_URI;
String selection = "(("
+ ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " NOTNULL) AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.HAS_PHONE_NUMBER
+ "=1) AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " != '') AND ("
+ ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID
+ " = '1' ))"; // Row ID 1 == All contacts
String sortOrder = ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
cl = new CursorLoader(getApplicationContext(), contactsUri,
CONTACTS_PROJECTION, selection, null, sortOrder);
}
} else {
// group cursor
Uri groupsUri = ContactsContract.Groups.CONTENT_SUMMARY_URI;
String selection = "((" + ContactsContract.Groups.TITLE
+ " NOTNULL) AND (" + ContactsContract.Groups.TITLE
+ " == 'Coworkers' ) OR ("
+ ContactsContract.Groups.TITLE
+ " == 'My Contacts' ))"; // Select only Coworkers
// (E-mail only) and My
// Contacts (Name only)
String sortOrder = ContactsContract.Groups.TITLE
+ " COLLATE LOCALIZED ASC";
cl = new CursorLoader(getApplicationContext(), groupsUri,
GROUPS_SUMMARY_PROJECTION, selection, null, sortOrder);
}
return cl;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in.
int id = loader.getId();
// Log.d("Dump Cursor MainActivity",
// DatabaseUtils.dumpCursorToString(data));
Log.d(DEBUG_TAG, "onLoadFinished() for loader_id " + id);
if (id != -1) {
// child cursor
if (!data.isClosed()) {
Log.d(DEBUG_TAG, "data.getCount() " + data.getCount());
HashMap<Integer, Integer> groupMap = listAdapter
.getGroupMap();
try {
int groupPos = groupMap.get(id);
Log.d(DEBUG_TAG, "onLoadFinished() for groupPos "
+ groupPos);
listAdapter.setChildrenCursor(groupPos, data);
} catch (NullPointerException e) {
Log.w("DEBUG",
"Adapter expired, try again on the next query: "
+ e.getMessage());
}
}
} else {
listAdapter.setGroupCursor(data);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// is about to be closed.
int id = loader.getId();
Log.d(DEBUG_TAG, "onLoaderReset() for loader_id " + id);
if (id != 1) {
// child cursor
try {
listAdapter.setChildrenCursor(id, null);
} catch (NullPointerException e) {
Log.w(DEBUG_TAG,
"Adapter expired, try again on the next query: "
+ e.getMessage());
}
} else {
listAdapter.setGroupCursor(null);
}
}
};
private ContentObserver mSpeakerChangesObserver = new ContentObserver(
new Handler()) {
#Override
public void onChange(boolean selfChange) {
if (getApplicationContext() != null) {
runOnUiThread(new Runnable() {
public void run() {
getSupportLoaderManager().restartLoader(-1, null,
mSpeakersLoaderCallback);
}
});
}
}
};
}
MyListAdapter.java:
package com.example.cursortreeadaptersearch;
import java.util.HashMap;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorTreeAdapter;
import android.widget.TextView;
public class MyListAdapter extends CursorTreeAdapter {
public HashMap<String, View> childView = new HashMap<String, View>();
/**
* The columns we are interested in from the database
*/
private final String DEBUG_TAG = getClass().getSimpleName().toString();
protected final HashMap<Integer, Integer> mGroupMap;
private MainActivity mActivity;
private LayoutInflater mInflater;
String mConstraint;
public MyListAdapter(Cursor cursor, Context context) {
super(cursor, context);
mActivity = (MainActivity) context;
mInflater = LayoutInflater.from(context);
mGroupMap = new HashMap<Integer, Integer>();
}
#Override
public View newGroupView(Context context, Cursor cursor,
boolean isExpanded, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.list_group, parent, false);
return view;
}
#Override
public void bindGroupView(View view, Context context, Cursor cursor,
boolean isExpanded) {
TextView lblListHeader = (TextView) view
.findViewById(R.id.lblListHeader);
if (lblListHeader != null) {
lblListHeader.setText(cursor.getString(cursor
.getColumnIndex(ContactsContract.Groups.TITLE)));
}
}
#Override
public View newChildView(Context context, Cursor cursor,
boolean isLastChild, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.list_item, parent, false);
return view;
}
#Override
public void bindChildView(View view, Context context, Cursor cursor,
boolean isLastChild) {
TextView txtListChild = (TextView) view.findViewById(R.id.lblListItem);
if (txtListChild != null) {
txtListChild.setText(cursor.getString(1)); // Selects E-mail or
// Display Name
}
}
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that
// group
int groupPos = groupCursor.getPosition();
int groupId = groupCursor.getInt(groupCursor
.getColumnIndex(ContactsContract.Groups._ID));
Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);
mGroupMap.put(groupId, groupPos);
Loader loader = mActivity.getSupportLoaderManager().getLoader(groupId);
if (loader != null && !loader.isReset()) {
mActivity.getSupportLoaderManager().restartLoader(groupId, null,
mActivity.mSpeakersLoaderCallback);
} else {
mActivity.getSupportLoaderManager().initLoader(groupId, null,
mActivity.mSpeakersLoaderCallback);
}
return null;
}
// Access method
public HashMap<Integer, Integer> getGroupMap() {
return mGroupMap;
}
public void filterList(CharSequence constraint) {
// TODO Filter the data here
}
}
I have very considerably simplified and cleaned the code (so that you guys that not need to do).
As you can see, I've in total 3 cursors (1 for the groups and 2 for the children). The data is get from ContactsContract (which are the contacts of the user).
The cursor from child 1 represents all the e-mails of all contacts and the cursor from child 2 represents all the display names of the contacts. (The most of the loader functions is from here).
The only thing is now how do I implement a search? Should I do it trough Content Provider or a raw query in the database? I would like that the results of both children tables is displayed. I think because it's easy to make a fault while typing that tokenize=porter is a option in my case.
I hope that someone can point me in a good direction.
Edit:
I've tried this in MyListAdapter.java (with FilterQueryProvider as suggested by Kyle I.):
public void filterList(CharSequence constraint) {
final Cursor oldCursor = getCursor();
setFilterQueryProvider(filterQueryProvider);
getFilter().filter(constraint, new FilterListener() {
public void onFilterComplete(int count) {
// assuming your activity manages the Cursor
// (which is a recommended way)
notifyDataSetChanged();
// stopManagingCursor(oldCursor);
// final Cursor newCursor = getCursor();
// startManagingCursor(newCursor);
// // safely close the oldCursor
if (oldCursor != null && !oldCursor.isClosed()) {
oldCursor.close();
}
}
});
}
private FilterQueryProvider filterQueryProvider = new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
// assuming you have your custom DBHelper instance
// ready to execute the DB request
String s = '%' + constraint.toString() + '%';
return mActivity.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
MainActivity.CONTACTS_PROJECTION,
ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME + " LIKE ?",
new String[] { s },
null);
}
};
And this in MainActivity.java:
search.setOnQueryTextListener(new OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
listAdapter.filterList(query);
expandAll();
return false;
}
#Override
public boolean onQueryTextChange(String query) {
listAdapter.filterList(query);
expandAll();
return false;
}
});
search.setOnCloseListener(new OnCloseListener() {
#Override
public boolean onClose() {
listAdapter.filterList("");
expandAll();
return false;
}
});
But then I get these errors when I try to search:
12-20 13:20:19.449: E/CursorWindow(28747): Failed to read row 0, column -1 from a CursorWindow which has 96 rows, 4 columns.
12-20 13:20:19.449: D/AndroidRuntime(28747): Shutting down VM
12-20 13:20:19.449: W/dalvikvm(28747): threadid=1: thread exiting with uncaught exception (group=0x415c62a0)
12-20 13:20:19.499: E/AndroidRuntime(28747): FATAL EXCEPTION: main
12-20 13:20:19.499: E/AndroidRuntime(28747): java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
What I'm doing wrong? Or is this because I'm only return 1 query (display names) instead of 2 (display names and e-mails) in runQuery?
Edit 2:
First of all I've changed all my database implementations to ContactsContract. This is become easier to maintain so that you don't have to write your own database implementation.
What I now have tried is to save my constraint in runQuery() of FilterQueryProvider, and then in getChildrenCursor run a query against that constraint. (as suggested by JRaymond)
private String mConstraint;
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that
// group
int groupPos = groupCursor.getPosition();
int groupId = groupCursor.getInt(groupCursor
.getColumnIndex(ContactsContract.Groups._ID));
Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);
mGroupMap.put(groupId, groupPos);
Bundle b = new Bundle();
b.putString("constraint", mConstraint);
Loader loader = mActivity.getSupportLoaderManager().getLoader(groupId);
if (loader != null && !loader.isReset()) {
if (mConstraint == null || mConstraint.isEmpty()) {
// Normal query
mActivity.getSupportLoaderManager().restartLoader(groupId,
null, mActivity.mSpeakersLoaderCallback);
} else {
// Constrained query
mActivity.getSupportLoaderManager().restartLoader(groupId, b,
mActivity.mSpeakersLoaderCallback);
}
} else {
if (mConstraint == null || mConstraint.isEmpty()) {
// Normal query
mActivity.getSupportLoaderManager().initLoader(groupId, null,
mActivity.mSpeakersLoaderCallback);
} else {
// Constrained query
mActivity.getSupportLoaderManager().initLoader(groupId, b,
mActivity.mSpeakersLoaderCallback);
}
}
return null;
}
And here is the FilterQueryProvider:
private FilterQueryProvider filterQueryProvider = new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
// Load the group cursor here and assign mConstraint
mConstraint = constraint.toString();
Uri groupsUri = ContactsContract.Groups.CONTENT_SUMMARY_URI;
String selection = "((" + ContactsContract.Groups.TITLE
+ " NOTNULL) AND (" + ContactsContract.Groups.TITLE
+ " == 'Coworkers' ) OR (" + ContactsContract.Groups.TITLE
+ " == 'My Contacts' ))"; // Select only Coworkers
// (E-mail only) and My
// Contacts (Name only)
String sortOrder = ContactsContract.Groups.TITLE
+ " COLLATE LOCALIZED ASC";
return mActivity.getContentResolver().query(groupsUri,
MainActivity.GROUPS_SUMMARY_PROJECTION, selection, null,
sortOrder);
}
};
As you can see I've load the query of the groups in order to get the getChildrenCursor working. Only what for query should I run in MainActivity that I get from the bundle?
I've looked into your issue, and unfortunately I don't have time to replicate your setup. In generic terms, however, You should be able to save your constraint, and then in 'getChildrenCursor', run a query against that constraint:
Cursor getChildrenCursor(Cursor groupCursor) {
if (mConstraint == null || mConstraint.isEmpty()) {
// Normal query
} else {
// Constrained query
}
}
I'm not certain, but I'm pretty sure that getChildrenCursor() will get called in response to a change of the parent cursor when you return the cursor in the filterQueryProvider(). You then just manage the null/filled state of the constraint.
Details:
In your filterList function, instead of doing a complicated procedure, just call runQueryOnBackgroundThread(constraint);. This will automatically offload database work to the background. Save your constraint in your filterQueryProvider:
String s = '%' + constraint.toString() + '%';
mConstraint = s;
For the query, it just depends on what you're trying to get out of the database - a quick adjustment to the code you posted runs the query like so:
String selection = ContactsContract.CommonDataKinds.Email.DATA
+ " NOT LIKE ''";
if (constraint != null) {
selection += " AND " + ContactsContract.CommonDataKinds.Email.DATA + " LIKE ?";
}
cl = new CursorLoader(getApplicationContext(),
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
PROJECTION, selection, constraint, sortOrder);
The one thing I'm not too sure about is the auto expand thing you have going, My filter works but you need to collapse and open the list again to see the change.
What you should to do is extend FilterQueryProvider. This provides a runQuery() function that returns a new cursor of filtered results (likely accomplished with a database query).
In your CursorTreeAdapter adapter implementation you will then use the setFilterQueryProvider() method to provide it an instance of your FilterQueryProvider.
Finally, when you want to perform filtering you will call mAdapter.getFilter().filter("c").
However seeing as you are not actually using the SearchView autocomplete features and instead populating your own list, your chosen solution is quite a bit more complicated than it needs to be. Why don't you instead drop the Content Provider and CursorTreeAdapter and use a more simple in-memory scheme of lists or maps to back your adapter? Populate the in-memory data as required (can your entire dataset fit in memory?).
Related
I am working on an Android App with a Database and I am having troubles in passing the right context to my Database Handler, as the instance of the App Context that I am passing to the Database Handler seems to be always null;
as I have been working on this for hours to make it work, I would appreciate any hints or constructive Feedback to make this work.
The app crashes always with the same Null Pointer Exception:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.Cursor android.database.sqlite.SQLiteDatabase.rawQuery(java.lang.String, java.lang.String[])' on a null object reference
in the method DatabaseHandler.getAllItems, at this part:
try (SQLiteDatabase db = this.getWritableDatabase(GoldbekStorageApp.getInstance())) {
cursor = db.rawQuery(selectQuery, null);
}
This is my Database Handler:
enter code herepackage com.example.xxx;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class DatabaseHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "itemsManager";
private static final String TABLE_ITEMS = "items";
private static final String KEY_NO = "number";
private static final String TAG = "Database values";
private static final String KEY_NAME = "name";
private static final String KEY_EAN = "ean";
private static final String KEY_TYPE = "type";
private static final String KEY_ITEMGROUPNAME = "itemgroupname";
private static final String KEY_DESTRUCTION = "destruction";
private static final String KEY_ARCHIVED = "archived";
public static final String TAGGGG = "Datenbank" ;
private String No;
private String Ean;
private String Name;
private String Itemgroupname;
private String Type;
private Boolean Destruction;
private Boolean Archived;
public Context context;
public String uname;
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
//3rd argument to be passed is CursorFactory instance
this.context = context; // add this line to initialize your context.
}
// Creating Tables
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_ITEMS_TABLE = "CREATE TABLE " + TABLE_ITEMS + "("
+ KEY_NO + " TEXT ," + KEY_NAME + " TEXT,"
+ KEY_ITEMGROUPNAME + " TEXT,"
+ KEY_TYPE + " TEXT,"
+ KEY_EAN + " TEXT,"
+ KEY_DESTRUCTION + " BOOLEAN,"
+ KEY_ARCHIVED + " BOOLEAN" +")";
Log.d(TAG, CREATE_ITEMS_TABLE);
db.execSQL(CREATE_ITEMS_TABLE);
}
public boolean getItemByEAN(String code) {
SQLiteDatabase db = this.getWritableDatabase();
Cursor mCursor = db.rawQuery("SELECT * FROM items WHERE ean =?", new String[]{ Ean });
if (mCursor != null)
{
Log.d(TAGGGG, "Worked");
return true;
/* record exist */
}
else
{
Log.d(TAGGGG, "Did not worked");
return false;
/* record not exist */
}
}
public void checkItemAgainstDatabase(String code)
{
String selectQuery = "SELECT * FROM " + TABLE_ITEMS + " WHERE " + KEY_EAN + "='" + code + "'";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if(cursor == null)
{
Log.d(TAGGGG, "Cursor ist Null, Item not present");
return;
}
if (cursor.getCount() == 0) {
Log.d(TAGGGG, " Item not present");
} else {
Log.d(TAGGGG, " Item present");
}
db.close();
return;
/*
for (Item item : itemList) {
String itemEanToBeMatched = item.getEan();
if (itemEanToBeMatched.equals(code)) {
Toast.makeText(Context, code, Toast.LENGTH_LONG).show();
//ScanService.checkEnteredCode(code, code, content, mContext.getApplicationContext());
}
String itemNoToBeMatched = item.getNo();
if (itemNoToBeMatched.equals(code)) {
Toast.makeText(mContext, code, Toast.LENGTH_LONG).show();
//ScanService.checkEnteredCode(code, code, content, mContext.getApplicationContext());
}
else {
Toast.makeText(mContext, R.string.not_in_database, Toast.LENGTH_LONG).show();
Vibrator vibrator;
vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(3000);
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(mContext.getApplicationContext(), notification);
r.play();
break;
}
} */
}
// Upgrading database
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + TABLE_ITEMS);
// Create tables again
onCreate(db);
}
// code to add the new item
public void addItem(Item items) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NO, ""+items.getNo()); // Item Ean
values.put(KEY_EAN, ""+items.getEan()); // Item Ean
values.put(KEY_NAME, items.getName()); // Item Name
values.put(KEY_TYPE, items.getType());
//values.put(KEY_ITEMGROUPNAM, item.getItemgroupname()); // Item Groupname
values.put(String.valueOf(KEY_ARCHIVED), items.getArchived());
values.put(String.valueOf(KEY_DESTRUCTION), items.getDestruction());
// Inserting Row
db.insert(TABLE_ITEMS, null, values);
//2nd argument is String containing nullColumnHack
db.close(); // Closing database connection
}
// code to get the single contact
public Item getItem(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_ITEMS, new String[] { KEY_NO,
KEY_NAME, KEY_EAN,KEY_TYPE, String.valueOf(KEY_ARCHIVED),
String.valueOf(KEY_DESTRUCTION)}, KEY_NO + "=?",
new String[] { String.valueOf(id) }, null, null, null, null);
if (cursor != null)
cursor.moveToFirst();
Item item = new Item(cursor.getString(0),
cursor.getString(1), cursor.getString(2),
cursor.getString(3),cursor.getString(4),cursor.getExtras().getBoolean(String.valueOf(5)),
Boolean.getBoolean(String.valueOf(6)));
Log.d(TAGGGG, String.valueOf(item));
// return contact
return item;
}
// code to get all contacts in a list view
public List<Item> getAllItems(Context context) {
List<Item> itemList = new List<Item>() {
#Override
public int size() {
return 0;
}
#Override
public boolean isEmpty() {
return false;
}
#Override
public boolean contains(#Nullable #org.jetbrains.annotations.Nullable Object o) {
return false;
}
#NonNull
#NotNull
#Override
public Iterator<Item> iterator() {
return null;
}
#NonNull
#NotNull
#Override
public Object[] toArray() {
return new Object[0];
}
#NonNull
#NotNull
#Override
public <T> T[] toArray(#NonNull #NotNull T[] a) {
return null;
}
#Override
public boolean add(Item item) {
return false;
}
#Override
public boolean remove(#Nullable #org.jetbrains.annotations.Nullable Object o) {
return false;
}
#Override
public boolean containsAll(#NonNull #NotNull Collection<?> c) {
return false;
}
#Override
public boolean addAll(#NonNull #NotNull Collection<? extends Item> c) {
return false;
}
#Override
public boolean addAll(int index, #NonNull #NotNull Collection<? extends Item> c) {
return false;
}
#Override
public boolean removeAll(#NonNull #NotNull Collection<?> c) {
return false;
}
#Override
public boolean retainAll(#NonNull #NotNull Collection<?> c) {
return false;
}
#Override
public void clear() {
}
#Override
public boolean equals(#Nullable #org.jetbrains.annotations.Nullable Object o) {
return false;
}
#Override
public int hashCode() {
return 0;
}
#Override
public Item get(int index) {
return null;
}
#Override
public Item set(int index, Item element) {
return null;
}
#Override
public void add(int index, Item element) {
}
#Override
public Item remove(int index) {
return null;
}
#Override
public int indexOf(#Nullable #org.jetbrains.annotations.Nullable Object o) {
return 0;
}
#Override
public int lastIndexOf(#Nullable #org.jetbrains.annotations.Nullable Object o) {
return 0;
}
#NonNull
#NotNull
#Override
public ListIterator<Item> listIterator() {
return null;
}
#NonNull
#NotNull
#Override
public ListIterator<Item> listIterator(int index) {
return null;
}
#NonNull
#NotNull
#Override
public List<Item> subList(int fromIndex, int toIndex) {
return null;
}
};
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_ITEMS;
Cursor cursor;
try (SQLiteDatabase db = this.getWritableDatabase(GoldbekStorageApp.getInstance())) {
cursor = db.rawQuery(selectQuery, null);
}
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Item item = new Item(No, Name, Itemgroupname, Ean, Type, Destruction, Archived);
item.setNo(cursor.getString(0));
item.setName(cursor.getString(1));
item.setEan(cursor.getString(2));
item.setType(cursor.getString(2));
// Adding items to list
itemList.add(item);
} while (cursor.moveToNext());
}
// return contact list
return itemList;
}
private SQLiteDatabase getWritableDatabase(GoldbekStorageApp context) {
return null;
}
// code to update the single item
public int updateItem(Item item) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NO, item.getNo());
values.put(KEY_NAME, item.getName());
values.put(KEY_EAN, item.getEan());
values.put(KEY_ITEMGROUPNAME, item.getItemgroupname());
values.put(KEY_TYPE, item.getType());
values.put(String.valueOf(KEY_DESTRUCTION), item.getDestruction());
values.put(String.valueOf(KEY_ARCHIVED), item.getArchived());
// updating row
return db.update(TABLE_ITEMS, values, KEY_NO + " = ?",
new String[] { String.valueOf(item.getNo()) });
}
// Deleting single item
public void deleteItem(Item item) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_ITEMS, KEY_NO + " = ?",
new String[] { String.valueOf(item.getNo()) });
db.close();
}
// Getting items Count
public int getItemsCount() {
String itemQuery = "SELECT * FROM " + TABLE_ITEMS;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(itemQuery, null);
cursor.close();
// return count
return cursor.getCount();
}
}
This is my onCreate-Method in the MainActivity, where I am calling the
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Sentry.captureMessage("testing SDK setup");
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getPosts();
//In the following lines, I am initializing the Context of my App and passing it //to the Database Handler and the Method Call of the method in the Database //Handler
*mContext = GoldbekStorageApp.getInstance();
DatabaseHandler db = new DatabaseHandler(mContext);
List<Item> items = db.getAllItems(mContext);*
new LongOperation().execute();
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_palette,
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
This is how I define the context of the App:
import android.app.Application;
import android.content.Context;
public class GoldbekStorageApp extends Application {
private static Context mContext;
public static GoldbekStorageApp mInstance= null;
public String palNo;
public String getPalNo() {
return palNo;
}
public void setPalNo(String palNo) {
this.palNo = palNo;
}
public GoldbekStorageApp(){}
public static synchronized GoldbekStorageApp getInstance() {
if(null == mInstance){
mInstance = new GoldbekStorageApp();
}
return mInstance;
}
}
I have the slight feeling that I am failing to initialize and pass the App Context to the Database Handler, but I don´t have the slightest idea where I am failing and what´s going wrong, as I am not an expert in handling the Context in Android; hence, any hints or help would be appreciated, thanks!
It's not the Context.
Instead you should remove this method:
private SQLiteDatabase getWritableDatabase(GoldbekStorageApp context) {
return null;
}
and just use SQLiteOpenHelper#getWritableDatabase() - it does not return nulls, and you pass a Context to it in the class's constructor.
There are a number of other problems with the code, this is a reason for the NPE when trying to invoke rawQuery() on null.
First, don't create Application object. This is done by Android.
Static way to get 'Context' in Android?
Second, do you really need it? Activity has context. It is Context, in fact. If you need application context, then activity has getApplicationContext() method
You need to change your getInstance method and Ctor in GoldbekStorageApp to:
public static synchronized GoldbekStorageApp getInstance(Context con) {
if(null == mInstance){
mInstance = new GoldbekStorageApp(con);
}
return mInstance;
}
public GoldbekStorageApp(Context con){mContext = con}
Do note, I am not sure why, I just know I've read in several places that holding a static reference to the app Context is not recommended.
The first time you are calling getInstance you need to make sure you are passing a valid context (i.e getApplicationContext() or MainActivity.this or something of the sort) later calls can be made with passing null as parameter since it will be ignored anyway
I'm not very familiar with sql but what I'm am simply trying to do is get two values from an already created database file."Balance" and "VoucherBalance". While I attempt to query the database I get either get back an empty cursor when I add the third and forth params to the query method, or if I leave third and forth params as null I get back a count of 1 which is just the names of the two columns I'm trying to acquire.
Why am I not getting the values when I am specifying which ones I want?
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.widget.Toast;
import java.util.Arrays;
public class DataBaseReader extends SQLiteOpenHelper
{
private static final String TAG = "DataBaseReader";
private static String DB_PATH = "/data/data/PACKAGENAME/databases/DATABAENAME.db";
private static String DB_NAME = "TABLENAME";
private SQLiteDatabase database;
public double balance;
public int voucher;
private final Context myContext;
public String text = "";
public DataBaseReader(Context context)
{
super(context, DB_NAME, null, 1);
this.myContext = context;
}
public void getWireAccountBalance()
{
String[] projection = {DatabaseContract.Database.COLUMN_BALANCE, DatabaseContract.Database.COLUMN_VOUCHER};
String[] selectionArgs = {"Balance", "VoucherBalance"};
String selection = DatabaseContract.Database.COLUMN_BALANCE + "=?" + " AND " + DatabaseContract.Database.COLUMN_VOUCHER + "=?";
SQLiteDatabase db = SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READONLY);
Cursor cursor = db.query(DatabaseContract.Database.TABLE_NAME, projection, selection, selectionArgs, null, null, null);
Log.d(TAG, "Cursor count is " + String.valueOf(cursor.getCount()));
text = Arrays.toString(cursor.getColumnNames());
Toast.makeText(myContext, text, Toast.LENGTH_LONG).show();
if (cursor.moveToFirst())
{
while (!cursor.moveToNext())
{
balance = cursor.getFloat(cursor.getColumnIndex(DatabaseContract.Database.COLUMN_BALANCE));
voucher = cursor.getInt(cursor.getColumnIndex(DatabaseContract.Database.COLUMN_VOUCHER));
}
} else
{
Log.d(TAG, "Cursor count is " + String.valueOf(cursor.getCount()));
}
}
#Override
public synchronized void close()
{
super.close();
if (database != null)
{
close();
}
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase)
{
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1)
{
}
}
Using moveTofirst moves to the first row, you then use moveToNext, which will return false (when there is just the one row), skipping the first row.
I'd suggest using :-
while(cursor.moveToNext()) {
balance = cursor.getFloat(cursor.getColumnIndex(DatabaseContract.Database.COLUMN_BALANCE));
voucher = cursor.getInt(cursor.getColumnIndex(DatabaseContract.Database.COLUMN_VOUCHER));
}
instead of :-
if (cursor.moveToFirst())
{
while (!cursor.moveToNext())
{
balance = cursor.getFloat(cursor.getColumnIndex(DatabaseContract.Database.COLUMN_BALANCE));
voucher = cursor.getInt(cursor.getColumnIndex(DatabaseContract.Database.COLUMN_VOUCHER));
}
} else
{
Log.d(TAG, "Cursor count is " + String.valueOf(cursor.getCount()));
}
I have a dictionary app which stores a word with the meaning, type (English,Chinese,Japanese etc) and favorite status (whether it's a favorite word). On 1 of my menu items, there's this item called sort. When I click on it, I am expecting the words in my main page to be sorted in ascending order but instead I got this error in ListFragment.java: 'java.lang.IllegalStateException: Couldn't read row 0, col 4 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.'. There's nothing wrong with my database.
MainActivity.java
package mapp.com.sg.pocketdictionary;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import mapp.com.sg.pocketdictionary.db.WordsDataSource;
public class MainActivity extends AppCompatActivity implements ListFragment.OnContactSelectedInterface/*,SearchView.OnQueryTextListener*/ {
protected WordsDataSource myDataSource;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myDataSource = new WordsDataSource(this);
this.setTitle("PD");
//The code here decides whether to create the fragment because it can be destroyed by the Android system
//when it reclaims memory
ListFragment savedFragment = (ListFragment) getSupportFragmentManager().findFragmentByTag("WORDS_FRAGMENT");
if (savedFragment == null) {
ListFragment fragment = new ListFragment();
Bundle bundle = new Bundle();
bundle.putBoolean("isFavourite", false);
fragment.setArguments(bundle);
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.placeHolder, fragment, "WORDS_FRAGMENT");
fragmentTransaction.commit();
}
}
#Override
protected void onResume() {
//The code here is required because when I save the data and navigate back
//I need my applicationn logic to refresh the fragement content to reflect the changes
//to avoid the list interface listing the old data.
super.onResume();
ListFragment savedFragment = (ListFragment) getSupportFragmentManager().findFragmentByTag("WORDS_FRAGMENT");
if (savedFragment != null) {
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.detach(savedFragment);
fragmentTransaction.attach(savedFragment);
fragmentTransaction.commit();
}
}
#Override
public void onListContactSelected(int index) {
//When tap on a row display word, this logic here will start collecting the
//id which is tied to the data and navigate the user to another Activity
//and at the same time pass the id over.
// Toast.makeText(this, "PUMPKINNNNNN", Toast.LENGTH_SHORT).show(); for testing if tap detected
Intent intent = new Intent(this, WordsDetailActivity.class);
intent.putExtra("WordId", index);//index variable name may not be suitable here
startActivity(intent);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater myMenuInflater = getMenuInflater();
myMenuInflater.inflate(R.menu.menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
String itemTitle = item.getTitle().toString();
if (item.getItemId() == R.id.favourite) {
// Toast.makeText(this, "GER" , Toast.LENGTH_SHORT).show();//Go to favorite action choice detected
startActivity(new Intent(this, FavouriteActivity.class));
} else if (item.getItemId() == R.id.Add) {
// Toast.makeText(this, "EEEEYEERR", Toast.LENGTH_SHORT).show();//Go to add word action choice detected
startActivity(new Intent(this, AddWordsActivity.class));
} else if (item.getItemId() == R.id.sort) {
Bundle bundle = new Bundle();
bundle.putBoolean("isSortByAsc", true);
ListFragment savedFragment = (ListFragment) getSupportFragmentManager().findFragmentByTag("WORDS_FRAGMENT");
if (savedFragment != null) {
savedFragment.setArguments(bundle);
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.detach(savedFragment);
fragmentTransaction.attach(savedFragment);
fragmentTransaction.commit();
}
}
}
return super.onOptionsItemSelected(item);
}
}
ListFragment.java
package mapp.com.sg.pocketdictionary;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import mapp.com.sg.pocketdictionary.db.WordsDataSource;
public class ListFragment extends Fragment {
protected WordsDataSource mDataSource;
private boolean isFavourite;
private boolean isSortByAsc;
public interface OnContactSelectedInterface {
void onListContactSelected(int index);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
OnContactSelectedInterface listener = (OnContactSelectedInterface) getActivity();
isFavourite = getArguments().getBoolean("isFavourite");
isSortByAsc = getArguments().getBoolean("isSortByAsc");
View view = inflater.inflate(R.layout.fragment_recycler, container,false);
//-------------------
//The rest of the code below is to read all or favorite word data
//and fill up the ArrayList variable.
mDataSource = new WordsDataSource(getContext());
mDataSource.open();
Cursor cursor;
if(isFavourite){
cursor = mDataSource.selectFavouriteWords();
}
else if (isSortByAsc){
cursor = mDataSource.sortWords();
}
else{
cursor = mDataSource.selectAllWords();
}
ArrayList<Words> wordsList = new ArrayList<>();
cursor.moveToFirst();
while(!cursor.isAfterLast()){
boolean tempFav = true;//The database uses 0 and 1 to represent true or false
//Therefore I used this tempFav so that my logic here can store true or false
//during the getString() call
//Error occurs here
if (cursor.getInt(4) != 0) {
tempFav = false;
}
Words tempWords =new Words(cursor.getString(1),cursor.getString(2),cursor.getString(3),tempFav);
tempWords.setId(cursor.getInt(0));
wordsList.add(tempWords);
//Log.d("MY Activity", "onCreateView: "+ tempWords.getId()+" " + tempWords.getWord()+" " +tempWords.getMeaning()+" "+ tempWords.getType()+" "+ cursor.getInt(4));
Log.d("MY Activity", "cursor: "+ cursor.getInt(0)+ " "+cursor.getString(1)+" " + cursor.getString(2)+" "+cursor.getString(3)+" " + cursor.getInt(4));
//Column 0 is id, column 1 is word, column 2 is meaning, column 3 is type, column 4 is favorite status(0 for true and 1 for false)
cursor.moveToNext();
}
mDataSource.close();
//-----------------------
//After the ArrayList variable is filled with data,
//create a listAdapter type object
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
//Creating a ListAdapter object and at the same time pass in a "walkie-talkie" so that it can
//yell out if any related events happening and also the ArrayList of contect information
ListAdapter listAdapter = new ListAdapter(listener, wordsList);
recyclerView.setAdapter(listAdapter);//Start using it
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
return view;
}
}
WordsDataSource.java
package mapp.com.sg.pocketdictionary.db;
//insert update delete
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import mapp.com.sg.pocketdictionary.Words;
public class WordsDataSource {
private SQLiteDatabase mDatabase;
private WordsHelper mWordsHelper;
private Context mContext;
public WordsDataSource(Context context){
mContext = context;
mWordsHelper = new WordsHelper(mContext);
}
//open data base
public void open() throws SQLException {
mDatabase = mWordsHelper.getWritableDatabase();
}
//close
public void close() {
mDatabase.close();
}
//insert
public void insertWords(Words words) {
mDatabase.beginTransaction();
try{
ContentValues values = new ContentValues();
values.put(WordsHelper.COLUMN_WORD, words.getWord());
values.put(WordsHelper.COLUMN_MEANING, words.getMeaning());
values.put(WordsHelper.COLUMN_TYPE, words.getType());
int fav = 0;
if (!words.isFavourite()) {
fav = 1;
}
values.put(WordsHelper.COLUMN_FAVOURITE, fav);
mDatabase.insert(WordsHelper.TABLE_WORDS, null, values);
mDatabase.setTransactionSuccessful();
} finally {
mDatabase.endTransaction();
}
}
//sort
public Cursor sortWords (){
String[] wordname = new String[]{ WordsHelper.COLUMN_WORD };
Cursor cursor= mDatabase.query(WordsHelper.TABLE_WORDS,wordname , null, null, null, null, WordsHelper.COLUMN_WORD+" ASC");
return cursor;
}
//select
public Cursor selectAllWords(){
Cursor cursor = mDatabase.rawQuery("Select * from " + WordsHelper.TABLE_WORDS, null);
return cursor;
}
public Cursor selectOneWord(int Id){
Cursor cursor = mDatabase.rawQuery("Select * from " + WordsHelper.TABLE_WORDS+" where "
+ WordsHelper.COLUMN_ID+" = " + Id, null);
return cursor;
}
public Cursor selectFavouriteWords() {
Cursor cursor = mDatabase.rawQuery("Select * from " + WordsHelper.TABLE_WORDS+" where "
+ WordsHelper.COLUMN_FAVOURITE+" = 0" , null);
return cursor;
}
//update
public boolean updateWords(int id, String word, String meaning,String type, boolean fav){
ContentValues values = new ContentValues();
int success = -1;
values.put(WordsHelper.COLUMN_WORD, word);
values.put(WordsHelper.COLUMN_MEANING, meaning);
values.put(WordsHelper.COLUMN_TYPE, type);
int favDigit = 0;
if(!fav){
favDigit = 1;
}
values.put(WordsHelper.COLUMN_FAVOURITE, favDigit);
success = mDatabase.update(
WordsHelper.TABLE_WORDS,
values,
WordsHelper.COLUMN_ID + " = " + id,
null
);
if(success != -1 && success != 0) {
return true;
} else {
return false;
}
}
public boolean setFavourite (int id, boolean fav) {
ContentValues values = new ContentValues();
int success = -1;
int favDigit = 0;
if(!fav){
favDigit = 1;
}
values.put(WordsHelper.COLUMN_FAVOURITE, favDigit);
success = mDatabase.update(
WordsHelper.TABLE_WORDS,
values,
WordsHelper.COLUMN_ID + " = " + id,
null
);
if(success != -1 && success != 0) {
return true;
} else {
return false;
}
}
public boolean updateWords(int id, String word, String meaning,String type){
ContentValues values = new ContentValues();
int success = -1;
if(word != null ){
values.put(WordsHelper.COLUMN_WORD, word);
}
if(meaning != null ){
values.put(WordsHelper.COLUMN_MEANING, meaning);
}
if(type != null ){
values.put(WordsHelper.COLUMN_TYPE, type);
}
success = mDatabase.update(
WordsHelper.TABLE_WORDS,
values,
WordsHelper.COLUMN_ID + " = " + id,
null
);
if(success != -1 && success != 0) {
return true;
} else {
return false;
}
}
//delete
public boolean deleteWords(int id) {
int success = -1;
success = mDatabase.delete(
WordsHelper.TABLE_WORDS,
WordsHelper.COLUMN_ID + " = " + id,
null
);
if(success != -1 && success !=0) {
return true;
} else {
return false;
}
}
public boolean deleteWords() {
int success = -1;
success = mDatabase.delete(
WordsHelper.TABLE_WORDS,
null,
null
);
if(success != -1 ) {
return true;
} else {
return false;
}
}
}
WordsHelper.java
package mapp.com.sg.pocketdictionary.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
//Helps make all code in WordsDataSource neater, readable and maintainable.
public class WordsHelper extends SQLiteOpenHelper {
public static final String TABLE_WORDS ="WORDS";
public static final String COLUMN_ID = "_ID";
public static final String COLUMN_WORD = "WORD";
public static final String COLUMN_MEANING = "MEANING";
public static final String COLUMN_TYPE = "TYPE";
public static final String COLUMN_FAVOURITE = "FAVOURITE";
private static final String DB_NAME = "words.db";
private static final int DB_VER = 1;
private static final String DB_CREATE = "CREATE TABLE "+TABLE_WORDS+
" (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT" +
", " + COLUMN_WORD + " TEXT, " +
COLUMN_MEANING + " TEXT,"+
COLUMN_TYPE + " TEXT,"+ COLUMN_FAVOURITE +" INTEGER)";
public WordsHelper(Context context){
super(context, DB_NAME, null, DB_VER);
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(DB_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
As you are trying to read the data, you need to get ReadableDatabase but your open always gets WritableDatabaseas in
Main code
mDataSource = new WordsDataSource(getContext());
mDataSource.openforRead(); // <-- will return readable database
Cursor cursor;
if(isFavourite){
cursor = mDataSource.selectFavouriteWords();
}
else if (isSortByAsc){
cursor = mDataSource.sortWords();
}
else{
cursor = mDataSource.selectAllWords();
}
ArrayList<Words> wordsList = new ArrayList<>();
cursor.moveToFirst();
while(!cursor.isAfterLast()){
boolean tempFav = true;//The database uses 0 and 1 to represent true or false
//Therefore I used this tempFav so that my logic here can store true or false
//during the getString() call
//Error occurs here
if (cursor.getInt(cursor.getColumnIndex(WordsHelper.COLUMN_FAVOURITE)) != 0) {
tempFav = false;
}
Words tempWords =new Words(cursor.getString(1),cursor.getString(2),cursor.getString(3),tempFav);
tempWords.setId(cursor.getInt(0));
wordsList.add(tempWords);
//Log.d("MY Activity", "onCreateView: "+ tempWords.getId()+" " + tempWords.getWord()+" " +tempWords.getMeaning()+" "+ tempWords.getType()+" "+ cursor.getInt(4));
Log.d("MY Activity", "cursor: "+ cursor.getInt(0)+ " "+cursor.getString(1)+" " + cursor.getString(2)+" "+cursor.getString(3)+" " + cursor.getInt(4));
//Column 0 is id, column 1 is word, column 2 is meaning, column 3 is type, column 4 is favorite status(0 for true and 1 for false)
cursor.moveToNext();
}
Add below method to WordsDataSource
public void openForRead() throws SQLException {
mDatabase = mWordsHelper.getReadableDatabase();
}
If you trying to access
int value=cursor.getInt(4)
and it have null value or out of rang of integer it will throw exception.
for such case try below code
if ( !cursor.isNull( columnIndex ) )
{
value = cursor.getInt( columnIndex );
}
else
{
value=0; //default value
}
For debugging purpose first check the value of cursor using below code
Log.d("CursorObject", DatabaseUtils.dumpCursorToString(cursor))
I'm trying to code a simple randomizer app. I had the randomizer button working, but then I changed some code (which I thought was irrelevant to the randomizer button) and it started crashing and getting the "CursorIndexOutOfBoundsException Index 0 requested, with a size of 0" error. I couldn't find any fixes to this that apply to my code. Can anyone help me fix this?
Here is my main class with the button:
package com.example.randomgamechooser;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
public class MainScreen extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_screen);
}
public void chooseGame (View view) {
GameList dbUtil = new GameList(this);
dbUtil.open();
String string = dbUtil.getRandomEntry();
//TextView textView = new TextView(this);
TextView textView = (TextView) findViewById(R.id.chosenbox);
textView.setTextSize(40);
textView.setText(string);
//setContentView (textView);
dbUtil.close();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_screen, menu);
return true;
}
//starts the Game Selection activity
public void openGames (View view) {
Intent intent = new Intent(this, GameSelction.class);
startActivity(intent);
}
}
Here is the GameList class:
package com.example.randomgamechooser;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.Random;
public class GameList {
private static final String TAG = "GameList";
//database name
private static final String DATABASE_NAME = "game_list";
//database version
private static final int DATABASE_VERSION = 1;
//table name
private static final String DATABASE_TABLE = "game_list";
//table columns
public static final String KEY_NAME = "name";
public static final String KEY_GENRE = "genre";
public static final String KEY_ROWID = "_id";
//database creation sql statement
private static final String CREATE_GAME_TABLE =
"create table " + DATABASE_TABLE + " (" + KEY_ROWID + " integer primary key autoincrement, "
+ KEY_NAME +" text not null, " + KEY_GENRE + " text not null);";
//Context
private final Context mCtx;
private DatabaseHelper mDbHelper;
private static SQLiteDatabase mDb;
//Inner private class. Database Helper class for creating and updating database.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// onCreate method is called for the 1st time when database doesn't exists.
#Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "Creating DataBase: " + CREATE_GAME_TABLE);
db.execSQL(CREATE_GAME_TABLE);
}
//onUpgrade method is called when database version changes.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion);
}
}
//Constructor - takes the context to allow the database to be opened/created
//#param ctx the Context within which to work
public GameList(Context ctx) {
this.mCtx = ctx;
}
//This method is used for creating/opening connection
//#return instance of GameList
//#throws SQLException
public GameList open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
//This method is used for closing the connection.
public void close() {
mDbHelper.close();
}
//This method is used to create/insert new game.
//#param name
// #param genre
// #return long
public long createGame(String name, String genre) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_GENRE, genre);
return mDb.insert(DATABASE_TABLE, null, initialValues);
}
// This method will delete game.
// #param rowId
// #return boolean
public static boolean deleteGame(long rowId) {
return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
}
// This method will return Cursor holding all the games.
// #return Cursor
public Cursor fetchAllGames() {
return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,
KEY_GENRE}, null, null, null, null, null);
}
// This method will return Cursor holding the specific game.
// #param id
// #return Cursor
// #throws SQLException
public Cursor fetchGame(long id) throws SQLException {
Cursor mCursor =
mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_NAME, KEY_GENRE}, KEY_ROWID + "=" + id, null,
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
public int getAllEntries()
{
Cursor cursor = mDb.rawQuery(
"SELECT COUNT(name) FROM game_list", null);
if(cursor.moveToFirst()) {
return cursor.getInt(0);
}
return cursor.getInt(0);
}
public String getRandomEntry()
{
//id = getAllEntries();
Random random = new Random();
int rand = random.nextInt(getAllEntries());
if(rand == 0)
++rand;
Cursor cursor = mDb.rawQuery(
"SELECT name FROM game_list WHERE _id = " + rand, null);
if(cursor.moveToFirst()) {
return cursor.getString(0);
}
return cursor.getString(0);
}
// This method will update game.
// #param id
// #param name
// #param standard
// #return boolean
public boolean updateGame(int id, String name, String standard) {
ContentValues args = new ContentValues();
args.put(KEY_NAME, name);
args.put(KEY_GENRE, standard);
return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + id, null) > 0;
}
}
And here is the cause part of the error log:
08-01 13:03:38.325: E/AndroidRuntime(278): Caused by: android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
08-01 13:03:38.325: E/AndroidRuntime(278): at android.database.AbstractCursor.checkPosition(AbstractCursor.java:580)
08-01 13:03:38.325: E/AndroidRuntime(278): at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:214)
08-01 13:03:38.325: E/AndroidRuntime(278): at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:41)
08-01 13:03:38.325: E/AndroidRuntime(278): at com.example.randomgamechooser.GameList.getRandomEntry(GameList.java:153)
EDIT: Here is the ListView class:
public class GameSelction extends Activity
{
GameList dbUtil = new GameList(this);
private SimpleCursorAdapter dataAdapter;
//#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_selction);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
getActionBar() .setDisplayHomeAsUpEnabled(true);
}
displayListView();
}
private void displayListView() {
dbUtil.open();
Cursor cursor = dbUtil.fetchAllGames();
// The desired columns to be bound
String[] columns = new String[] {
GameList.KEY_NAME,
GameList.KEY_GENRE,
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.name,
R.id.genre,
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this, R.layout.game_info,
cursor,
columns,
to,
0);
ListView listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long rowId) {
// Get the cursor, positioned to the corresponding row in the result set
//Cursor cursor = (Cursor) listView.getItemAtPosition(position);
GameList.deleteGame(rowId);
}
});
}
/**
* Set up the {#link android.app.ActionBar}, if the API is available.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupActionBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
//opens the AddGame activity
public void openAddgame (View view) {
Intent intent = new Intent(this, AddGame.class);
startActivity(intent);
}
public void buttonBackMain (View view) {
Intent intent = new Intent(this, MainScreen.class);
startActivity(intent);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.game_selction, menu);
return true;
}
}
The problem is in this section of code:
public String getRandomEntry()
{
//...
Cursor cursor = mDb.rawQuery(
"SELECT name FROM game_list WHERE _id = " + rand, null);
if(cursor.moveToFirst()) {
return cursor.getString(0);
}
return cursor.getString(0);
}
What you ended up doing was saying return cursor.getString(0); whether or not there were results in the cursor. So remove the second occurrence, and it should work.
EDIT:
After scanning your code, it seems that the only place you use this method is to fill a TextView. In that case you can use this as a chance to communicate a visual error message to yourself or your users, or do anything else with it that you want. So I would suggest using something to the effect of
public String getRandomEntry()
{
//EDIT: This will make your random generator less biased toward 1.
Random random = new Random();
int rand = random.nextInt(getAllEntries()) + 1;
/* Assuming your _id starts at 1 and auto-increments, this will
* start the random digits at 1 and go as high as your highest _id */
Cursor cursor = mDb.rawQuery(
"SELECT name FROM game_list WHERE _id = " + rand, null);
if(cursor.moveToFirst()) {
return cursor.getString(0);
}
return "There were no games in the database to choose from.";
}
EDIT:
Try using this. Notice that this code uses mDb.query(), which you used elsewhere. I'm not sure why rawQuery() would refuse to work, but maybe this will do it.
public String getRandomEntry()
{
Random random = new Random();
int rand = random.nextInt(getAllEntries()) + 1;
Cursor cursor = mDb.query(true, DATABASE_TABLE, new String[] {KEY_NAME},
KEY_ROWID + "=" + rand, null, null, null, null, null);
if(cursor.moveToFirst()) {
return cursor.getString(0);
}
return "There were no games in the database to choose from.";
}
Based on the initial look, the SQLiteDatabase object represented by mDB appears to be empty, as that's what is throwing the error.
What the error is saying is that your code is requesting the item at index 0 (basically, the first item), but the size of your index is 0 (basically, there are no items in the index).
Somewhere along the line, your database object is either emptied or not populated. To test this, run your fetchAllGames method and verify the contents of the index.
Didn't read the whole code, but I guess the problem is that, you are trying to get first element, from empty database.
Just check if the size of cursor greater than 0, before getting element.
if (cursor.getColumnCount() > 0)
return cursor.getString(0);
else return "no items";
I'll start by saying i've researched this specific solution to death and am aware of the solutions "this has been solved HERE" and HERE, but I am unable to get these working. Other info: I'm using the compatibility library and also actionbarsherlock, which both have been working great, no issues there i dont think.
I haven an SQLite database that i want to fill my entries into expandable list grouped by date. I know that my database is correctly populated and here are the columns I have verified to contain proper data:
ID (1, 2, 3, 4, etc)
TITLE (some text)
URL (url as string)
DATE (ddMMyyyy)
where the date column data is like this: 22Dec2012 or 23Dec2012 etc.
There are many entries per date, i want to group those as children with the date as group. I see the database is filled with the correct info. I have a few problems, the main is that my getChildrenCursor never gets called, only the constructor gets called. Second is that per the linked examples, I cannot get the getSherlockActivity context into my adapter (nor does getActivity() work either, just for testing purposes). I'll explain the error with logcat below.
My Fragment
public class MyFragment extends SherlockFragment implements LoaderManager.LoaderCallbacks, OnItemClickListener {
public static MyFragment newInstance(String symbol) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putString(Consts.INFO, info);
f.setArguments(args);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myView = inflater.inflate(R.layout.fragment_layout3, container, false);
mListView = (ExpandableListView)myView.findViewById(R.id.expandableListView);
mListView.setOnItemClickListener(this);
return myView;
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onResume() {
super.onResume();
refresh();
}
public void refresh() {
Log.v(TAG, "refresh");
populateExpandableList();
loader = getLoaderManager().getLoader(-1);
if (loader != null && !loader.isReset()) {
getLoaderManager().restartLoader(-1, null, this);
} else {
getLoaderManager().initLoader(-1, null, this);
}
if (getLoaderManager().getLoader(0x9999) == null) {
getLoaderManager().initLoader(0x9999, null, this);
//getLoaderManager().initLoader(-1, null, this);
}
else {
getLoaderManager().restartLoader(0x9999, null, this);
//getLoaderManager().restartLoader(-1, null, this);
}
getLoaderManager().getLoader(0x9999).forceLoad();
//getLoaderManager().getLoader(1).forceLoad();
}
private void populateExpandableList() {
// Set up our adapter
Log.v(TAG, "Endtering setup adapter");
mDbHelper = new DBHelper(getSherlockActivity(), DBConstants.DATABASE_NAME, null, DBConstants.DATABASE_VERSION);
mAdapter = new MyExpandableAdapter(getSherlockActivity(), this,
R.layout.news_list_group,
R.layout.news_list_child,
new String[] { DBConstants.DATE_GROUP_NAME }, // Name for group layouts
new int[] { R.id.group_title },
new String[] { DBConstants.TITLE_NAME, DBConstants.URL_NAME }, // Name for child layouts
new int[] { R.id.item_title, R.id.item_value });
mListView.setAdapter(mAdapter);
}
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
}
#Override
public Loader onCreateLoader(int loaderId, Bundle args) {
topics = new ArrayList<String>();
if (loaderId == 0x9999) {
return new MyLoader(getSherlockActivity(), mHandler, topics.toArray(new String[topics.size()]));
}
if (loaderId != -1 && loaderId != 0x9999) {
Log.v(TAG, "CreateLoader child cursor: loader ID: "+loaderId);
// child cursor
mCursorLoader = new SQLiteCursorLoader(getSherlockActivity(), mDbHelper, "SELECT _ID, "+DBConstants.TITLE+", " +DBConstants.URL + ", " + DBConstants.TIME + ", " + DBConstants.STATUS + " FROM "+DBConstants.TABLE+" WHERE "+ DBConstants.KEY_ID+"="+String.valueOf(loaderId)+" ORDER BY "+DBConstants.TIME+" DESC", null);
return mCursorLoader;
}
else if (loaderId != 0x9999) {
Log.v(TAG, "CreateLoader group cursor: loader ID: "+loaderId);
// group cursor
mCursorLoader = new SQLiteCursorLoader(getSherlockActivity(), mDbHelper, "SELECT date, "+DBConstants.TITLE+", "+DBConstants.URL+" FROM "+DBConstants.TABLE, null);
return mCursorLoader;
}
return null;
}
#Override
public void onLoadFinished(Loader loader, Object result) {
Log.v(TAG, "onLoadFinished");
if(result != null /*&& result.length > 0*/) {
if (loader.getId() == 0x9999) {
getLoaderManager().restartLoader(-1, null, this);
getLoaderManager().getLoader(-1).forceLoad();
}
if (loader.getId() != -1 && loader.getId() != 0x9999) {
Log.v(TAG, "LoadFinished second loader: loaderID: "+loader.getId());
if (!((Cursor) result).isClosed()) {
Log.v(TAG, "data.getCount() " + ((Cursor) result).getCount());
HashMap<Integer,Integer> groupMap = mAdapter.getGroupMap();
try {
int groupPos = groupMap.get(loader.getId());
Log.v(TAG, "onLoadFinished() for groupPos " + groupPos);
mAdapter.setChildrenCursor(groupPos, (Cursor) result);
} catch (NullPointerException e) {
Log.w("DEBUG","Adapter expired, try again on the next query: "
+ e.getMessage());
}
}
else {
mAdapter.setGroupCursor((Cursor) result);
}
}
else {
}
}
}
#Override
public void onLoaderReset(Loader loader) {
int id = loader.getId();
Log.v(TAG, "onLoaderReset() for loader_id " + id);
if (id != -1) {
// child cursor
try {
mAdapter.setChildrenCursor(id, null);
} catch (NullPointerException e) {
Log.w("TAG", "Adapter expired, try again on the next query: "
+ e.getMessage());
}
} else {
mAdapter.setGroupCursor(null);
}
}
public class MyExpandableAdapter extends SimpleCursorTreeAdapter {
private final String TAG = getClass().getSimpleName().toString();
private MyFragment mFragment;
protected HashMap<Integer, Integer> mGroupMap = null;
public NewsFeedExpandableAdapter(Context context, MyFragment mf, int groupLayout, int childLayout, String[] groupFrom,
int[] groupTo, String[] childrenFrom, int[] childrenTo) {
super(context, null, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo);
mFragment = mf;
mGroupMap = new HashMap<Integer, Integer>();
Log.v(TAG, "Adapter constructor");
}
#Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that group
Log.v(TAG, "getChildrenCursor");
int groupPos = groupCursor.getPosition();
int groupId = groupCursor.getInt(groupCursor.getColumnIndex(DBConstants.TIME_GROUP_NAME));
Log.v(TAG, "getChildrenCursor() for groupPos " + groupPos);
Log.v(TAG, "getChildrenCursor() for groupId " + groupId);
mGroupMap.put(groupId, groupPos);
Loader loader = mFragment.getLoaderManager().getLoader(groupId);
if ( loader != null && !loader.isReset() ) {
mFragment.getLoaderManager().restartLoader(groupId, null, mFragment);
} else {
mFragment.getLoaderManager().initLoader(groupId, null, mFragment);
}
return null;
}
//Accessor method
public HashMap<Integer, Integer> getGroupMap() {
return mGroupMap;
}
}
}
Now in my adapter above, you see i'm using :
Loader loader = mFragment.getLoaderManager().getLoader(groupId);
if ( loader != null && !loader.isReset() ) {
mFragment.getLoaderManager().restartLoader(groupId, null, mFragment);
} else {
mFragment.getLoaderManager().initLoader(groupId, null, mFragment);
}
rather than this:
Loader loader = getSherlockActivity().getLoaderManager().getLoader(groupId);
if ( loader != null && !loader.isReset() ) {
getSherlockActivity().getLoaderManager().restartLoader(groupId, null, mFragment);
} else {
getSherlockActivity().getLoaderManager().initLoader(groupId, null, mFragment);
}
Because when i use getActivity or getSherlockActivity, the code gives an error on the first line above saying:
"Type mismatch, cannot convert from Loader<Object> to Loader"
and on the third and fifth line errors:
"The method restartLoader(int, Bundle, LoaderManager.LoaderCallbacks<D>) in the type LoaderManager is not applicable for the arguments (int, null, MyFragment)"
Does anyone possibly know why i cannot use getActivity here? It's driving me completely bonkers. So i attempt to use mFragment.getLoaderManager instead using the fragment i passed in to the adapter, but i dont know if this is even correct.
I placed breakpoints in the code and it shows that the adapter constructor is called, and that's it, getChildrenCursor never gets called ever. in the fragment breakpoints, it reaches "Create Group Loader id=-1" and thats it. in onLoadFinished only the first "onLoadFinished" tag get's shown in the log. It never makes it into the IF statements within onLoadFinished. When i check that the loadFinished result is not null in logcat, it shows as a cursor object being passed in to onLoadFinished, but something just isnt working right.
prior to trying expandable list, i was using these identical SQLiteCursorLoader's setup, just retrieving all columns, and they worked fine. I am absolutely admitting that maybe both my SELECT statements are not correct. but i've tried a ton of different select queries, and everytime i get same result, nothing. Can anyone PLEASE help me I'm desperate and going bonkers. Thanks.