I have dug deep down into SO but, although I have found other people asking similar questions to mine, I have not yet find a question that addresses the same issues I have. I have not found a satisfying answer either.
I have a ListView. When I call from the adapter, .notifyDataSetChanged, the ListView is updated, but I can see the update only once onResume() is called. In other words, I do not see it instantly, only after I leave the activity and comeback.
What can I do to see the update instantly? I have tried the .notifyDataSetChanged method, I have tried resetting the adapter... nothing worked.
According to your comment, you dont update the array IN the adapter, but an array held by the activity you passed to the adapter once. Thats why the adapter isnt updating properly. You are changing the array outside of your adapter-class, which might not be the same array-object your adapter is using. At onResume(), your adapter is recreated with the new array and showing the new content.
A solution would be using the following custom Adapter class:
class MyAdapter extends BaseAdapter {
private Array[] myArray;
public MyAdapter(Array[] myArray) {
this.myArray = myArray;
}
public updateContent(Array[] myNewArray) {
this.myArray = myNewArray;
this.notifyDataSetChanged();
}
// your getItem, getView, and so on methods
}
Then from your activity, simple call myArray.updateContent() with your new Array and it will update immediatly.
Its never good to hold and manipulate an object used from one class (the adapter) within another one (the activity). Try to move all code for manipulating the array into the adapter and use methods to add/remove items. This will make it a lot easier finding this kind of errors!
Related
I'm familiar with MVVM in WPF, but am in the process of porting an existing Android app to make use of a ViewModel class and LiveData for the first time.
The app "as/was" has a custom ArrayAdapter that is used to correctly display the items contained in a List in a gridview. The code that does that looks essentially like this ("essentially" because there are actually 4 such grids in the activity that all do it like this):
ArrayAdapter<String> playerAdapter = new MyAdapter<String>(this, R.layout.grid_item, playerList);
gridView.setAdapter(playerAdapter)
and when the playerList contents changed, there was an explicit call to
playerAdapter.notifyDataSetChanged()
Since my goal is to have the grid respond automatically when the playerList changes, and never have to notify anything, it seems now that the "playerList" needs to become a MutableLiveData<List<String>>.
I suspect I am going to have a problem though-
The MyAdapter class extends ArrayAdapter<T>
So in practice it's going to become an
ArrayAdapter<MutableLiveData<List<String>>>
But a MutableLiveData isn't going to work there, right?
I could unwrap it and make the call to create it look like this:
ArrayAdapter<String> playerAdapter =
new MyAdapter<String>(this, R.layout.grid_item, playerList.getValue());
Is this correct? It seems the way of madness.
Ultimately my goal is to just assign the gridView's android:adapter property to this thing in xml and never have to think about it anymore (beyond the required calls to setvalue() and postValue() when I modify the underlying list...which I'm not even sure how would work here.
I would hope there's already a MutableLiveData Collection that's ready to go somewhere and I'd use that.
What the standard way to do this?
I believe the way you are thinking is wrong. Don't worry about this:
ArrayAdapter<MutableLiveData<List<String>>>
You will get a list thats all. MutableLiveData is to set a livedata object like setValue() or postValue() manually. Just set your below code whenever liveData gets triggered and gives you the new playerList, something like this:
viewModel.getPlayListObservable().observe(this, new Observer<List<String>>() {
#Override
public void onChanged(List<String> playerList) {
ArrayAdapter<String> playerAdapter = new MyAdapter<String>(this, R.layout.grid_item, playerList);
gridView.setAdapter(playerAdapter);
playerAdapter.notifydataChanged();
}
});
I searched for hours but still have no explanation (even not in official docu) what is the right way to use a RecyclerView.Adapters' notify-methods. Already read a lot of similar question on SO, but no could solve my simple problem. I try to put down a short, simplified example.
class MyAdapterClass extends RecyclerView.Adapter<MyAdapterClass.MyViewHolderClass> {
...
List myItemList = new ArrayList<MyItemType>();
...
}
RecyclerView myRecView = (RecyclerView) findViewById(R.id.myRecView);
myRecView.setAdapter(new MyAdapterClass(...)));
Even when inserting the first item, the view is not refreshed.
myAdapter.myItemList.add(newItem);
myAdapter.notifyItemInserted(myAdapter.myItemList.size() - 1);
If I then tightly touch (try to scroll) the 1-item-listview, the item appears. So it seams to be a refreshing issue. But why? I have an emtpy list, then I insert one item, then I notify the adapter about the insertion. Whats wrong with my expectation?
By the way, even if I additionally call:
myAdapter.notifyItemChanged(myAdapter.myItemList.size() - 1);
it gets not automatically refreshed.
EDIT: And of course, I dont want to use notifyDataSetChanged() :-) only insert, update, remove on an existing list.
Try to add this code inside your adapter class :
#Override
public int getItemCount() {
return myItemList.size();
}
i hope you define in adpter getItemCount() in list size. then used blow code for notify adapter.
myadapter.notifyItemRangeChanged(0, userDataArrayList.size());
I have a ListFragment backed by an ArrayAdapter that gets populated by a Loader. When the user clicks on one of the items, I want to pass a reference to the selected item, as well as the rest of the list items to another fragment. My question is how should I get all of the items from the adapter? Here are the possibilities that I see:
1. Keep a reference to the backing List
Create the adapter like so:
List<DomainObject> items = new ArrayList<DomainObject>();
listAdapter = new ArrayAdapter<DomainObject>(getActivity(), R.layout.mine, items);
and then simply pass items or a copy of it to the next activity.
The downside I see of this is that I'm relying on the undocumented fact that the same list that I pass to the constructor contains the items later on.
2. Iterate through the adapter
When an item is clicked, iterate through the adapter and build up the list. This seems like an unnecessary amount of work. The items are contained in a List in the adapter and I'm manually copying each item to a new list.
3. Keep a separate list of items when adding to adapter
Before adding an item to the adapter, add it to a separate list that I maintain in the fragment. This is also wasteful as the list of items is copied in the ArrayAdapter and the fragment.
I'm a little late to the game, but I've run up against a similar issue.
One way to deal with #1 would be to maintain the reference to the list within a subclass of ArrayAdapter, so that your reuse is controlled by the adapter object.
Something like:
public class DomainAdapter extends ArrayAdapter<DomainObject> {
private final List<DomainObject> items;
public DomainAdapter(Context context, List<DomainObject> items) {
super(context, R.layout.mine, items);
this.items = items;
}
public List<DomainObject> getItems() {
return items;
}
}
The solution that I've gone with in the meantime is just to not use ArrayAdapter. In cases where you're fighting against this API, it seems like it's better just to use the less fully-featured (and complex) BaseAdapter. You can read more about the decision to go with BaseAdapter instead of ArrayAdapter in this article: Android Adapter Good Practices.
A quick test says that method 1 works. It seems the quickest and cleanest, but since it is undocumented you may want to test it across the intended platforms and whenever they update in case the underlying structure of ArrayAdapter changes.
I am using compile SDK version 22 and min SDK Version 10.
The best method is to "keep a reference to the List" BUT not passing "items" variable/parameter to the Constructor:
List<DomainObject> items = new ArrayList<DomainObject>();
listAdapter = new ArrayAdapter<DomainObject>(getActivity(), R.layout.mine);
In this way you only instantiate the ArrayList as an empty array and you will have to manage YOUR list by yourself.
I think first method is best way to do this.
I dont think, Data would be original for the Another Activity. because, You would pass items through bundle, so the object is written on bundle first and then in next Activity we read from bundle.
However, if you are using some other way to pass the list, use list.clone() to create new Object, instead of passing original one.
I have a list of 4 items, I have used listview. I want to change a string dynamically on recieving internal event. I see that when I receive the event I am setting the string correctly but and then calling
mAdapter.notifyDataSetInvalidated();
mAdapter.notifyDataSetChanged();
but the list is not getting updated.
I've had the same experience. The cause was that the list adapter was updating on the wrong thread i.e. not the UI thread. This is easily solved by changing the adapter data on the UI thread through (as I found on other posts):
runOnUiThread(new Runnable() {
public void run() {
// code that changes the list adapter data
}
});
Of course you can always create a (inner) class that implements Runnable that is provided with the list adapter and data to add, insert etc.
Note: calling notifyDataSetInvalidated() or notifyDataSetChanged() will not be necessary as it is called by default, unless you turned it off explicitly with setNotifyOnChange(false);
I think that notifyDataSetChanged only works if you use the add (or insert), remove or clear functions on adapter.
You can rebuilt the list adapter for force to refresh the listView.
Excuse me for my bad english
EDIT 2:
I did solve my problem, but i don't know how:S I was moving my code snippets a bit around, a suddenly it worked. Must have done something in the wrong order, but its weird, checked it many times. Thanks for you help, and sorry I can't post an answer ;)
Hi.
I have a list view which I'm trying to refresh to update it self when i add an element to
the underlying array list.
Here is the code snippet:
private void addEvent() {
arrlEvents.add( event );
adptEvents.notifyDataSetChanged();
updateSaveFile();
filterList();
}
The arrlEvents is the underlying arraylist with the events, and im adding one event, trying to update the list view with notifyDataSetChanged(), but it doesnt work. Can anyone help?
Thanks for your time:)
EDIT:
Here is the source code for the adapter:
private ArrayAdapter<Event> adptEvents;
adptEvents = new ArrayAdapter<Event>( EventCalendar.this, R.layout.list_items, arrlEvents );
I have seen that sometimes it just randomly doesnt notify the adapter.
Try using adptEvents as protected or public on a global scope.
I have found that when that doesnt work. You can just re set the adapter again, just substitute the notifyDataSetChanged() for:
adptEvents = new ArrayAdapter<Event>( EventCalendar.this, R.layout.list_items, arrlEvents );
Edit:
Heres a code snipper from an App I wrote that works.
Class definition:
public class ClassName extends ListActivity implements AdapterView.OnItemSelectedListener {
Global Variable:
CustomAdapter adapter;
in OnCreate():
adapter = new CustomAdapter(this,R.layout.layout_name,dataSet);
setListAdapter(adapter);
Whenever I need to notify
adapter.notifyDataSetChanged();
There is no persistent link between arrlEvents and the adptEvents.... the latter simply initialises itself with the elements from the former. adptEvents has no way to know when arrlEvents changes.
To add new items you should call adptEvents.add(event) and not bother calling notifyDataSetChanged() explicitly, since ArrayAdapter.add() does that for you automatically.