I have created a list view in android and I want to add edit text above the list and when the user enter text the list will be filtered according to user input
can anyone tell me please if there is a way to filter the list adapter in android ?
Add an EditText on top of your listview in its .xml layout file.
And in your activity/fragment..
lv = (ListView) findViewById(R.id.list_view);
inputSearch = (EditText) findViewById(R.id.inputSearch);
// Adding items to listview
adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.product_name, products);
lv.setAdapter(adapter);
inputSearch.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
MainActivity.this.adapter.getFilter().filter(cs);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { }
#Override
public void afterTextChanged(Editable arg0) {}
});
The basic here is to add an OnTextChangeListener to your edit text and inside its callback method apply filter to your listview's adapter.
EDIT
To get filter to your custom BaseAdapter you"ll need to implement Filterable interface.
class CustomAdapter extends BaseAdapter implements Filterable {
public View getView(){
...
}
public Integer getCount()
{
...
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
arrayListNames = (List<String>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<String> FilteredArrayNames = new ArrayList<String>();
// perform your search here using the searchConstraint String.
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mDatabaseOfNames.size(); i++) {
String dataNames = mDatabaseOfNames.get(i);
if (dataNames.toLowerCase().startsWith(constraint.toString())) {
FilteredArrayNames.add(dataNames);
}
}
results.count = FilteredArrayNames.size();
results.values = FilteredArrayNames;
Log.e("VALUES", results.values.toString());
return results;
}
};
return filter;
}
}
Inside performFiltering() you need to do actual comparison of the search query to values in your database. It will pass its result to publishResults() method.
Implement your adapter Filterable:
public class vJournalAdapter extends ArrayAdapter<JournalModel> implements Filterable{
private ArrayList<JournalModel> items;
private Context mContext;
....
then create your Filter class:
private class JournalFilter extends Filter{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults result = new FilterResults();
List<JournalModel> allJournals = getAllJournals();
if(constraint == null || constraint.length() == 0){
result.values = allJournals;
result.count = allJournals.size();
}else{
ArrayList<JournalModel> filteredList = new ArrayList<JournalModel>();
for(JournalModel j: allJournals){
if(j.source.title.contains(constraint))
filteredList.add(j);
}
result.values = filteredList;
result.count = filteredList.size();
}
return result;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
items = (ArrayList<JournalModel>) results.values;
notifyDataSetChanged();
}
}
}
this way, your adapter is Filterable, you can pass filter item to adapter's filter and do the work.
I hope this will be helpful.
In case anyone are still interested in this subject, I find that the best approach for filtering lists is to create a generic Filter class and use it with some base reflection/generics techniques contained in the Java old school SDK package. Here's what I did:
public class GenericListFilter<T> extends Filter {
/**
* Copycat constructor
* #param list the original list to be used
*/
public GenericListFilter (List<T> list, String reflectMethodName, ArrayAdapter<T> adapter) {
super ();
mInternalList = new ArrayList<>(list);
mAdapterUsed = adapter;
try {
ParameterizedType stringListType = (ParameterizedType)
getClass().getField("mInternalList").getGenericType();
mCompairMethod =
stringListType.getActualTypeArguments()[0].getClass().getMethod(reflectMethodName);
}
catch (Exception ex) {
Log.w("GenericListFilter", ex.getMessage(), ex);
try {
if (mInternalList.size() > 0) {
T type = mInternalList.get(0);
mCompairMethod = type.getClass().getMethod(reflectMethodName);
}
}
catch (Exception e) {
Log.e("GenericListFilter", e.getMessage(), e);
}
}
}
/**
* Let's filter the data with the given constraint
* #param constraint
* #return
*/
#Override protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<T> filteredContents = new ArrayList<>();
if ( constraint.length() > 0 ) {
try {
for (T obj : mInternalList) {
String result = (String) mCompairMethod.invoke(obj);
if (result.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
filteredContents.add(obj);
}
}
}
catch (Exception ex) {
Log.e("GenericListFilter", ex.getMessage(), ex);
}
}
else {
filteredContents.addAll(mInternalList);
}
results.values = filteredContents;
results.count = filteredContents.size();
return results;
}
/**
* Publish the filtering adapter list
* #param constraint
* #param results
*/
#Override protected void publishResults(CharSequence constraint, FilterResults results) {
mAdapterUsed.clear();
mAdapterUsed.addAll((List<T>) results.values);
if ( results.count == 0 ) {
mAdapterUsed.notifyDataSetInvalidated();
}
else {
mAdapterUsed.notifyDataSetChanged();
}
}
// class properties
private ArrayAdapter<T> mAdapterUsed;
private List<T> mInternalList;
private Method mCompairMethod;
}
And afterwards, the only thing you need to do is to create the filter as a member class (possibly within the View's "onCreate") passing your adapter reference, your list, and the method to be called for filtering:
this.mFilter = new GenericFilter<MyObjectBean> (list, "getName", adapter);
The only thing missing now, is to override the "getFilter" method in the adapter class:
#Override public Filter getFilter () {
return MyViewClass.this.mFilter;
}
All done! You should successfully filter your list - Of course, you should also implement your filter algorithm the best way that describes your need, the code bellow is just an example.. Hope it helped, take care.
Related
I implemented an AutoCompleteTextView where the data is updated from the server every time the user enters a new text input. It works well. However, every time I enter a new query, the previous results are displayed until the new result set is updated.
My guess is that it displays the currently queried results until the backend responds with the new search results. Is there a way to clear the current results?
Activity code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_test);
actv = (AutoCompleteTextView) findViewById(R.id.actv);
actv.setThreshold(1);
actv.setAdapter(new SearchSuggestionsAdapter(this, actv.getText().toString()));
Custom adapter:
public class SearchSuggestionsAdapter extends ArrayAdapter<String> {
List<String> types = new ArrayList<>();
protected static final String TAG = "SuggestionAdapter";
private List<String> suggestions;
private Context context;
public SearchSuggestionsAdapter(Activity context, String nameFilter) {
super(context, android.R.layout.simple_dropdown_item_1line);
suggestions = new ArrayList<String>();
this.context = context;
}
#Override
public int getCount() {
return suggestions.size();
}
#Override
public String getItem(int index) {
return suggestions.get(index);
}
#Override
public Filter getFilter() {
Filter myFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
//Get new results from backend
//searchItemsFromServer is the method that returns the data
//new data is successfully sent. no problem there
List<String> new_suggestions = searchItemsFromServer(constraint.toString());
suggestions.clear();
for (int i = 0; i < new_suggestions.size(); i++) {
suggestions.add(new_suggestions.get(i));
}
filterResults.values = suggestions;
filterResults.count = suggestions.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence contraint,
FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return myFilter;
}
}
Thank you in advance!
Change your code to your activity to this
SearchSuggestionsAdapter searchSuggestionsAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_test);
searchSuggestionsAdapter = new SearchSuggestionsAdapter(this, actv.getText().toString());
actv = (AutoCompleteTextView) findViewById(R.id.actv);
actv.setThreshold(1);
actv.setAdapter(searchSuggestionsAdapter);
actv.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus){
searchSuggestionsAdapter.clear();
}
}
});
}
Add the following code to your SearchSuggestionsAdapter
/**
* Create list and notify recycler view
*/
public void clear() {
if (suggestions != null) {
suggestions.clear();
notifyDataSetChanged();
}
}
actv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
suggestionAdapter.clear;
}
});
When user click on autocomplete remove the old adapter. But for this you need to define suggesstionAdapter first. and every time update this adapter and set it to autocomplete
Clear suggestion before filtering. Call adapter.clear();
Add new method in adapter
public void clear() {
suggestions.clear();
notifyDataSetChanged();
}
I have a working search function that inserts search results into a spinner, but i want it to be live without the need to press a search button just like google.
Something like this:
Well, first of all, you need to use an AutoCompleteTextView, that triggers a filter each time that the text change, and show the suggestions exactly as the image that you attached.
Then, you must be your custom adapter and add it to your view in your onCreate function of your activity:
protected void onCreate(Bundle savedInstanceState) {
...
AutomaticSearchAdapter adapter = new AutomaticSearchAdapter(this, new ArrayList<CustomObject>(), "");
listView = (AutoCompleteTextView) findViewById(R.id.search_complete);
listView.setThreshold(1);
listView.setAdapter(adapter);
...
}
Here is a template of how you can build your custom adapter, that load data from any API and suggest according to your own filtering criteria
public class AutomaticSearchAdapter extends ArrayAdapter implements AsyncResponse, Filterable {
private ArrayList<ContactItem> realList; // The list that will be shown
private ArrayList<CustomObject> results = new ArrayList<CustomObject> (); // A list with all data from your API
private Filter mFilter = new Filter(){
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if(constraint != null) {
String constraintTrim = ContactSearchAdapter.toPlain(constraint.toString()).trim().toLowerCase();
// When te user introduces at least 3 characteres, get data from your API
if (constraintTrim.length() == 3) {
// Call Here to you API, and populate results
}
ArrayList<CustomObject> suggestions = new ArrayList<CustomObject> ();
for (CustomObject object : results) {
if(/*some condition on your downloaded datum */) {
suggestions.add(object);
}
}
filterResults.values = suggestions;
filterResults.count = suggestions.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence contraint, FilterResults results) {
if(results == null){
return;
}
try {
realList.clear();
if (results.values != null){
realList.addAll((List<CustomObject>) results.values);
}
notifyDataSetChanged();
}catch ( Exception e) {
}
}
};
public int getCount() {
return realList.size();
}
#Override
public Object getItem(int position) {
try {
if (realList.size() > 0 ) {
return realList.get(position);
}else {
return null;
}
}catch (Exception ex) {
return null;
}
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// HERE MUST BE YOUR CUSTOM getView FROM YOUR LIST
// YOU CAN LEARN MORE ABOUT http://androidexample.com/How_To_Create_A_Custom_Listview_-_Android_Example/index.php?view=article_discription&aid=67
return convertView;
}
#Override
public void postStart() {
//Nothing to do.
}
#Override
public Filter getFilter() {
return mFilter;
}
}
Of course, you will need refactor the code above for your own requirements.
I bevieve what you need is an onTextChanged listener take a look here https://developer.android.com/reference/android/text/TextWatcher.html
I try added filter to ArrayList (data is from json/php script on my server). I find but I not see resolving...
I try more sample codes and more resolving, but nothing works.
listView = (ListView) findViewById(R.id.list);
list = new ArrayList<FriendItem>();
adapter = new FriendAllAdapter(context, list);
listView.setAdapter(adapter);
String filename = getResources().getString(R.string.friendalllist_php);
asyncLoadVolley = new AsyncLoadVolley(context, filename);
Map<String, String> map = new HashMap<String, String>();
map.put(Constant.ID, Sessions.getUserId(context));
asyncLoadVolley.setBasicNameValuePair(map);
asyncLoadVolley.setOnAsyncTaskListener(asyncTaskListener);
connectionDetector = new ConnectionDetector(context);
if(savedInstanceState==null) {
}
//enables filtering for the contents of the given ListView
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(listItemClickListener);
EditText myFilter = (EditText) findViewById(R.id.myFilter);
myFilter.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
//here added filter...
}
});
For example:
You have a model - FriendItem
public class FriendItem {
private String firstName;
private String lastName;
//.....
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
and have an adapter - FriendAllAdapter.
In this adapter you should implement Filterable interface.
You"ll need to Override getFilter() method and return new object of Filter class (android.widget.Filter).
In performFiltering() method your need return FilterResults object with count of filtered elements and value - list of elements. Also in this method you need to realize the algorithm comparison of the search query to values in your FriendItem (list item). In my example I search FriendItem(s) which fields (firstName or lastName) contains text in my editText.
Input parameter is a CharSequence object which you set in onTextChanged() method on added textChangeListener to your EditText.
myFilter.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.getFilter().filter(s);
}
});
In publishResults() method your get filtering result your need cast results.values to List and replace your list to filtering result.
public class FriendAllAdapter extends BaseAdapter implements Filterable {
private List<FriendItem> list;
private final List<FriendItem> fullList = new ArrayList<>();
public FriendAllAdapter(Context context, List<FriendItem> list) {
//...... your code
this.list = list;
fullList.addAll(list);
}
//...... your code
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//... your code
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint == null || constraint.length() == 0) { // if your editText field is empty, return full list of FriendItem
results.count = fullList.size();
results.values = fullList;
} else {
List<FriendItem> filteredList = new ArrayList<>();
constraint = constraint.toString().toLowerCase(); // if we ignore case
for (FriendItem item : fullList) {
String firstName = item.getFirstName().toLowerCase(); // if we ignore case
String lastName = item.getLastName().toLowerCase(); // if we ignore case
if (firstName.contains(constraint.toString()) || lastName.contains(constraint.toString())) {
filteredList.add(item); // added item witch contains our text in EditText
}
}
results.count = filteredList.size(); // set count of filtered list
results.values = filteredList; // set filtered list
}
return results; // return our filtered list
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
list = (List<FriendItem>) results.values; // replace list to filtered list
notifyDataSetChanged(); // refresh adapter
}
};
return filter;
}
}
I think you`ll understand it.
P.S. sorry for my English.
I have a custom ArrayAdapter like in this code:
public class UtenteAdapter extends ArrayAdapter<Utente> implements Filterable {
private Context context;
private ArrayList<Utente> utenti;
private ArrayList<Utente> utentiFiltrati;
private FiltroPersonalizzato filtro;
public UtenteAdapter(Context context, ArrayList<Utente> utenti) {
super(context, R.layout.riga_utente, utenti);
this.context=context;
this.utenti=utenti;
utentiFiltrati = new ArrayList<Utente>();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//omitted..
return riga;
}
#Override
public void notifyDataSetChanged(){
super.notifyDataSetChanged();
}
#Override
public Filter getFilter() {
if (filtro == null) {
filtro = new FiltroPersonalizzato();
}
return filtro;
}
private class FiltroPersonalizzato extends Filter {
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults risultato = new FilterResults();
ArrayList<Utente> i = new ArrayList<Utente>();
if (prefix!= null && prefix.toString().length() > 0) {
// use the initial values !!!
for (int index = 0; index < utenti.size(); index++) {
Utente si = utenti.get(index);
final int length = prefix.length();
// if you compare the Strings like you did it will never work as you compare the full item string(you'll have a match only when you write the EXACT word)
// keep in mind that you take in consideration capital letters!
if(si.getNome().toLowerCase().substring(0, length).compareTo(prefix.toString().toLowerCase()) == 0){
i.add(si);
}
}
risultato.values = i;
risultato.count = i.size();
}
else{
// revert to the old values
synchronized (utentiFiltrati){
risultato.values = utenti;
risultato.count = utenti.size();
}
}
return risultato;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
utentiFiltrati = (ArrayList<Utente>)results.values;
notifyDataSetChanged();
}
}
}
I have a SearchView and I want to filter the rows by name but I have no results after filtering. I am sure that I have results(from debugging) but if I call filter from my ListFragment I can't see my result:
#Override
public boolean onQueryTextSubmit(String query) {
adapter.getFilter().filter(query);
return true;
}
I update my ArrayAdapter from the ListFragment like this:
private void setListaUtenti(){
if(getListAdapter()==null){
// creo l'adapter
adapter=new UtenteAdapter(
getActivity(),
utenti);
setListAdapter(adapter);
} else{
adapter.notifyDataSetChanged();
}
}
Why don't I see the results of the filtering operation?
i have a searchview and i want filter row by name but i have no
result.
It's normal that you don't get a result as in the publishResults() callback of the Filter you assign the results to the utentiFiltrati list and call notifyDataSetChanged(). This will do nothing as your adapter is based on the utenti list, the one you pass to the super class constructor. Make the following changes:
public UtenteAdapter(Context context, ArrayList<Utente> utentiValues) {
super(context, R.layout.riga_utente, utentiValues);
this.context=context;
this.utentiFiltrati = utentiValues;
utenti = new ArrayList<Utente>(utentiValues);
}
// ...
FilterResults risultato = new FilterResults();
ArrayList<Utente> i;
if (prefix == null || prefix.toString().length() == 0) {
// the contract of a Filter says that you must return all values if the
// challenge string is null or 0 length
i = new ArrayList<Utente>(utenti);
} else {
i = new ArrayList<Utente>();
// use the list that contains the full set of data
for (int index = 0; index < utenti.size(); index++) {
Utente si = utenti.get(index);
final int length = prefix.length();
if(si.getNome().toLowerCase().substring(0, length).compareTo(prefix.toString().toLowerCase()) == 0){
i.add(si);
}
}
//...
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
utentiFiltrati = (ArrayList<Utente>)results.values;
notifyDataSetChanged();
}
I want to add a permanent search result in autocomplete text view in android.
For ex:If i enter 'x' in the autocomplete and if it shows the list of hotels...xyz1,xyz2.etc...Then the last result must be "NOT IN LIST" value. If the user cant find their hotel then they can select NOT IN LIST option..
Even if the user types in the text that the predictive search could not give then 'NOT IN LIST' should be the only suggestion that autocomplete should give. I am new to Android.I am doing a small app.Plz help..If i have to use custom autocomplete text view then what method should i override? If so ,tell me with a method code that i have to override
Here is an AutoCompleteAdapter i used in one of my apps. I hope this solves you problem
Set the adapter from below to your AutoCompleteTextView control:
ArrayAdapter<String> adapter = new AutoCompleteAdapter(this,
R.layout.dropdown_item);
autoComplete.setAdapter(adapter);
Sample adapter:
private class AutoCompleteAdapter extends ArrayAdapter<String>
implements Filterable {
private ArrayList<String> mData;
public AutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
mData = new ArrayList<String>();
}
#Override
public int getCount() {
try {
return mData.size();
} catch (NullPointerException e) {
return 0;
}
}
#Override
public String getItem(int index) {
return mData.get(index);
}
#Override
public Filter getFilter() {
Filter myFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
//This shows a progress to the user in my app. you don't need to use this
handle.sendEmptyMessage(MSG_SHOW_PROGRESS);
try {
//Fill mData with your data
mData = XmlParser.searchLocations(constraint
.toString());
} catch (Exception e) {
handle.sendEmptyMessage(MSG_HIDE_PROGRESS);
}
mData.add("NOT IN LIST");
filterResults.values = mData;
filterResults.count = mData.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence contraint,
FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
handle.sendEmptyMessage(MSG_HIDE_PROGRESS);
} else {
notifyDataSetInvalidated();
handle.sendEmptyMessage(MSG_HIDE_PROGRESS);
}
}
};
return myFilter;
}
}
if (constraint != null) {
try {
for(int i=0;i<temp.size();i++)
{
if(temp.get(i).toString().startsWith(constraint.toString().toUpperCase()))
{
mData.add(temp.get(i).toString());
}
}
mData.add("SEARCH NOT THERE");
} catch (Exception e) {
}
filterResults.values = mData;
filterResults.count = mData.size();
}
return filterResults;
I have done my filtering...still it shows all the results instead of whatever i type in it...:(