I am trying to create an AutoCompleteTextView dynamically for use like a spinner in Android. I have made it work properly with showing the dropdown list on click and on focus change. But when I change orientation after selecting any option, only the selected option is shown in the dropdown after that.
Is there any way to correct this and make it show all the items after orientation change as well? I found one way which is to setText("") everytime but this also clears any selected value which is undesirable.
Any help appreciated!
Recently had a need to do something similar myself. I did this with a custom adapter that overrides the getFilter() which returns null (so that it returns all available values).
public class AutocompleteAdapter extends ArrayAdapter<String> {
public AutocompleteAdapter(Context context, int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
}
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
return null;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
}
};
}
}
Related
I want to periodically change the suggestions given by an AutoCompleteTextview by getting the list from a RESTful web service, and can't get it working smoothly. I set up a hard-coded list of suggestions to make sure it's working:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, new String[] {"Hi", "Ho"});
speciesName.setAdapter(adapter);//my autocomplete tv
I have got a TextWatcher on the textview and when the text changes that launches a non-blocking call to get a new list of suggestions -- this part which gets a new list is working fine. Then I want to reset the adapter, like so:
public void setOptionsAndUpdate(String[] options) {
Log.d(TAG, "setting options");
//speciesName.setAdapter((ArrayAdapter<String>)null);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, options);
speciesName.setAdapter(adapter);
}
This method is called, but doesn't work -- the list of suggestions either disappears or the displayed suggestions remain unchanged despite the call to setAdapter.
Is this even the right approach? I looked at SimpleCursorAdapter but couldn't see how to register my web service as a content provider. (It's of the form http://www.blah.com/query?term=XX, where the XX is the input from my app, and the response is a JSON Array of strings.)
I didn't have any luck using adapter.notifyDataSetChanged() when dynamically adding and changing the data in the adapter. In my situation, I was hitting an external api asynchronously and getting a list of completion data periodically.
This code clears the adapter, and adds the new data as you'd expect. However, I had to call the getFilter().Filter method to force the data to show. Also, I had to explicitly filter based on the current text in the AutocompleteTextView because my api call was asynchronous.
adapter.clear();
for (Map<String, String> map : completions) {
adapter.add(map.get("name"));
}
//Force the adapter to filter itself, necessary to show new data.
//Filter based on the current text because api call is asynchronous.
adapter.getFilter().filter(autocompleteTextView.getText(), null);
This is how I update my AutoCompleteTextView:
String[] data = terms.toArray(new String[terms.size()]); // terms is a List<String>
ArrayAdapter<?> adapter = new ArrayAdapter<Object>(activity, android.R.layout.simple_dropdown_item_1line, data);
keywordField.setAdapter(adapter); // keywordField is a AutoCompleteTextView
if(terms.size() < 40) keywordField.setThreshold(1);
else keywordField.setThreshold(2);
Now of course, this is static and doesn't deal with an over-the-air suggestions but, I can also suggest you to notify adapter for the changes after you assign it to the AutoCompleteTextView:
adapter.notifyDataSetChanged();
Hope this helps.
-serkan
There was a pretty good tutorial on this topic using remote data in the Google Map API to populate a AutoCompleteTextView here.
If you need a cached version, I retrieved it from here.
The original tutorial has been deleted, but essentially you need to write an ArrayAdapter with a custom filter in a similar way to that shown below and assign it to your AutoCompleteTextView.
Note: You need to implement a method autocomplete() that does whatever operation is required to synchronously fetch and return the autocompletion items. As the filter is invoked in a background thread, this will not block the main UI thread.
private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private ArrayList<String> resultList;
public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.get(index);
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}};
return filter;
}
}
Since i am not able to add a comment, i am giving a new answer
There is no need for clearing the adapter or calling adapter.getFilter().filter(...)...
To dynamically update an AutoCompleteTextView adapter, simply add the new item to the adapter and setAdapter again. For the example given in the original question, i tried the following and it works (the code below does not show the initial setting of the adapter, since multiple answers here cover that. This just shows updating the adapter dynamically). The adapter update can be alongside the code that updates the List associated with the ArrayAdapter.
adapter.add(String newSuggestion); // this goes inside a loop for adding multiple suggestions
speciesName.setAdapter(adapter) ; // speciesName is an AutoCompleteTextView as given in the original question.
The best solution I found for updating the adapter:
Editable text = autocomplete.getText();
autocomplete.setText(text);
autocomplete.setSelection(text.length());
How it works:
We set the text of autoCompleteTextView with its current text, so the adapter notifies that data is changed and updates the listViews's content.
But by this trick the cursor moves to the beginning of edittext. so we use autocomplete.setSelection(text.length()) for moving the cursor to the end.
Works like a charm!
Edit:
Also you must use clear(), add() and remove() methods directly on your ArrayAdapter instead of your ArrayList.
I have a list of items with types [a,b,c,d] indicates status of item. When I click on ButtonA I want to show items with types [a,b] in recyclerview, and click on ButtonB show items with types [c,d]. My current solutions is using two list and two adapter, I wonder if there's better approach, thank you.
Its really Easy! I guess in your object model u have Boolean field. You can use Filterable interface.
Just implement it in activity/fragment or viewModel to filter list that u r passing to adapter according to that boolean value or any other criteria inside respective clicklisteners of ur buttons. Its quite simple and intuitive.
Or just share ur code i can do it for you. I really want points ))
The job of an RecyclerAdapter is to show the data you're passing to it.
Unfortunately you don't provide any code, so I assume that the buttons are outside your RecyclerView.
Place a method inside your RecyclerAdapater which you can call from outside. The notifyDataSetChanged() re-runs onBindViewHolder() with your new provided data.
public void updateList(List<YourObjectType> yourObjects) {
this.yourObjects = yourObjects;
notifyDataSetChanged();
}
I solve my problem by implement Filterable interface in adapter. onCreate in activity, after initial adapter to recyclerview, I added this:
adapter.getFilter().filter("u");
Here is my adapter code:
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
if (charSequence.equals("u")) {
List<Transaction> filteredList = new ArrayList<>();
for (Transaction trans : allTrans) {
if (trans.getTr_stt().equalsIgnoreCase("0") ||
trans.getTr_stt().equalsIgnoreCase("2") ||
trans.getTr_stt().equalsIgnoreCase("5")) {
filteredList.add(trans);
}
}
filteredTrans = filteredList;
} else {
List<Transaction> filteredList = new ArrayList<>();
for (Transaction trans : allTrans) {
if (trans.getTr_stt().equalsIgnoreCase("1") ||
trans.getTr_stt().equalsIgnoreCase("3") ||
trans.getTr_stt().equalsIgnoreCase("4")) {
filteredList.add(trans);
}
}
filteredTrans = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = filteredTrans;
return filterResults;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
filteredTrans = (ArrayList<Transaction>) filterResults.values;
notifyDataSetChanged();
}
};
}
I am implementing Edit text with RecyclerView where RecylecerView should update its item and its View when user enter any text in the edit text. It is a very common requirement and i have found tons of solution for this. But i m facing a strange issue that after filtering recylcerView is showing wrong item in the list.
To Illustrate. lets suppose RecylcerView contains items as a,b,c,d and e.
if i search 'c. list shows only one item 'a'. however when i click on this item it is 'c' only. it means only layout not getting updated however values do get updated.
Here is the my implementation.
public class CustomFilter extends Filter{
private CustomFilter(SListRecyclerViewAdapter mAdapter) {
super();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
sData.clear();
final FilterResults results = new FilterResults();
if (constraint.length() == 0) {
sData.addAll(filteredsData);
}
else {
final String filterPattern = constraint.toString().toLowerCase().trim();
for (final S surg: filteredsData) {
if (S.getSUserName().toLowerCase(Locale.getDefault()).contains(filterPattern)) {
sData.add(surg);
}
}
}
results.values = sData;
results.count = sData.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
Please see the below example,you have to update the data with result.Please try
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mFilteredData = (ArrayList<String>) results.values;
notifyDataSetChanged();
}
I dont why it is working this way. But i have found a work around.
recyclerView.swapAdapter(sListRecyclerViewAdapter,false);
It is basically swapping the adapter with new one but recycling earlier views. Its not very much optimal but at least workable.
I have an AutoCompleteTextView in my application, but there is a lot more behind every item in the DropDownMenu than what is displayed.
I have my own ArrayAdapter for the AutoCompleteTextView, and when the user starts typing anything the autocompleteTextView starts to reduce the drop down list. This is what I want to change. Every time the user types a new letter, I´m making a new search from the database and would like to show all of those in the drop down menu that pops up, i.e I dont want the autocompleteTextView to reduce the list due to what the user is typing.
So, my question is, is there a way to block an autocompleteTextView from reducing the results, or is it easier to just do an edit text view with my own drop down menu?
Thanks.
Finally, I found a solution that worked perfectly. Just want to share it since I spend a lot of time to get it work.
The AutoCompleteTextView does not have any methods to set no filter, or any way to use a custom filer.
As I wrote I have a custom ArrayAdapter to the AutoCompleteTextView. So what I did was in the ArrayAdapter class, override the function getFilter.
And give the cred to the people posting the answer here.
#Override
public Filter getFilter() {
return new KNoFilter();
}
private class KNoFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence arg0) {
FilterResults result = new FilterResults();
result.values = searchResults;
result.count = searchResults.size();
return result;
}
#Override
protected void publishResults(CharSequence arg0, FilterResults arg1) {
notifyDataSetChanged();
}
}
I want to periodically change the suggestions given by an AutoCompleteTextview by getting the list from a RESTful web service, and can't get it working smoothly. I set up a hard-coded list of suggestions to make sure it's working:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, new String[] {"Hi", "Ho"});
speciesName.setAdapter(adapter);//my autocomplete tv
I have got a TextWatcher on the textview and when the text changes that launches a non-blocking call to get a new list of suggestions -- this part which gets a new list is working fine. Then I want to reset the adapter, like so:
public void setOptionsAndUpdate(String[] options) {
Log.d(TAG, "setting options");
//speciesName.setAdapter((ArrayAdapter<String>)null);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, options);
speciesName.setAdapter(adapter);
}
This method is called, but doesn't work -- the list of suggestions either disappears or the displayed suggestions remain unchanged despite the call to setAdapter.
Is this even the right approach? I looked at SimpleCursorAdapter but couldn't see how to register my web service as a content provider. (It's of the form http://www.blah.com/query?term=XX, where the XX is the input from my app, and the response is a JSON Array of strings.)
I didn't have any luck using adapter.notifyDataSetChanged() when dynamically adding and changing the data in the adapter. In my situation, I was hitting an external api asynchronously and getting a list of completion data periodically.
This code clears the adapter, and adds the new data as you'd expect. However, I had to call the getFilter().Filter method to force the data to show. Also, I had to explicitly filter based on the current text in the AutocompleteTextView because my api call was asynchronous.
adapter.clear();
for (Map<String, String> map : completions) {
adapter.add(map.get("name"));
}
//Force the adapter to filter itself, necessary to show new data.
//Filter based on the current text because api call is asynchronous.
adapter.getFilter().filter(autocompleteTextView.getText(), null);
This is how I update my AutoCompleteTextView:
String[] data = terms.toArray(new String[terms.size()]); // terms is a List<String>
ArrayAdapter<?> adapter = new ArrayAdapter<Object>(activity, android.R.layout.simple_dropdown_item_1line, data);
keywordField.setAdapter(adapter); // keywordField is a AutoCompleteTextView
if(terms.size() < 40) keywordField.setThreshold(1);
else keywordField.setThreshold(2);
Now of course, this is static and doesn't deal with an over-the-air suggestions but, I can also suggest you to notify adapter for the changes after you assign it to the AutoCompleteTextView:
adapter.notifyDataSetChanged();
Hope this helps.
-serkan
There was a pretty good tutorial on this topic using remote data in the Google Map API to populate a AutoCompleteTextView here.
If you need a cached version, I retrieved it from here.
The original tutorial has been deleted, but essentially you need to write an ArrayAdapter with a custom filter in a similar way to that shown below and assign it to your AutoCompleteTextView.
Note: You need to implement a method autocomplete() that does whatever operation is required to synchronously fetch and return the autocompletion items. As the filter is invoked in a background thread, this will not block the main UI thread.
private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private ArrayList<String> resultList;
public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.get(index);
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}};
return filter;
}
}
Since i am not able to add a comment, i am giving a new answer
There is no need for clearing the adapter or calling adapter.getFilter().filter(...)...
To dynamically update an AutoCompleteTextView adapter, simply add the new item to the adapter and setAdapter again. For the example given in the original question, i tried the following and it works (the code below does not show the initial setting of the adapter, since multiple answers here cover that. This just shows updating the adapter dynamically). The adapter update can be alongside the code that updates the List associated with the ArrayAdapter.
adapter.add(String newSuggestion); // this goes inside a loop for adding multiple suggestions
speciesName.setAdapter(adapter) ; // speciesName is an AutoCompleteTextView as given in the original question.
The best solution I found for updating the adapter:
Editable text = autocomplete.getText();
autocomplete.setText(text);
autocomplete.setSelection(text.length());
How it works:
We set the text of autoCompleteTextView with its current text, so the adapter notifies that data is changed and updates the listViews's content.
But by this trick the cursor moves to the beginning of edittext. so we use autocomplete.setSelection(text.length()) for moving the cursor to the end.
Works like a charm!
Edit:
Also you must use clear(), add() and remove() methods directly on your ArrayAdapter instead of your ArrayList.