I will be very grateful if someone can help me one this :)
I have a Custom Adapter (extending ArrayAdapter), and on the objects it displays (movieDatas), there is a property that vary with time (downloadProgress)
Since I use this adapter in multiple places, I wondered wether it is possible for my CustomAdapter to listen to every movieDatas.downloadProgress property, and then update itself ? Thus, not using ArrayAdapter.notifyDataSetChanged from the activity, but the adapter would take the decision to update by itself.
Previously, I used a Timer on every Activity that called myListView.invalidate() every 5 seconds, but I wondered if the adapter could handle the changes by itself ?
Thank you very much for your help, I begin in android development.
I don't know how you're doing it, but it sounds like you could totally use a callback to implement it.
1) Create an interface like this:
public interface OnDownloadProgressChangeListener{
public void onProgress(int progress);
}
2) Add this to your MovieData object:
// We use an ArrayList because you could need to listen to more than one event. If you are totally sure you won't need more than one listener, just change this with one listener
private ArrayList<OnDownloadProgressChangeListener> listeners = new ArrayList<OnDownloadProgressChangeListener>();
public void addDownloadProgressChangeListener(OnDownloadProgressChangeListener listener){
listeners.add(listener);
}
public void clearDownloadProgerssChangeListeners(){
listeners.clear();
}
//Add any handlers you need for your listener array.
// ALWAYS use this method to change progress value.
public void modifyProgress(int howMuch){
progress+=howMuch;
for (OnDownloadProgressChangeListener listener : listeners)
listener.onProgress(progress);
}
3) Override your custom adapter add method
#Override
public void add(final MovieData item){
item.addDownloadProgressChangeListener(new OnDownloadProgressChangeListener(){
public void onProgress(final int progress){
// Add your logic here
if (progress == 100){
item.update();
}
}
});
super.add(item);
}
4) Whenever an item gets modified, call notifyDataSetChanged() on your adapter. You can even add it after the super.add(item) line in the add implementation, but this is extremely inefficient if you're going to add a lot of items: Add them first then notify the changes.
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 seen few question on SOF, but neither of them helped.
In my application I have a List of users which can be accessed by clicking friends of a user. The flow is:
Go to my profile
Click on my friends to go to an Activity which has list of users(my friends)
Click on any of the listView Item takes to that user's profile
From that profile I can see that user's friends list the same way as mine.
The problem is all these listView items have a button to add as friend which makes me and that user as friend in that list(like follow changes to following in twitter) now I come back through the backstack, and somewhere in one of the previous listViews that user is present for whom the button is still add as friend.
How to change the button(a flag in my adapter data) for that user in all the ListViews?
Use Interface to send events back to the activity and update the list or database when the event is received.
Interfaces are ways of passing messages to "the outer world". Just look at a simple button onClickListener. You basically call a setOnClickListener(this) on the button and implement onClickListener, which is an interface here. Whenever the button is clicked, you get an event in onClick. It is the safest way to pass messages between activities without the need of intents ( which according to me is a huge pain in the ... ) Here is an example:
Example:
class A extends Activity implements EventInterface{
public A(){
//set a listener. (Do not forget it!!!)
//You can call it wherever you want;
//just make sure that it is called before you need something out of it.
//safest place is onCreate.
setEventInterfaceListener( A.this );
}
//this method will be automatically added once you implement EventInterface.
void eventFromClassB(int event){
//you receive events here.
//Check the "event" variable to see which event it is.
}
}
class B{
//Interface logic
public interface EventInterface{
public static int BUTTON_CLICKED = 1;
void eventFromClassB(int event);
}
static EventInterface events;
public static void setEventInterfaceListener(EventInterface listener) {
events = listener;
}
private void dispatchEvent(int trigger) {
if (events != null) {
events.eventFromClassB(trigger);
}
}
//Interface ends
void yourMethod(){
//Call this whenever you want to send an event.
dispatchEvent( BUTTON_CLICKED );
}
}
each time my activty receives a message (from some TCP listening thread), it does
mLstAdpChatScreen.add(line);
updateUI();
private void updateUI()
{
runOnUiThread(new Runnable()
{
public void run()
{
mLstAdpChatScreen.notifyDataSetChanged();
mLstAdpChatScreen.notifyDataSetInvalidated();
mLstVwChatScreen.requestLayout();
mLstVwChatScreen.invalidate();
}
});
}
While this approach works on most of my listviews and they do get updated, it does not for a certain listview. I must be missing something :-?
Thank you
That should work for most of the cases however I got the same problem when trying to update listview from database. I called adapter.notifyDataSetChanged(); too, but listview didn't update at all although I could confirmed the data was changed, and the ArrayList's size increased.
In the end, I implemented a method inside my custom Adapter extending from BaseAdapter just to call notifyDataSetChanged there, like
public class MyAdapter extends BaseAdapter{
public void updateData(){
this.notifyDataSetChanged();
}
}
Then in Activity, I just call adapter.updateData(). And this worked for me. Very weird.
This tutorial uses a SimpleAdapter which works fine, but I need to update the arrays in the adapter when new data is entered.
Could you please guide me on how to update a ListView using something other than a SimpleAdapter?
Use a ArrayAdapter backed by an ArrayList. To change the data, just update the data in the list and call adapter.notifyDataSetChanged().
If you create your own adapter, there is one notable abstract function:
public void registerDataSetObserver(DataSetObserver observer) {
...
}
You can use the given observers to notify the system to update:
private ArrayList<DataSetObserver> observers = new ArrayList<DataSetObserver>();
public void registerDataSetObserver(DataSetObserver observer) {
observers.add(observer);
}
public void notifyDataSetChanged(){
for (DataSetObserver observer: observers) {
observer.onChanged();
}
}
Though aren't you glad there are things like the SimpleAdapter and ArrayAdapter and you don't have to do all that?
SimpleListAdapter's are primarily used for static data! If you want to handle dynamic data, you're better off working with an ArrayAdapter, ListAdapter or with a CursorAdapter if your data is coming in from the database.
Here's a useful tutorial in understanding binding data in a ListAdapter
As referenced in this SO question
Most people recommend using notifyDataSetChanged(), but I found this link pretty useful. In fact using clear and add you can accomplish the same goal using less memory footprint, and more responsibe app.
For example:
notesListAdapter.clear();
notes = new ArrayList<Note>();
notesListAdapter.add(todayNote);
if (birthdayNote != null) notesListAdapter.add(birthdayNote);
/* no need to refresh, let the adaptor do its job */
I created a method just for that. I use it any time I need to manually update a ListView. Hopefully this gives you an idea of how to implement your own
public static void UpdateListView(List<SomeObject> SomeObjects, ListView ListVw)
{
if(ListVw != null)
{
final YourAdapter adapter = (YourAdapter) ListVw.getAdapter();
//You'll have to create this method in your adapter class. It's a simple setter.
adapter.SetList(SomeObjects);
adapter.notifyDataSetChanged();
}
}
I'm using an adapter that inherites from BaseAdapter. Should work for any other type of adapter.