The data fetched from my ContentProvider is not loaded in the screen. I don't know where is the problem especially that data variable passed in onLoadFinished is not null and the query method from the ContentProvider doesn't return null too. Here is the implementation of the Loader callbacks.
The data.getCount() in onLoadFinished returns 0.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
getLoaderManager().initLoader(MOVIE_LOADER, null, this);
super.onActivityCreated(savedInstanceState);
}
#Override
public android.support.v4.content.Loader<Cursor> onCreateLoader(int id, Bundle args) {
android.support.v4.content.Loader cursorLoader;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
pref = preferences.getString("sort_method", "0");
String a;
if (pref.equals("0")) {
cursorLoader = new android.support.v4.content.CursorLoader(getActivity(), MovieContract.MostPopMovieEntry.CONTENT_URI,
new String[]{MovieContract.MostPopMovieEntry._ID, MovieContract.MostPopMovieEntry.COLUMN_POSTER_PATH},
null,
null,
null);
} else {
cursorLoader = new android.support.v4.content.CursorLoader(getActivity(), MovieContract.TopRatedMovieEntry.CONTENT_URI,
new String[]{MovieContract.TopRatedMovieEntry._ID, MovieContract.TopRatedMovieEntry.COLUMN_POSTER_PATH},
null,
null,
null);
}
if (cursorLoader != null) {
a = "cursorLoader initiated in onCreateLoader is not null";
} else {
a = "cursorLoader initiated in onCreateLoader is null";
}
Log.d(LOG_TAG, a);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.d(LOG_TAG, data.getCount() + " rows loaded");
if (data != null) {
Log.d(LOG_TAG, "Data received onLoadfinished is no null");
}else{
Log.d(LOG_TAG, "Data received onLoadfinished is null");
}
movieAdapter.swapCursor(data);
}
The query doesn't return null, the loader initiated in onCreateLoader doesn't return null, neither the data received in onLoadFinished returns null.
Any idea what might be the problem that causes the data not be loaded to the Fragment?
I had the same problem when the adapter belonged to a ViewPager
This code solved my problem
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// to be restored after reload
mInitialScrollPosition = mViewPager.getCurrentItem();
// do change the data
mAdapter.swapCursor(data);
// restore position is invalid
if (mInitialScrollPosition >= mAdapter.getCount()) mInitialScrollPosition = -1;
// do change the data
mAdapter.notifyDataSetChanged();
mViewPager.setAdapter(mAdapter);
if (mInitialScrollPosition >= 0) mViewPager.setCurrentItem(mInitialScrollPosition);
}
With an adapter belonging to a GridView only
mAdapter.notifyDataSetChanged();
was necessary.
Related
in onLoadFinished() i use myadapter.swapCuesor(cursor) and i wonder if it calls newView in the adapter afterwards? its not written in the api of android's adapter so im asking it here. if not so how the adapter update itself?
When you are using a CursorLoader, the Cursor is managed for you. The only thing you have to do is implement the following three methods:
// Called when a new Loader needs to be created
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(this, ContactsContract.Data.CONTENT_URI,
PROJECTION, SELECTION, null, null);
}
// Called when a previously created loader has finished loading
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
// Called when a previously created loader is reset, making the data unavailable
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
You don’t have to open and close the Cursor yourself, the loader will do this for you. This is the most important reason why you have to use swapCursor, it doesn’t close the Cursor when you swap it with another Cursor.
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
Cursor oldCursor = mCursor;
if (oldCursor != null) {
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (newCursor != null) {
if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
} else {
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
return oldCursor;
}
ChangeCursor on the other hand, first swaps the current Cursor with the new one and then closes it for you. If you use this method with your CursorLoader, your app may crash sometimes.
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
I have two tables with names disease_table and sysmptoms_table. I retrieved the data from disease_table from the DB and displayed on the listview and when the listitem is clicked, I have to select and display disease category symptoms accordingly and I did that successfully but my code has redundancy, I had to write two methods in the datahelper class to retrieve the symptoms as per the disease in another listview. and I am retrieving the symptom data in list view with the query with the condition of WHERE "disease_id=1" with foreign key reference
the code for the methods is as follows,
//getting pain symptom names in a arraylist and then display in listview
//this.setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1,symptompain));
public List<String> getAllSymptomPain() {
List<String> symptompain = null;
cr = db.query(SYMPTOM_TABLE_NAME, new String[] {"symname"}, "diseaseid=1", null, null, null, null);
if(null != cr){
symptompain = new ArrayList<String>();
if (cr.moveToFirst()) {
do {
symptompain.add(cr.getString(0));
} while (cr.moveToNext());
}
if (cr != null && !cr.isClosed()) {
cr.close();
}
}
return symptompain;
}
//getting colorchange symptom names in a arraylist and then display in listview
//this.setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,symptomcolorchange));
public List<String> getAllSymptomColorChange() {
List<String> symptomcolorchange = null;
cr = db.query(SYMPTOM_TABLE_NAME, new String[] {"symname"}, "diseaseid=2", null, null, null, null);
if(null != cr){
symptomcolorchange = new ArrayList<String>();
if (cr.moveToFirst()) {
do {
symptomcolorchange.add(cr.getString(0));
} while (cr.moveToNext());
}
if (cr != null && !cr.isClosed()) {
cr.close();
}
}
return symptomcolorchange;
}
How can I write these two in a single method and then call it in class which extends listactivity under onListItemclick method?
And my OnListItemClick() method is as follows :
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
String item=(String)getListAdapter().getItem(position);
if(item.equals("Pain in Teeth")){
// passing the method here
}
else if(item.equals("Pain in Gums")){
// passing the method here
}
else if(item.equals("Pain in Mucosa")){
// passing the method here
}
else if(item.equals("Pain in TMJoint")){
// passing the method here
}
else if(item.equals("Non-Specific Pain")){
// passing the method here
}
}
Try this:
public List<String> getSymptomsByDiseaseId(long diseaseId) {
List<String> symptomsList = new ArrayList<String>();
String selection = "diseaseid=?";
String[] selectionArgs = { String.valueOf(diseaseId) };
Cursor cursor = db.query(false, SYMPTOM_TABLE_NAME, null, selection, selectionArgs, null, null, null, null);
if (cursor.moveToFirst()) {
do {
symptomsList.add(cursor.getString(0));
} while (cursor.moveToNext());
}
cursor.close();
return symptomsList;
}
I'm getting object data returned from my code here, but I don't see how I can be doing that.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage);
Log.d(TAG, "onCreate");
datasource = new DataSource(this);
datasource.open();
List<Encouragement> values = datasource.getAllEncouragements();
Log.d(TAG, "values");
ArrayAdapter<Encouragement> adapter = new ArrayAdapter<Encouragement>(this,
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
Log.d(TAG, "end of onCreate");
}
and
public List<Encouragement> getAllEncouragements() {
List<Encouragement> encouragements = new ArrayList<Encouragement>();
Cursor cursor = database.query(DatabaseHelper.TABLE_ENCOURAGEMENTS,
allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Encouragement encouragement = cursorToEncouragement(cursor);
encouragements.add(encouragement);
cursor.moveToNext();
}
cursor.close();
return encouragements;
}
is producing com.example.myActivity.myObject#34563475 in the list item.
The listview is calling toString() to represent myObject. You have not overridden the toString() method hence the default Object.toString() method is called.
#Override
public String toString(){
return this.name; // whatever you need to represent this instance of myObject.
}
I want to show the items queried from database in the listview with SimpleCursorAdapter.
For example, there may be 20,000 items in the database. I want to just load 100 items(_id : 1-100) queried instead of load all items, when scrolling in the end of listview, load another 100 items(_id : 101-200) queried, how to achieve it? Any suggestion is welcome, thanks.
Relative codes are as follows:
protected void onCreate(Bundle savedInstanceState) {
mCursor = managedQuery(CONTENT_URI, PROJECTION, null, null, "_id DESC");
mAdapter = new SimpleCursorAdapter(this,R.layout.list_content, mCursor, keys, values);
setListAdapter(mAdapter);
}
In my defined listview, i want to load more items by query database.
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
int lastItem = firstVisibleItem + visibleItemCount - 1;
if (mListAdapter != null) {
if ((lastItem == mListAdapter.getCount()-1) && (mRefreshState != REFRESHING)) {
mFooterView.setVisibility(View.VISIBLE);
mRefreshState = REFRESHING;
new Handler().postDelayed(new Runnable() {
public void run() {
//execute the task , i want to load more items by query database
RefreshListView(LOADING_STORED_INFO);
}
}, DEFAULT_DELAY_TIMER);
}
}
}
In the AsyncTask loading data, i do the query operation.
protected Integer doInBackground(Integer... params)
{
Uri uri = ContentUris.withAppendedId(CONTENT_URI, mCursor.getInt(0)-1);
cursor = managedQuery(uri, PROJECTION, null, null, "_id DESC");
return (0 == params[0]) ? 1 : 0;
}
#Override
protected void onPostExecute(Integer result)
{
mAdapter.changeCursor(cursor);//is this OK?
mAdapter.notifyDataSetChanged();
/*
if (1 == result)
{
mListView.setSelection(1);
}
else
{
mListView.setSelection(mCount-1);
}*/
// Call onRefreshComplete when the list has been refreshed.
mListView.onRefreshComplete(result);
super.onPostExecute(result);
}
Use the LIMIT statement in the SQL query in this way:
SELECT your_column FROM your_table ORDER BY your_order LIMIT limit_skip, limit_count
Then you can use a OnScrollListener to retrieve the index of the first visible cell and the number of visible cells so you can increment limit_skip and limit_count coherently.
Instead of the generic AsyncTask use a CursorLoader and implement LoaderManager.LoaderCallbacks<Cursor> as follow:
public Loader<Cursor> onCreateLoader(int id, Bundle args){
String orderBy = "_id DESC"
if(args != null){
orderBy += " LIMIT " + args.getInt("LIMIT_SKIP") + "," + args.getInt("LIMIT_COUNT");
}
return new CursorLoader(this /*context*/, CONTENT_URI, PROJECTION, null, null, orderBy);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data){
listAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader){
listAdapter.swapCursor(null);
}
Then, in onCreate(), pass null as cursor to new SimpleCursorAdapter() and create the CursorLoader in this way:
getLoaderManager().initLoader(0, null, this /*LoaderCallbacks<Cursor>*/);
Then, in onScroll(), reset everytime the loader in this way:
Bundle args = new Bundle();
args.putInt("LIMIT_SKIP", limit_skip_value);
args.putInt("LIMIT_COUNT", limit_count_value);
getLoaderManager().restartLoader(0, args, this /*LoaderCallbacks<Cursor>*/);
I've implemented a custom Adapter for a ExpandableListView which I extended from the CursorTreeAdapter class. Everything is working as expected.
But I'm wondering if there's pattern or some kind of best practice on how to asynchronously query the database in the getChildrenCursor() method of the adapter class. At the moment I'm passing my SQLiteOpenHelper class to the constructor of my adapter and use it in getChildrenCursor() to query the database synchronously on the UI thread.
You could also use a CursorLoader instead of subclassing AsyncTask to asynchronously query a provider.
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id != -1) {
// child cursor
return new CursorLoader(getActivity(), childrenUri,
CHILDREN_PROJECTION, selection, selectionArgs, sortOrder);
} else {
// group cursor
return new CursorLoader(getActivity(), groupsUri,
GROUPS_PROJECTION, selection, null, sortOrder);
}
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
int id = loader.getId();
if (id != -1) {
// child cursor
if (!data.isClosed()) {
try {
mAdapter.setChildrenCursor(id, data);
} catch (NullPointerException e) {
Log.w("TAG",
"Adapter expired, try again on the next query: "
+ e.getMessage());
}
}
} else {
// group cursor
mAdapter.setGroupCursor(data);
}
}
public void onLoaderReset(Loader<Cursor> loader) {
int id = loader.getId();
if (id != -1) {
// child cursor
mAdapter.setChildrenCursor(id, null);
} else {
// group cursor
mAdapter.setGroupCursor(null);
}
}
And in your adapter class you can override the getChildrenCursor() method like this:
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that group
int id = groupCursor.getInt(groupCursor
.getColumnIndex(ContactsContract.Groups._ID));
mActivity.getLoaderManager().initLoader(id, null,mFragment);
return null;
}
getChildrenCursor says:
If you want to asynchronously query a
provider to prevent blocking the UI,
it is possible to return null and at a
later time call setChildrenCursor(int,
Cursor).
So, in getChildrenCursor(), start an AsyncTask and return null. In the onPostExecute() method call setChildrenCursor()