Android ListView - Refresh and sort? - android

I am doing this:
// Member variable
List<String> items = null;
// in onCreate
items = new ArrayList<String>();
// later on in a Task (onPostExecute)
items.add(NewItem);
adapter.notifyDataSetChanged();
My List is in Alphabetical order. When I add an item, it simply puts it at the bottom. Any way to put it in the proper place?
(Yes it will be in proper order the second time you come back to this page, but not on adding it)
Edit: I am first putting it in order through a SQL script call.

My suggestion to your answer is that, as and when you add an item on ListView try to set the adapter again on the list at the time of adding item.
this way you get the refreshed data on your ListView.

I decided to research any methods for ArrayList and did not find any. I did find this, however:
items.add(NewItem);
Collections.sort(items);
Worked perfectly! I had to import Collections into the project.

Related

How to prevent double items but also prevent entire list from refreshing?

I am getting a JSON Array of user ids strings from a database:
["uid1", "uid103", "uid322"]
I am fetching this array on a time triggered loop, and I use notifyDataSetChanged with myDataSet.clear() to display the list, and to prevent duplicates. The problem with this method is that the list is constantly "refreshing", and I just want to either add or remove the items that's needed to be removed or added.
If I remove the clear() part, then the list won't refresh, but I will get duplicate items.
This is how the code (+pseudo) looks like:
trigger(just a time based loop) {
myDataset.clear();
jsonArray = //the id array from the database;
for (int i=0;i<jsonArray.length;i++){
myDataset.add(new User(jsonArray.get(i), imageUrl));
mAdapter.notifyDataSetChanged();
}
}
You can use for this purpose DiffUtil class (Medium example).
It calculates differences between new and old datasets and updates it.
But if you don't want to deep dive into DiffUtil you can try to use combination of notifyItemInserted() and notifyItemChanged()
You need to compare the 2 lists before refreshing. So,
If an item is removed, Your list's size will be smaller.
If an item is added, the list's size will be bigger and the new item will be the last item in the retrieved array.
But if an item is removed and at the same time an item is added, your list's size will be the same but the last item will change.
So, you need to check both array size and the last item before refreshing like this:
trigger(just a time based loop) {
if((myDataset.size() != jsonArray.size()) || myDataset[last] != jsonArray[last]) {
myDataset.clear();
jsonArray = //the id array from the database;
for (int i=0;i<jsonArray.length;i++){
myDataset.add(new User(jsonArray.get(i)));
mAdapter.notifyDataSetChanged();
}
}
}
Change your code as below, Your loop will do all the changes in your data and once the loop is over you notify those changes to your list in one go, instead of refreshing it each time.
for (int i=0;i<jsonArray.length;i++){
myDataset.add(new User(jsonArray.get(i)));
}
mAdapter.notifyDataSetChanged();
If you want to avoid duplicates use Set, add items to set from your json array and use it as datasource for your list.
You have a basic misunderstanding of the timer trigger. The loop will not pause there waiting for your next User entry from JSON. So adding notify in the loop is very bad practice. One notify outside the loop is enough. If a new User is added, it should appear in another timer trigger, i.e., another loop.
If you are seeing duplicate, that must be in the code handling the JSON to insert your database. However this code is not listed here so you need to look at that code.
Adding a unique index in the database table is a good solution.
for avoiding duplicate use contains
myDataset.clear();
for (int i=0;i<jsonArray.length;i++){
User user=new User(jsonArray.get(i), imageUrl);
//now check if its already in list or not by using contains.
if(!myDataset.contains(user)){
myDataset.add(user)
}
}//end of loop
and then pass list to your adapter and then call notifyDataSetChanged()
mAdapter.notifyDataSetChanged();

SwipeMenuListView new Row added is showing First time data

I've read many of the previous posts on this topic, but i'm not getting it right.
I have an adapter which has a private values of the items in the list.
When i update the values(add a new items), i watch the value in debugger and the "getView" func and see that the value is correct.
BUT the actual rowView i see is just the first item in the list.
I have no clue what may cause this.
This listview is on the same activity while i show a different layout and hide the listview to add a new item.
Can there be a connection while the listview visibility is "GONE"?
When i remove items from it it updates listview fine(that is done when listview is visible).
private void updateAdapter() {
this.values.clear();
this.values.addAll(staticlistIndifferentclass);
notifyDataSetChanged();
}
~~~~UPDATE~~~~
Ok,
So i discovered the cause of the problem, though i'm not sure why it is.
The code was fine the way it was with regular Listview but the bug is on:
com.baoyz.swipemenulistview.SwipeMenuListView
Try use ListView.invalidateViews
This should cause views rebuilding
Use new Adapter object like this :
listView.setAdapter(new yourCustomAdapter());
When you delete all dataSet items ,it is better to bind new Adapter object .

android clear arraylist or adapter?

After working on an app for a while I realize I use
adapter.clear()
and
arraylist.clear()
I can see both are working just fine, I would like to know the difference between the two!
Both are called before I start and asyncTask that updates my list with information from my server!
You should not be clearing the ArrayList directly. The ArrayAdapter makes absolutely no guarantees that it maintains the same referenced list given to it. In fact it will change when you perform a search with it's filter. Which would make arrayList.clear() fail.
Rule of thumb, if you ever need to mutate or retrieve the associating data...do it directly from the adapter. Not the list you used to construct it.
Adapter = it contains copies of diff views,arrays
aaraylist holds the data which we want to display in our view.
ex: arraylist<HashMap<String,String>> ah= new ArrayList<HashMap<String,String>>();
the above list contains hashmap
if i clear the arraylist there will be no data to show on listview or gridview so it will be empty
if i clear adapter than it will destroy the copies of array and views so the output will be same

Android listview refresh only once

I am really desperate, please help me.
In my main Activity I want to remove all items from the listview then add other data. I am able to delete the data and refresh the listview, but when I add new data, something strange happens. I insert the data in my Array. I call notifydatasetchanged on my adapter after every insertion. The adapter gets those items (I have confirmed this in debug mode). But the listview will not refresh. And now comes the VERY STRANGE thing: When I tap the back button on my device, the application quits (this part is ok), but for a moment before quitting it refreshes the listview with the correct data.
Here are some parts of my code:
For deleting the data I use:
newsArrayList = new ArrayList<NewsClass>();
Adapter = new NewsAdapter(this, newsArrayList);
lv.setAdapter(Adapter);
"lv" is my listview. The listview correctly loses all items and refreshes. I have also tried this:
newsArrayList.clear();
Adapter.notifyDataSetChanged();
Also worked like expected. Then I use a "for" loop to add new items to my array, where I have the following lines:
newsArrayList.add(news[i]);
Adapter.notifyDataSetChanged();
As I wrote, when running in debug mode, everything seems fine. I have spent all day working on this problem. I have read several posts about similar issues on stackoverflow, I even watched a video on youtube. I am calling everything from the main UI thread.
I can work around this problem by restarting my activity, but I want to do it better than that. I have tried invalidate and such things, but nothing helped. Please note the VERY STRANGE thing, I wrote...
I think, that I have to force the activity to redraw its views, or something like that, but even refreshDrawableState(); on my listview did not help.
Thank you in advance!
Basically If you want to clear list data then just call
newsArrayList.clear();
lv.setAdapter(null);
Again add what ever your data in ArrayList and set adapter
newsArrayList = new ArrayList<NewsClass>();
Adapter = new NewsAdapter(this, newsArrayList); // considering list with some elements
lv.setAdapter(Adapter);
If you are getting outofmemory error/exception then there is problem with your Adapter Class inside getView method

How to remove the filter on an ArrayAdapter used in an AutoCompleteTextView?

As the title, how can I remove the filtering on an ArrayAdapter used by an AutoCompleteTextView to get the original list back?
A little more background:
It all started from the sad fact that the "position" value passed in to onItemClick() is useless. The "position" refers to the position AFTER the array has been filtered, but I need to know its REAL position. So, what I'm trying to do is when I've got the text of the selected item (by using getItemAtPosition(position)), I compare it one-by-one with the original string array that backs the ArrayAdapter. However, I found that when onItemClick() is called, the adapter is already filtered, I no longer have access to the original array. So I thought if I can remove the filter, maybe I can get back the original array and look for the selected item in it.
ArrayAdapter<String> mAdapter;
public void onCreate() {
// Create an adapter and remembere it as a class member.
mAdapter = new ArrayAdapter<String>(this, layoutId);
// Add 100 strings to it and attach it to an AutoCompleteTextView
for (int i = 0; i < 100; i++)
mAdapter.add("random text");
((AutoCompleteTextView)findViewById(id)).setAdapter(mAdapter);
}
#Override
public void onItemClick(AdapterView<?> actv, View view, int position, long id) {
if (actv.getAdapter().equals(mAdapter))
Log.d("The adapter contained in actv is the same one I created earlier.");
// And, I can get the text of the item the user selected
String selected = (String)actv.getItemAtPosition(position);
// However, although the adapter passed in is still the same one, but the
// number of items in it is only 1! Because the array has been filtered.
int numItems = actv.getAdapter.getCount();
// So, I'm thinking if I can somehow remove the filtering here, then I can
// get back those 100 items, and do a search like following:
for (int i = 0; i < actv.getAdapter.getCount(); i++)
if (selected == actv.getAdapter.getItem(i))
break; // Eureka!!!
}
To tackle the problem of obtaining the REAL position of the selected item:
Is there a way to utilize the "id" value? Like, assign each item an id, then hopefully onItemClick() would pass back the correct id.
Like I said above, remove the filter (is it possible), get back the original 100 items, and perform a one-by-one search.
This is the last resort, I know it'll work, but I don't want to do it: Once I get the text of the selected text, I go back to the source of the data (from a database), query those 100 items out, and perform the search.
Another lame last resort: To avoid the overhead on accessing the database again as in #3, when in onCreate(), while creating the ArrayAdapter, I use an ArrayList of my own to remember all those 100 strings.
Am I doing it all wrong? What's the "right" way of obtaining the real position of the selected item from an AutoCompleteTextView?
Thank you very much!
(I read somewhere, some buy that seemed to be from Google Android team, said that one should use getFirstVisiblePosition() to resolve the position. But I can't figure out how.)
I don't know if you're still interested, but I found this answering a similar question: Problem with AutoCompleteTextView and Spinner using the same Adapter
Copying the method in the AutoCompleteTextView source code:
Filter filter = mAdapter.getFilter();
filter = null;
See my response in the above question for the grepcode link.
This is actually pretty simple to solve.. Instead of adding each element to the adapter as you get it (I'm assuming your random text part is just for example purposes), instead use the following:
First build your array into a variable, call it myArray..
then initialize your adapter like this:
mAdapter = new ArrayAdapter<String>(this, layoutId, myArray);
Now make sure that myArray is a class variable so you can reference it from anywhere else in the class.. Of course if you need access to this from another class you'd want to make a getter for it... Then you can easily iterate over the array to see if the value selected is in the array.. You'll have the whole set of data there instead of trying to get it from the adapter.
Here is a good example on using a validator for a similar looking use case:
Android, Autocomplettextview, force text to be from the entry list
In my case, I have address that can be set either by autocomplete or clicking on the map. If user click on the map, editText text should be set to address selected from the map, and in that case filtering should be disable temporarily.
I code like this:
public void OnAddressFound(String address) {
// Temporary disable autocomplete
editTextSearch.setAdapter(null);
editTextSearch.setText(address);
// Enable autocomplete again
setAutoCompleteAdapter();
}
where setAutoCompleteAdapter() is called during onCreate, and again in temporary disable/enable filter:
void setAutoCompleteAdapter() {
PlacesAutoCompleteAdapter adapter = new PlacesAutoCompleteAdapter(this, R.layout.item_autocomplete_map_search, autoCompleteList);
editTextSearch.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
Hope its help you also.
I found the solution, kinnda tricky but its works
after i looking inside of the source, i found that th treshold variable used as filtering validation, here we just need to set the treshold to maximum int so filtering never perform.
threshold = Integer.MAX_VALUE
or
setThreshold(Integer.MAX_VALUE)

Categories

Resources