Ok, so ive spend two days on this and I have tried everything under the sun and Google :L
Basically, if I add or delete something from my server, I want to update my listview which is in mylistfragment from my detailfragment to reflect the changes on the server. The changes have occur ed through clicking either a delete or add button in my detail fragment.
I have a callback method which has everything that I need to repopulate the listview with I just cant seem to get the listview to update.
Any help would be much appreciated.
My callback code is as follows :
public final Handler myCallBackAll = new Handler() {
#Override
public void handleMessage(Message msg) {
ListView lv = (ListView) view.findViewById(R.id.listView1);
if (msg.obj == "No Response") {
TextView tv2 = (TextView) view.findViewById(R.id.textView2);
tv2.setText("No Response. Please check your internet connection");
tv2.setVisibility(View.VISIBLE);
lv.setVisibility(View.INVISIBLE);
} else {
Beer beers = new Beer();
ArrayAdapter<Beer> arrayAdapter = new ArrayAdapter<Beer>(
getActivity(), android.R.layout.simple_list_item_1,(List)msg.obj);
lv.setAdapter(arrayAdapterNew);
arrayAdapter.notifyDataSetChanged();
}
}
};
It doesnt throw any errors just doesnt work
How my callback is called is here
if (isBound) {
myBinder.getAllBeer(myCallBackAll);
}
Its a bounded service I have to use, a stipulation of the project. All this code works, just mylistview wont update
You say that you have already have the callback set up, so I am assuming the callback is being fired accordingly. If that is the case, make sure you update the data source (Array, List, etc) you are using in your adapter to reflect the changes you made in detailfragment.
Also, don't forget that every time you add, edit, delete data from your adapter's data source, you must call mAdapter.notifyDataSetChanged() to tell the adapter something in your data changed.
EDIT
Try creating your handler this way instead: public final Handler myCallBackAll = new Handler(Looper.getMainLooper())
notifyDataSetChanged() must be executed on the main UI thread. When you create a new handler it will execute on its own thread, so you need to make sure it does execute on the UI thread. Take a look at here for more information
finally figured it out, may not be the best approach but it does work, i had declare my listview as a static variable in my main activity, i then reference this variable in both fragments and it works a treat :P
Related
I have displayed some data in a Listview using ListAdapter. Now I want the listview to refresh automatically after x seconds. How can this be done?
I am using AsyncTask to fetch data from Server.
I have tried this:
public void reloadDeviceData() {
handler.postDelayed(runnable = new Runnable() {
public void run() {
new GetDevices().execute(); // this method will contain your almost-finished HTTP calls
handler.postDelayed(this, TIME);
}
}, TIME);
}
What is happening is that the listview does not refresh but instead add items after the current listview.
GetDevices is the class which extends AsyncTask here.
When you get the response from API. just update the adapter list by using follwing line adapter.list.clear() then after adapter.list.addAll(apiList) and then after You just have to notify the listview adapter.
adapter.notifydatasetchanged();
I would recommend :
use recyclerview for performance point of view .
Check Delta difference between new and old list and add /update those items only. For difference you can use either basic collections class methods or DiffUittls lib
My implementation of ArrayAdapter is such that it listens for changes in the under lying data. So any changes in the data, the adapter is notified and it calls the notifydataSetChanged()
public class MyAdapter extends ArrayAdapter implements ObjectStateListener {
#Override
public void onObjectStateChanged() {
/*Call appropriate functions as the underlying data as changed.
A good way of doing that would be to retrieve only the positions of the filtered values
so that the original dataholder can be used but display only certain indices
*/
Log.v(TAG, "FilterCriteria changed. Trigger fired");
this.filterPositions = Helper().getFilteredPositions(filterCriteria, mAppartments);
Log.v(TAG, "CALLING NOTIFYDATASETCHANGED()...");
notifyDataSetChanged();
Log.v(TAG, "FINISHED CALLING NOTIFY DATASET CHANGED");
}
}
In the above code the function onObjectStateChanged() is called whenever there is change in the data and in turn it calls notifyDataSetChanged(). To test if the onObjectStateChanged() is called correctly, I have put a bunch of Log statements as you can see. All those logs appear in logcat. But getView() is not getting called after calling notifyDataSetChanged() and hence the listview is not getting refreshed.
Why is this happening? notifyDataSetChanged() is supposed to ensure that getView() gets called and the new data is reflected in the ListView right?
Definition of ObjectStateChangeListener
public interface ObjectStateListener {
public void onObjectStateChanged();
public void listenForStateChange(Object o);
}
If you are deleting or inserting a new value on the list then whenever there is change in adapter list notifystatechange() will be called so just after inserting or deleting the value call notifystatechange () it will refresh the listview.
I've had to stop using notifyDataSetChanged() on my AutoCompleTextViews as they're not refreshing either. I just keep a local copy of the adapter and then reset it with the new dataset when it's changed.
Be sure your ArrayAdapter is set to notify on change (it is by default), and you technically shouldn't even need to make that call to notifyDataSetChanged() based on the docs: http://developer.android.com/reference/android/widget/ArrayAdapter.html
First update the data where you have kept it's reference in the adapter and then call the notifyDataSetChanged(). You will have to pass in the new data as well.
I have standard ListView activity linked to the database:
dbHelper = new DbAdapter(this);
dbHelper.open();
recordsCursor = dbHelper.fetchAllRecords();
startManagingCursor(recordsCursor);
String[] from = new String[]{DbAdapter.KEY_1, DbAdapter.KEY_2};
int[] to = new int[]{R.id.text1, R.id.background};
adapter = new SimpleCursorAdapter(this, R.layout.row, recordsCursor, from, to);
User has an option to update the list manually thru the menu item. It runs the task to get updated date from the server:
new SyncTask(getApplicationContext(), true).execute();
(second parameter indicates if this is manually started synchronization or automatic)
Once synchronization finished (onPostExecute), I would like to update the list (fetchAllRecords, changeCursor etc.) if the same activity is still displayed. How could I understand if this is the case?
Unlike fragments you don't have any methods to check the activity lifecycle, but it's simple to add something similar:
private boolean mResumed;
#Override
protected void onResume() {
mResumed = true;
}
#Override
protected void onPause() {
mResumed = false;
}
private void isResumed() {
return mResumed;
}
Simply call isResumed() when your background task completes to see if the Activity is still visible on the screen.
Well, i don't understand in deep the main problem.
You say before:
" what if user just chosen a item in menu item to refresh the list and then waits for its completion? "
If this happens you can update the listview at the end of the task like you say before "onPostExecute" (Without a problem).
But in the other case you can retrive the state of the activity with methods that return flags i you want or if your using fragments you can getActivity() to do something. Ever when the Activity will be displayed the activity do a 'Resumen' step so like Philio explains before.
Conclusion, you can update the listview automatic in onResumen and you can update it manually in onPostExecute in the syncTask. This method will be called in automatic or manual mode doesn't matter.
I am working on tabHost with 4 tabwidget, my all four tab widget has a listActivity that shows list of items from different arraylist objects set from Json parsing Bean classes..
Now the application is working f9 but,,, about 5 in 1 time ratio it shows exception that my adapter is reset but listView unable to display data as the adapter is set from background thread.
I can't provide the adapter data from same UI thread b'coz my Bean classes and Data manager are defined else where....
I have used adapter.notifyDataSetChanged() where required...
Please do not suggest that...
With Regards,
Arpit
Even if things are defined some place else you should still be able to set the adapter using:
runOnUiThread(new Runnable() {
#Override
public void run() {
list.setAdapter(adapter);
}
});
or simply post a runnable to the UI thread:
view.post(new Runnable() {
#Override
public void run() {
list.setAdapter(adapter);
}
});
Hope this helps, Christoffer
I'd recommend using a Handler (http://developer.android.com/reference/android/os/Handler.html) to perform all changes to the adapter: create a Handler instance for each Activity and then send a Message (http://developer.android.com/reference/android/os/Message.html) to that handler from your data manager/bean classes with the new data for the list adapter. You can then update the adapter safely from the Handler as it will perform its work on the UI thread.
Short writeup is here: http://www.tutorialforandroid.com/2009/01/using-handler-in-android.html and there are a number of other questions on SO that describe it.
Hi everytime I install app, open it than close it with back button and than again reopen it I get the same error:
05-05 10:49:35.453: ERROR/AndroidRuntime(5118): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131361820, class android.widget.ListView) with Adapter(class com.TVSpored.ChannelItemAdapter)]
I have read that I have to make onRestart function (LINK)... but I have no idea how to handle it...
onCreate function do the following things:
set layout
set all necessary objects (ProgressDialog, ArrayList, ChannelItemAdapter, channelDBAdapter)
Loads data in asynctask: new LoadChannels().execute(channelItem);
As I have said before it work perfectly onStart and when browsing the app... but when leaving the app and restart it, it always crushes...
Thanks for your help!
Added code:
protected ArrayList<ToDoItem> doInBackground(ArrayList<ToDoItem>... passing)
{
cItems = passing[0]; //get passed arraylist
toDoListCursor = channelDBAdapter. getAllToDoItemsCursor();
startManagingCursor(toDoListCursor);
channelItem.clear();
if (toDoListCursor.moveToFirst())
do
{
String task = toDoListCursor.getString(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_NAME));
String created = toDoListCursor.getString(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_EPG_NAME));
int fav = toDoListCursor.getInt(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_FAV));
int id = toDoListCursor.getInt(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_EPG_ID));
int ch_type = toDoListCursor.getInt(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_CH_CHTYPE_ID));
int country = toDoListCursor.getInt(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_CH_COU_ID));
ToDoItem newItem = new ToDoItem(task, created, fav, id, ch_type, country);
channelItem.add(0, newItem);
}
while(toDoListCursor.moveToNext());
return cItems; //return result
}
From the stack trace you have provided, it seems you modify the content of your ListView Adapter outside of the UI thread. I think you should have a look at your ASyncTask to be sure that you perform modifications on your adapter only on methods that are executed in the UI thread (not in doInBackground for example).
See ASyncTask