I have an ArrayAdapter which I notify of some changes (notifydatasetchanged).
Directly after, I want to work on the changed dataset, however the methods working on the changed set apparently start before the dataset has been changed.
I have tried a while loop:
while (l.getChildCount()<2){
}
Where l is my dataset and the number of children in the beginning was on, later two.
However, this solution loops endlessly.
My dataset IS changed, I have tested that - only not before I can work on it in the same method where I do the changing.
How can I fix that?
Related
i have a question, i have a for loop, that loops through an arraylist and checks if a value is something, and if so, it deletes an row in a listview....I am having trouble though, this is the code...
for (int i=0; i<displayList.size(); i++)
{
Object toRemove = mAdapter.getItem(i);
Log.e("Counter+++",String.valueOf(i));
if (!firstname.equals("")) {
if (firstnamefilterstring.equals("Contains"))
{
if (!displayList.get(i).getFirstname().contains(firstname))
{
//displayList.remove(listView.getItemAtPosition(i)); doesnt work
// displayList.remove(toRemove); doesnt work either
L.e("1");
}
The problem is through debugging I have realized this loop only runs 6/12 times (the for loop is 12 elements big), and I have no clue why... Through some additional debugging, I realized that the two lines with "doesnt work" if you comment them out, it runs fine (12 times), but with either of those two lines it doesnt....Im at a lost here,
The reason it runs half the time is you are removing items from your display List, hence displayList.remove.
The java.util.ArrayList.remove(int index) method removes the element
at the specified position in this list. Shifts any subsequent elements
to the left (subtracts one from their indices).
Question 1
I was looking at the example code on this page that uses SortedList with RecyclerView.
At line 127, after the CheckBox status changed, recalculatePositionOfItemAt() method was used. The javadocs for SortedList<T> says that recalculatePositionOfItemAt() is for adjusting item positions without triggering onChanged() callback. And updateItemAt() will call onChanged() and/or onMoved() if necessary.
In the case of the example code, the item's field boolean mIsDone changed. I thought updateItemAt() would be more appropriate here?
Question 2 (related)
I tried to play around to use updateItemAt() with a sorted list, but some times java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling was triggered and I found lines 122-125, 152-154 in the example code helps to avoid the exception. I thought the checkbox checked status changing event can only happen when the user checks/unchecks the checkbox. Why are these lines necessary? Some times random tapping or scrolling events far away from the checkboxes can trigger the event?
To update a Listview we can do following two things:
1) Create a new ArrayAdapter and bind it to ListView everytime the data is updated.
this.listView.setAdapter(new ArrayAdapter(getActivity(), data));
or
2) Update the contents of ArrayAdapter which is already binded to listview:
this.arrayadapter.clear();
for (Data item : data)
this.arrayadapter.add(item);
this.arrayadapter.notifyDataSetChanged();
So which method is more expensive?
The way you are doing it, the second one is definitely more expensive. ArrayAdapter calls notifyDataSetChanged() internally in clear() as well as in add(). You could change it to
this.arrayadapter.setNotifyOnChange(false);
this.arrayadapter.clear();
for (Data item : data)
this.arrayadapter.add(item);
this.arrayadapter.setNotifyOnChange(true);
this.arrayadapter.notifyDataSetChanged();
But even if you it this way, both calls contain a synchronized block which makes them slow. So even though you are creating a new ArrayAdapter in the first method, I would say it is faster.
You could further optimize it by using a custom adapter with a setItems(data) method that just replaces the internal data list.
I'd becareful with the accepted answers final conclusion. While SimonSays makes valid points about the iteration and adding...there's a lot that happens with setting a new adapter he fails to recognize. Which makes it hard to say if it's actually any better then the suggested for...each approach.
Setting a new adapter will cause the ListView to flush out all it's recycled view's. It'll also have to re-measure all the new views coming in...which leads to the getView() being called 3-4 times per item for several of the positions...if not all positions. (Varies with platform). So even though there's a sync block on adding, you'll probably see the getView method invoked far less times with possibility of using recycled views.
Basically, knowing which is better is hard and depends greatly on use case. I'd vote to stick with the for...each approach simply for better readability.
I am trying to follow what is going on with the boolean variable, hasMoreData with EndlessAdapter and why is seems to be prematurely turning false.
Let me start from beginning to run through what happens. Note: I am using a task and setRunInBackground(false);
I start off setting my list and setting the adapter:
profileList = new ArrayList<ProfileReview>();
endlessAdapter = new EndlessProfileAdapter(getActivity(), profileList);
endlessAdapter.setRunInBackground(false);
listView.setAdapter(endlessAdapter);
Sidenote: Not sure if this is correct, but it seems I am setting the list with an empty adapter.
The first thing that appears to happen after adapter is set is the method cacheInBackground(), where my profileList size is zero, so it sets 0 as int startPoint when calling my AsyncTask where hasMoreData is set to true. Meanwhile, in this (cache) method, hasMoreData returns true. Not sure why? Because the list is zero in size? Or because its still associated with the default value of true?
In the task, it grabs first 10 items.
Then as user scrolls, the thobber starts spinning. And next 10 are displayed. Log.d tells me that profileList.size() is now 10 and hasMoreData is therefore false.
public void onItemsReady(ArrayList<ProfileReview> data) {
profileList.addAll(data);
endlessAdapter.onDataReady();
hasMoreData = profileList.isEmpty(); \\ Log.'d this out
}
My questions: My list starts with 10 items, users scrolls, it grabs 10 more. Then stops after a total of 20 items (or when hasMoreData == false.) But I have many more items to pull from. How do I keep hasMoreData == true? What is the trigger for this? Obviously the trigger is list size (I think?), and why would the list size ever be 0 once it starts to grab data? (until the end of course)
Not sure if this is correct, but it seems I am setting the list with an empty adapter.
EndlessAdapter is definitely designed to start with a non-empty adapter. In fact, it is designed assuming that the user must scroll to get it to load more data. Behavior in your current approach is unspecified, and I do not recommend that approach. Please load some data, then populate the list once your first batch of data is ready.
Meanwhile, in this (cache) method, hasMoreData returns true. Not sure why? Because the list is zero in size? Or because its still associated with the default value of true?
Since EndlessAdapter does not have a hasMoreData method. A search of the source code to EndlessAdapter turns up nothing named hasMoreData. Heck, the only places the word "more" appears is in comments.
A sample app has a hasMoreData value. Since you are not using this sample app, I cannot help you with random data members of random classes in your own code.
In the sample app, in EndlessAdapterCustomTaskFragment, I use a data member named hasMoreData. This is a boolean value, designed to be returned from cacheInBackground(). The responsibility of cacheInBackground() is to return true if we should continue to load data (after the current batch just loaded), false otherwise. In the case of this sample app, hasMoreData is populated by the call to onItemsReady(), itself triggered by onPostExecute() of the AsyncTask simulating loading some data. hasMoreData is set to true or false depending upon whether the items collection is empty, so it basically does a single load of additional data, then calls it quits.
But that is the behavior of a sample app. I didn't even write most of this class -- it came as a patch adding in support for your own data-fetching task. Do not consider sample code to be anything more than a sample.
Hence, you need to set your hasMoreData value to whatever makes sense for your application logic to serve whatever role you decided to use hasMoreData for. If hasMoreData has the same role in your code as it does in the sample, leave it true until you have determined that you are out of data, then set it false.
sorry for stupid question. But really interesting and incomprehensible. In this session discussed about notifyDataSetChanged() method.
From documentation for this method - "called when the data set being observed has changed, and which when read contains the new state of the data". My English bad and I do not understand all. But I right if guess that method called when I need refresh ListView with new data set?
If I'm right then I'm confused. In the past and my first program I played with contacts api of android. And run some processing in an asynctask. At this time appeared dialog with progress bar and in the background, you could see how the state of ListView changed in real time. Data for ListView row changed via BindView.
Why? So I'm in something wrong. Explain please.
As i read it, BindView is only used with cursors, which are a specific type of a data set basically. You can have alternative data sets, there is for example an ArrayListAdapter in the API which uses an ArrayList as its dataset. In case that data set changes, notifyDataSetChanged() will have to be called to notify the list view that its bounds will have to be recalculated and its views have to be redrawn (and probably some more).
If you decide to write your own and create the possibility to modify the data shown in the list view through an adapter (one could imagine adding method like addObject(SomeObject o) in your home made adapter for example), then you'd call notifyDataSetChanged() in that method.
Similarly if you have a deleteObject(SomeObject x), if the remaining data set is larger than zero you'd call notifyDataSetChanged() or when the remaining data set is empty you'd call notifyDataSetInvalidated() which in turn will to some extra stuff like setting the so called empty view in the list if you have one specified.