I am developping an Android app which loads reddits and put it in a db, I use an asynchron cursor loader in my fragment SubredditsFragment.class. This fragment contains an adapter, which has a cursor loader. When I stop or reset the loader, the loader needs to be swapped on my adapter.
public class SubRedditsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private List<SubRedditData> subRedditDataList;
private IntentFilter filter;
public static final String TAG = SubRedditsFragment.class.getName();
private SubredditAdapter adapter;
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = null;
String where = null;
String[] whereArgs = null;
String sortOrder = null;
Uri queryUri = RedditContentProvider.CONTENT_URI;
return new CursorLoader(getActivity(), queryUri, projection, where, whereArgs, sortOrder);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.i(TAG,"Added broadcastreceiver");
getActivity().registerReceiver(receiver,filter);
adapter = new SubredditAdapter(getActivity().getApplicationContext(),subRedditDataList);
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
getLoaderManager().destroyLoader(loader.getId());
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
The problem is that I can't use the method adapter.swapCursor(), it's unknown for Android. I get the error message Cannot resolve method 'swapCursor(loader)'
add this code in your Adapter, mCursor is the global cursor variable
public void swapCursor(Cursor newCursor) {
// Always close the previous mCursor first
if (mCursor != null) mCursor.close();
mCursor = newCursor;
if (newCursor != null) {
// Force the RecyclerView to refresh
this.notifyDataSetChanged();
}
}
Related
TASK: I want to display a list of ListView and when you select an item to display the list of dialogue and display it in a list of another element (parent and child elements)
static class GroupCursorLoader extends CursorLoader {
DB db;
public GroupCursorLoader(Context context, DB db) {
super(context);
this.db = db;
}
#Override
public Cursor loadInBackground() {
Cursor cursor = db.getGroupAll();
return cursor;
}
}
static class DetailCursorLoader extends CursorLoader{
DB db;
public DetailCursorLoader(Context context, DB db) {
super(context);
this.db = db;
}
#Override
public Cursor loadInBackground() {
Cursor cursor;
cursor = db.getDetailAll();
return cursor;
}
}
Now, The question is, how to handle the two Cursor?, i.e . written in the following methods
public Loader onCreateLoader(int id, Bundle args)
public void onLoadFinished(Loader loader, Cursor data)
public void onLoaderReset(Loader loader)
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return return new GroupCursorLoader(this, mDB);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
scAdapterForGroup.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.d(LOG, "onLoadReset");
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if(id == GROUP_LOADER_ID) {
return new GroupCursorLoader(this, mDB);
} else {
return new DetailCursorLoader(this, mDB);
}
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if(loader instanceof GroupCursorLoader) {
scAdapterForGroup.swapCursor(data);
} else {
scAdapterForDetails.swapCursor(data);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.d(LOG, "onLoadReset");
}
Answer question at ru.stackoverflow.com
I have a listFragment and when an item of the list is clicked it starts a new activity. When back button is pressed, the list is shown again. Im using cursor Loaders so, I want to know if is there an easy way to refresh the cursor loader when back button is pressed. This is because the new activity changes the contents of the list.
Edit:
public class ListWordFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static String TAG = ListWordFragment.class.getSimpleName();
private CursorLoader cursorLoader;
// Loader
private static final int URL_LOADER = 0;
private SimpleCursorAdapter adapter;
public static LoaderManager mLoaderManager;
// ActionBar
private AutoCompleteTextView autoCompView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_list_words, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ListView list = getListView();
String[] from = new String[] { Word.NAME, Word.TYPE, Word.TRANSLATE };
int[] to = new int[] { R.id.textView_word, R.id.textView_type,
R.id.textView_translate };
adapter = new ListWordAdapter(getSherlockActivity(),
R.layout.row_list_words, null, from, to, 0);
setListAdapter(adapter);
mLoaderManager = getLoaderManager();
mLoaderManager.initLoader(URL_LOADER, null, this);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
fillRow(l, id, position, false);
}
private void fillRow(ListView l, long ide, int position, boolean firstCall) {
Cursor cursor = getActivity().getContentResolver().query(
Uri.withAppendedPath(WordListProvider.WORDS_CONTENT_URI,
String.valueOf(ide)), null, null, null, null);
if(cursor.moveToFirst()){
String id = cursor.getString(cursor.getColumnIndex(Word.ID));
String name = cursor.getString(cursor.getColumnIndex(Word.NAME));
String type = cursor.getString(cursor.getColumnIndex(Word.TYPE));
String translate = cursor.getString(cursor
.getColumnIndex(Word.TRANSLATE));
String example = cursor.getString(cursor.getColumnIndex(Word.EXAMPLE));
String note = cursor.getString(cursor.getColumnIndex(Word.NOTE));
// Master/Detail
if ((getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
&& (getResources().getString(R.string.selected_configuration)
.equals(Constants.CONFIGURATION_LARGE))) {
WordDetailFragment frag = (WordDetailFragment) getFragmentManager()
.findFragmentById(R.id.word_detail_fragment);
if (frag != null) {
frag.setId(id);
frag.setName(name);
frag.setType(type);
frag.setTranslate(translate);
frag.setExample(example);
frag.setNote(note);
}
} else if (firstCall) {
// Do nothing
} else {
Intent i = new Intent(getActivity().getApplicationContext(),
WordDetailActivity.class);
i.putExtra(Word.ID, id);
i.putExtra(Word.NAME, name);
i.putExtra(Word.TYPE, type);
i.putExtra(Word.TRANSLATE, translate);
i.putExtra(Word.EXAMPLE, example);
i.putExtra(Word.NOTE, note);
startActivity(i);
}
cursor.close();
}
}
public void onWordSaved() {
getLoaderManager().restartLoader(URL_LOADER, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle) {
String[] projection = { Word.ID, Word.NAME, Word.TYPE, Word.TRANSLATE,
Word.EXAMPLE, Word.NOTE };
/* Takes action based on the ID of the Loader that's being created */
switch (loaderID) {
case URL_LOADER:
// Returns a new CursorLoader
cursorLoader = new CursorLoader(getSherlockActivity(), // Parent
// activity
// context
WordListProvider.WORDS_CONTENT_URI, // Table to query
projection, // Projection to return
null, // No selection clause
null, // No selection arguments
null // Default sort order
);
return cursorLoader;
default:
// An invalid id was passed in
return null;
}
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
Try adding to your Fragment class:
#Override
protected void onResume()
{
super.onResume();
getLoaderManager().restartLoader(URL_LOADER, null, this);
}
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
To clear up your efficiency concern: When you call the initial init, it will start, but restartLoader on the same ID will automatically cancel that previous request and start a new one. So it won't get executed fully twice on activity start up.
More info in Google Loader Dev Doco.
I have been working all day on this, and have been trying to understand how it all fits together and reworking my code to fit together well. So I will show you what I have. There is no error, just blank.
MyAgendaLoaderManager.java:
public class MyAgendaLoaderManager implements LoaderCallbacks<Cursor>{
MyAgendaAdapter agendaAdapter;
Context mContext;
String date;
public MyAgendaLoaderManager(Context context, MyAgendaAdapter adapter, String date) {
agendaAdapter = adapter;
mContext = context;
this.date = date;
}
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
Uri baseUri = SmartCalProvider.CONTENT_URI;
return new CursorLoader(mContext, baseUri,
new String[] {"_id, event_name, start_date, start_time, end_date, end_time, location"},
"WHERE date(?) >= start_date and date(?) <= end_date", new String[]{date, date}, null);
}
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
// TODO Auto-generated method stub
agendaAdapter.swapCursor(arg1);
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
// TODO Auto-generated method stub
agendaAdapter.swapCursor(null);
}
}
CalProvider.java:
public class SmartCalProvider extends ContentProvider {
public static final String AUTHORITY = "content://com.smartcal.eventprovider";
public static final Uri CONTENT_URI = Uri.parse(AUTHORITY);
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "cal.db";
private SmartCalOpenHelper openHelper;
private SQLiteDatabase database;
#Override
public boolean onCreate() {
openHelper = new SmartCalOpenHelper(getContext());
return true;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
database = openHelper.getWritableDatabase();
return database.query("events_info", projection, selection, selectionArgs, null, null, null);
}
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(Uri arg0, String arg1, String[] arg2) {
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
}
And if it matters, heres the code that runs it in the main activity:
agendaAdapter = new MyAgendaAdapter(this, null);
MyAgendaLoaderManager loader = new MyAgendaLoaderManager(this, agendaAdapter, getChosenDate());
I just don't see how it blanks. Please keep in mind I intentionally left some stuff blank in the CursorLoader, and LoaderManager because I did not want to fill it all in to find out there was an error, so I was just testing to see if the initial list was display, and it was not. Any help figuring out what I did wrong would be much appreciated.
EDIT: Actually, now that I think about it, there is nothing that actually ties what I am doing to a specific list besides when my adapter makes the view that holds it. But that view isn't part of the regular layout. So maybe thats an error I have, unfortunately I have no idea how to do so.
You shouldn't instantiate a loader directly. You need to go through the activities getLoaderManager() method for it to be properly initialized and started. So from your activity call getLoaderManager().initLoader()/restartLoader() as needed.
When I start a cursor loader with
Bundle bundle = new Bundle();
bundle.putInt("arg", 123);
getLoaderManager().restartLoader(0, bundle, this);
I want to get the bundle in
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
But this only seems possible from onCreateLoader(...)
The only workaround I can think of is to subclass CursorLoader and add some fields to persist data across loading to onLoadFinished(...)
Thanks!
I wouldn't just use a private member field in the class implementing LoaderCallbacks because you never know exactly which loader is finishing. Better to do as the asker suggested and store the data with the loader. Here's how I do it:
public static class CursorWithData<D> extends CursorWrapper {
private final D mData;
public CursorWithData(Cursor cursor, D data) {
super(cursor);
mData = data;
}
public D getData() {
return mData;
}
}
#Override
public Loader<Cursor> onCreateLoader(int id, final Bundle bundle) {
// ...
return new CursorLoader(getActivity(), uri, projection, selection, args, order) {
#Override
public Cursor loadInBackground() {
return new CursorWithData<Bundle>(super.loadInBackground(), bundle);
}
};
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
CursorWithData<Bundle> cursorWithData = (CursorWithData<Bundle>) cursor;
Bundle args = cursorWithData.getData();
cursor = cursorWithData.getWrappedCursor(); // Optional if you are worried about performance
// ...
}
Since you are using 'this' as third parameter of onLoadFinished, I assume that the class is implementing the LoaderManager.LoaderCallbacks interface. So there is no need for the parameter, you can use a private member field.
I obviously new and have been trying to two days to figure out how to save the state of my main activity to no avail. I would appreciate any help. When I launch the ShowDetail activity and return to the main activity I have no data in the list. I have two xml files a main.xml and a item.xml file. main is just a listview and a textview. Item.xml is 3 textviews for the data in the list. Item Here is the code from my main activity:
public class main extends ListActivity {
private EventsData events;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
events = new EventsData(this);
try {
Cursor cursor = getEvents();
showEvents(cursor);
} finally {
events.close();
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
}
#Override
public void onPause(){
super.onPause();
}
#Override
public void onRestart(){
super.onRestart();
}
private static String[] FROM = { CODE, EXCERPT, _ID, };
private static String ORDER_BY = CODE + " ASC";
private Cursor getEvents() {
SQLiteDatabase db = events.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, FROM, null, null, null, null, ORDER_BY);
startManagingCursor(cursor);
return cursor;
}
private static int[] TO = { R.id.code, R.id.excerpt, };
private void showEvents(Cursor cursor) {
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.item, cursor, FROM, TO);
setListAdapter(adapter);
}
private static String[] MIKEY = { _ID, CODE, DEFINITION };
protected void onListItemClick(ListView l, View v, int position, long id) {
Cursor cursor = ((CursorAdapter)getListAdapter()).getCursor();
cursor.getLong(2);
SQLiteDatabase db = events.getReadableDatabase();
Cursor c = db.query(TABLE_NAME, MIKEY, "_id = "+cursor.getLong(2)+"", null, null, null, null);
c.moveToFirst();
Intent in1 = new Intent();
Bundle bun = new Bundle();
bun.putLong("id", c.getLong(0));
bun.putString("code", c.getString(1));
bun.putString("definition", c.getString(2));
in1.setClass(this, ShowDetail.class);
in1.putExtras(bun);
startActivity(in1);
}
}
I'd say you need to place your general actions into onResume() instead of in onCreate().
Maybe a look at the application lifecycle helps understanding what I mean:
http://developer.android.com/reference/android/app/Activity.html