I have Fragment and it uses RecyclerView Adapter with LoaderManager to list items. RecyclerView i use is from this address. I've implemented LoaderManager in Fragment and gave context of its Activity to RecyclerView.
When i add new item to list, it does not refresh and show new item in list. I use same Adapter and structure on another activity and it works good. But here i use RecyclerView with Loader inside Fragment and it does not refresh list when i add item, instead i have to go back(finish fragment) and enter again.
I have 2 tables like this. First I get cursor from student_course table and from that cursor i get student ids of specific class. Then using data of first cursor, i get second cursor from students table which has list of students from same class. Then i send second cursor to adapter. I do below operation on both onCreateView() and onCreateLoader:
String[] projection0 = {
DatabaseOpenHelper.COL_STUDENT_ID,
DatabaseOpenHelper.COL_COURSE_ID};
String selection0=DatabaseOpenHelper.COL_COURSE_ID + "=?";
String selectionArgs0[]={String.valueOf(courseId)};
mDataset0=getActivity().getContentResolver().query(StudentContentProvider.CONTENT_URI2, projection0,selection0,selectionArgs0,null);
if(mDataset0.getCount()!=0) {
//BATCH
List<String> selectionList = new ArrayList<String>();
mDataset0.moveToFirst();
...
String[] projection1 = {
DatabaseOpenHelper.COLUMN_ID,
DatabaseOpenHelper.COL_STUDENT_NUMBER,
DatabaseOpenHelper.COL_STUDENT_NAME,
DatabaseOpenHelper.COL_STUDENT_CARD_ID,
DatabaseOpenHelper.COL_STUDENT_COLOR};
selection1 = selection1.substring(0, selection1.length() - 2) + ")";
mDataset1 = getActivity().getContentResolver().query(StudentContentProvider.CONTENT_URI1, projection1, selection1, selectionArgs1, null);
}else{
mDataset1=null;
}
...
mAdapter = new studentListAdapter(getActivity(),mDataset1,this);
mDataset1 is students of same class.
1) What might be the problem ? 2) Is there any way to implement LoaderManager class inside
Update 1: When i change selection1 and selectionArgs1 to null, it updates list immediately after i add item. But it shows all students from every class because i didn't specify selection.
Update 2: When i check onLoadFinished() i see that new cursor, after loading new content, is still same as old.
1) I realized that cursor is not changing when new data is added, so when i add new data i call getLoaderManager().restartLoader(LOADER_ID,null,this) (by localBrodCastManager in my case) which reloads cursor (calls onCreateLoader()) and new data is shown on list. (as Suggested here). This does trick.
2) I found that the reason cursor does not change when item is added, because on onCreateLoader() i returned cursor with specific id selection of students, which means cursor will not change until there is change on that items with specific ids i have given. So, if there is new item added, my cursoris not changed, but if one of the items(which is listed) is removed or changed, then cursor is changed. Solution is here.
Related
I am creating an app where you can insert tasks to do and amend them. I currently can view the tasks in a list which I have created, however when I click on the list only the title is shown, although the other entries aren't shown.
Within the entry screen I have code which populates the Entry screen with previous information:
mSaveButton = (ImageButton) findViewById(R.id.diaryform_ib_save);
Log.i(TAG,"Field Title: "+ mTitle);
Intent showTitle = getIntent();
String fieldTitle = showTitle.getStringExtra(Constants.DIARY_TITLE);
Log.i(TAG,"Field Title 2: "+ fieldTitle);
mTitle.setText(fieldTitle);
Intent showEntry = getIntent();
String fieldEntry = showEntry.getStringExtra(Constants.DIARY_ENTRY);
mEntry.setText(fieldTitle);
Log.i(TAG,"Field Entry: "+ mEntry);
Log.i(TAG,"Field Entry 2: "+ fieldEntry);
I have Log messages in there so I can see what information is being given. For fieldTitle the title is passed and for fieldEntry null is passed. When I make both fieldTitle both fields are filled with the title.
Within my ListActivity class I have the following code snippet:
diaryDAO = new DAO(this); //Creates an instance of the DAO
Cursor diaryCursor = diaryDAO.queryDiary(Diary.DiaryItem.LIST_PROJECTION, null, null);
Log.i(TAG, "I get to here!");
String[] diaryDataColumns = { Diary.DiaryItem.COLUMN_NAME_DIARY_TITLE,Diary.DiaryItem.COLUMN_NAME_DIARY_ENTRY };
Log.i(TAG, "Also here");
int[] viewIDs ={ R.id.textView1 };
Log.i(TAG, "Diary Data Col" +diaryDataColumns);
Log.i(TAG, "I'm here");
//Creates backing adapter for the ListView
#SuppressWarnings("deprecation") s
SimpleCursorAdapter diaryAdapter= new SimpleCursorAdapter(
this,
R.layout.activity_diary_list,
diaryCursor,
diaryDataColumns,
viewIDs
);
Log.i(TAG, "Now im here");
this.setListAdapter(diaryAdapter);
Where I create the diaryDataColumns string I tried added the Diary.DiaryItem.COLUMN_NAME_DIARY_ENTRY and it doesn't show within the entry screen BUT when I change the Diary.DiaryItem.COLUMN_NAME_DIARY_TITLE to ENTRY the list view and the entry shows the entry given.
Any ideas? Slightly confused as to what I am doing wrong.
Thanks
edit:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent openItem = new Intent(this, DiaryEntryActivity.class);
Cursor listCursor = (Cursor)l.getAdapter().getItem(position);
openItem.putExtra(Constants.DIARY_TITLE, listCursor.getString(1));
startActivity(openItem); //Starts the intent actions
}
It seems to me you give two columns, but only one text view. The first given columns data will be populated in the text view, but I think, there is no data in the first column, but your second column contains data, and when you switch them, that data will be populated in the text view. Try it out with two textViews.
But I think to work with content provider is an effecient way, there is a good tutorial on vogella.com about it.
Since you are using SQLite, I suggest you look at using SQLiteOpenHelper. Typically, you subclass this class from the Android API and then use it to implement a ContentProvider. The ContentProvider subclass in turn delivers Cursors to your app which can be used by a CursorAdapter. I suggest you start with a SimpleCursorAdapter and then customize it from there. You should also learn about CursorLoaders which allow you to load data from a database on a separate thread. For more details look at http://developer.android.com/guide/topics/ui/layout/listview.html.
I have a dialogfragment which adds an item to the database and immediately shows it in the listview .
The item added shows in the listview immediately but i don't know why the last item from the database doesn't appear .
It appears after executing the activity showing listview again , but not just after adding the item through my dialog fragment .
for example , if i have 3 items "a" , "b" and "d" in my listview and database , and when i add "c" , "d" doesn't show in the listview . it only shows after executing the fragment showing listview again .
Your adapter is using two different lists, the first is the superclass' list populated as object:
super(activity, R.layout.laundry_list_item, objects);
The other is your own:
private List my;
When you add an item to my the superclass, which provides the data for your ListView, does not know of this change until your restart the Activity.
Try changing your Save button to this:
nam = name.getText().toString();
pri = price.getText().toString();
i = new MyItem(nam, Double.parseDouble(pri));
db.addMyItem(i);
MyActivity f = (MyActivity) getActivity().getFragmentManager().findFragmentByTag("my");
f.getMyListAdapter().add(i);
// You might need this, but I believe the adapter will update itself in add()
//f.getMyListAdapter().notifyDataSetChanged();
getDialog().dismiss();
This will use the superclass' list and avoid your problem.
Alternatively with the code you have posted, I believe a SimpleCursorAdapter is a better choice than your own adapter.
In your Fragment:
public void onCreate() {
...
// Retrieve a Cursor of every row in the database, "SELECT _id, name, quantity FROM Table"
mCursor = db.getAllMyItemsCursor();
madapter = new SimpleCursorAdapter(this, R.layout.laundry_list_item, cursor,
new String[] { "name", "quantity" },
new int[] { R.id.item_name, R.id.item_qty });
}
public void updateListView() {
mCursor.requery();
madapter.notifyDataSetChanged();
}
In the Save Button:
nam = name.getText().toString();
pri = price.getText().toString();
db.addMyItem(new MyItem(nam, Double.parseDouble(pri)));
MyActivity f = (MyActivity) getActivity().getFragmentManager().findFragmentByTag("my");
f.updateListView();
Hope one of these helps!
In my app the spinne shows the active items (I use Loader of support library to load them). The init of spinner works fine and it shows only the active items.
But I got the followeing problem:
When I edit the items in another activity and store them (active and deactivat some of them) an go back (Back-Button), then I see the "old" list of items in the spinner. The adapter get the new cursor with correct data, but the spinner does not refresh his list.
What can I do to refresh the spinner list?
In onCreate() I initialize the Spinner
_ItemSpinner = (Spinner) getView().findViewById(R.id.spn_itemss);
_ItemAdapter = new AdvancedCursorAdapter(
getActivity(),
R.layout.spinner_2line_item,
null, _ColumnList, _ColumnViews, 0);
_ItemSpinner.setAdapter(_ItemAdapter);
In onResume() I start the loader initialisation
#Override
public void onResume() {
getLoaderManager().initLoader(1, null, this);
super.onStart();
}
In onLoadFinished(Loader<Cursor> loader, Cursor data) I swap the cursor for the adapter
_ItemAdapter.swapCursor(data);
you can call the adapter's notifyDataSetChanged method
Finally I found out, not why, but which, element destroy the refresh of the spinner.
I used two different URIs of the same ContentProvider for updating all items and showing only active items
CONTENT_URI_ITEMS
CONTENT_URI_ACTIVE_ITEMS
If I use the same URI for updating and showing (with needed active filter), the spinner list will be updated. If I use different URIs, the spinner shows old data.
I don't understand this behavior (the cursor returns correct number of items, but the spinner doesn't care), but I have a work around that works.
Store data:
getActivity().getContentResolver().update(
RecordsContentProvider.CONTENT_URI_ITEMS,
_Changes,
COLUMN_ID + " =?",
new String[] { String.valueOf(_ID) });
Before (get data) - doesn't work:
CursorLoader curLoader = new CursorLoader(this.getActivity(),
RecordsContentProvider.CONTENT_URI_ACTIVE_ITEMS,
_ITEMS_PROJECTION,
null, null, null);
After (get data) - work:
CursorLoader curLoader = new CursorLoader(this.getActivity(),
RecordsContentProvider.CONTENT_URI_ITEMS,
_ITEMS_PROJECTION,
COLUMN_IS_ACTIVE + " =1", null, null);
notifyDataSetChanged is not necessary, if the underlying ContentProvider do this for you like this: (getContext().getContentResolver().notifyChange(uri, null);)
Have you tried:
getLoaderManager().restartLoader(0, null, this);
in your activity, just after making changes on a database.
I have a ListView in a ListActivity that's bound to some data. I have a content provider that's providing the data.
The ListActivity gets the data by querying a content resolver:
Uri uri = Uri.parse("content://my.provider.DocumentProvider/mystuff");
contentCursor = this.getContentResolver().query(uri, null, null, null, null);
So now the activity has the cursor. It creates an adapter and attaches it to the list:
ListAdapter adapter = new DocumentListCursorAdapter(this, R.layout.main_row_layout, contentCursor, new String[] { "titleColumn" }, new int[] { titleColumnIndex });
setListAdapter(adapter);
This works fine; the list shows the data in the cursor.
But now the content provider has new data. I want the list to update to show the new data.
The examples I've seen involve a call to the adapter's notifyDataSetChanged, but it seems to me that this breaks the separation between the content provider and the list, which is consuming the content.
Does the content provider need to know what adapters are attached to the cursor so it can call their notifyDataSetChanged method? Or is there a better way that doesn't see these two things coupled this way.
I found the answer here:
http://mylifewithandroid.blogspot.com/2008/03/observing-content.html
In a nutshell, the provider calls notifyChange to indicate that the content at the URI has changed:
getContext().getContentResolver().notifyChange(uri, null);
And the ListActivity calls setNotificationUri on the cursor to register that it's interested in receiving notification of changes:
contentCursor.setNotificationUri(getContentResolver(), uri);
(Thanks njzk2 for pointing me in the right direction).
asuming that ContentProvider is yours you should add cursor.setNotificationUri(getContext().getContentResolver(), uri); in your query implementation of CP(before you return cursor) and getContext().getContentResolver().notifyChange(uri, null); in update, insert, delete ... SimpleCursorAdapter which is prolly base of your DocumentListCursorAdapter should take care about refreshing listview.
You mean, you want to update list as per data changed.
For that just try this :
when you get new cursor then just put this code instead set new adapter to list..
adapter.notifyDatasetChanged();
and
listview.invalidate()
I'm trying to implement a fast scroller with AlphabetIndexer, but when the cursor changes, its not refreshing the index chache. In my CursorAdapter constructor I call setCursor(cursor) but nothing changes, and acording to the documentation:
Your adapter is responsible for updating the cursor by calling
setCursor(Cursor) if the cursor changes. getPositionForSection(int)
method does the binary search for the starting index of a given
section (alphabet).
But nothing is happening. I'm using this for a search filter, so when I search contacts, it updates the list with the contacts, so AlphabetIndexer should work for update the index of the new items in the list.
Example: My whole list starts with contacts that starts with 'A' and ends with contacts that starts with 'E'. So the AlphabetIndexer will have this indexes in its cache.
But, lets try to search contacts with 'C', and lets say I have 250 contacts that starts with 'C'. So, I have to fast scroll through this contacts, and the 'C' index must show, but instead of only 'C' it shows all indexes, that were shown when I had the entire list.
Here is my CursorAdapter constructor where I call setCursor(cursor) for every letter I type:
public MyCursorAdapter(Context context, Cursor cursor, ArrayList<Integer> ids)
{
super(context, cursor);
try
{
mAlphaIndexer = new AlphabetIndexer(cursor, cursor.getColumnIndexOrThrow("Name")," ABCDEFGHIJKLMNOPQRSTUVWXYZ");
notifyDataSetChanged();
mAlphaIndexer.setCursor(cursor);
this.mSelectedContacts = ids;
Log.e("MyCursorAdapter", "construtor: ");
}
catch(Exception ex)
{
Log.e("MyCursorAdapter", "Error: " + ex);
}
finally
{
mAlphaIndexer.setCursor(cursor);
}
}
I've solved this by calling this in sequence after I set the adapter:
listView.setFastScrollEnabled(false);
listView.setFastScrollEnabled(true);
That worked for me.
call setContentView(R.layout.whatever)and then re-populate the ListView with your new adapter / new data items. This will redraw the ListView with your new items and the FastScroll Overlay will appear in the correct place.