How to filter a RecyclerView with a SearchView(android.support.v7.widget.SearchView) which is not included in toolbar?
I want to filter the results in my recycler adapter using names which i am getting from api in a model.
I have set the recyclerview inside a fragment.
you can simply do it by searchview or edit text
for search view
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String text) {
return false;
}
#Override
public boolean onQueryTextChange(String text) {
filter(s.toString(),getCustomDeviceCheckItemsList());
return true;
}
});
private void filter(String text,ArrayList<CustomCheckItem> names) {
//new array list that will hold the filtered data
ArrayList<CustomCheckItem> filterdNames = new ArrayList<>();
//looping through existing elements
for (CustomCheckItem s : names) {
//if the existing elements contains the search input
if (s.getTitle().toLowerCase().contains(text.toLowerCase())) {
//adding the element to filtered list
filterdNames.add(s);
}
}
//calling a method of the adapter class and passing the filtered list
childAdapter.filterList(filterdNames);
}
and in adapter you have to write
public void filterList(ArrayList<CustomItem> filterdNames) {
this.list = filterdNames;
notifyDataSetChanged();
}
and if you want to do it by edit text then
search.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
filter(s.toString(),getCustomDeviceCheckItemsList());
}
});
Related
I am using com.github.mancj:MaterialSearchBar:+ dependency in my app to search data in my SQLite database. It does load suggestions based on the data i have in the database but when a user clicks on one of the suggestion and confirms it by clicking the search icon in the keyboard nothing gets displayed, the adapter is not being called to display the result. below is my search activity
recyclerView = findViewById(R.id.hyme_list);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
materialSearchBar = findViewById(R.id.searchBar);
database = new Database(this);
materialSearchBar.setHint("Search here");
materialSearchBar.setCardViewElevation(10);
loadSuggestList();
materialSearchBar.addTextChangeListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
List<String> sugest = new ArrayList<>();
for (String search: suggestList) {
if (search.toUpperCase().contains(materialSearchBar.getText().toUpperCase()))
sugest.add(search);
}
materialSearchBar.setLastSuggestions(sugest);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
materialSearchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() {
#Override
public void onSearchStateChanged(boolean enabled) {
if (!enabled){
searchAdapter = new SearchAdapter(getBaseContext(),database.getSongs());
recyclerView.setAdapter(searchAdapter);
}
}
#Override
public void onSearchConfirmed(CharSequence text) {
startSearch(text.toString());
}
#Override
public void onButtonClicked(int buttonCode) {
}
});
searchAdapter = new SearchAdapter(this,database.getSongs());
recyclerView.setAdapter(searchAdapter);
}
private void startSearch(String text) {
searchAdapter = new SearchAdapter(this,database.getSongByName(text));
recyclerView.setAdapter(searchAdapter);
}
private void loadSuggestList() {
suggestList = database.getTitle();
materialSearchBar.setLastSuggestions(suggestList);
}
Friends , I have an edittext which is acting like a searchview.On text change,it is fetching data and storing in filterdNames but it is not updating the recyclerview.Kindly help, i am new to andriod.
Here is my recyclerview adapter code-
public class MyCategoryAdaptercheckbox extends RecyclerView.Adapter<MyCategoryAdaptercheckbox.ViewHolder> {
List<GetMyCategoryAdapter> getMyCategoryAdapter;
Context context;
List<String> category_name;
GetMyCategoryAdapter getMyCategoryAdapter1;
public MyCategoryAdaptercheckbox(List<GetMyCategoryAdapter> getMyCategoryAdapter, Context context, List<String> category_name) {
this.getMyCategoryAdapter = getMyCategoryAdapter;
this.context = context;
this.category_name = category_name;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
if (viewHolder instanceof ViewHolder){
}
getMyCategoryAdapter1 = getMyCategoryAdapter.get(i);
((ViewHolder) viewHolder).tv_categorytitle.setText(getMyCategoryAdapter1.getC_name());
((ViewHolder) viewHolder).tv_categoryid.setText(getMyCategoryAdapter1.getC_id());
((ViewHolder) viewHolder).gt= getMyCategoryAdapter1;
}
public void filterList(ArrayList<String> filterdNames) {
this.category_name = filterdNames;
notifyDataSetChanged();
}
}
Here is my GetMyCategoryAdapter class code -
public class GetMyCategoryAdapter {
String c_name,c_id;
public String getC_name() {
return c_name;
}
public void setC_name(String c_name) {
this.c_name = c_name;
}
public String getC_id() {
return c_id;
}
public void setC_id(String c_id) {
this.c_id = c_id;
}
}
And here is the fragment code -
searchView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
//after the change calling the method and passing the search input
filter(editable.toString());
}
});
private void filter(String text) {
//new array list that will hold the filtered data
ArrayList<String> filterdNames = new ArrayList<>();
//looping through existing elements
for (String s : category_name) {
//if the existing elements contains the search input
if (s.toLowerCase().contains(text.toLowerCase())) {
//adding the element to filtered list
filterdNames.add(s);
}
}
//calling a method of the adapter class and passing the filtered list
((MyCategoryAdaptercheckbox) MyAdapter).filterList(filterdNames);
}
The problem is in filterList method:
you update category_name, but not getMyCategoryAdapter (which is used in onBindViewHolder). Try change getMyCategoryAdapter as well before notifyDataSetChanged
I am using recyclerview with checkbox and search filter. The truble start when i search using filter on recyclerview. for example i have 3 items on recycler view (A,B,C). first time i check item A , and then i search for item B and Check Item B and when i delete the query on search and back to recyclerview (with notifydatasetchanged() to refresh the recyclerview) there is nothing wrong like this
checked
but when i uncheck both items (items A and B), and then i search item c and check the item c, THE FIRST ITEM CHECKED (in here A ) always checked but there is no item A on checkedarraylist
checked but no exist
this is the model for the list in rectclerview
public class herbsModel {
private boolean isSelected;
private String idHerbs,nameHerbs;
public herbsModel(String idHerbs, String nameHerbs) {
this.idHerbs = idHerbs;
this.nameHerbs = nameHerbs;
}
public String getIdHerbs() {
return idHerbs;
}
public String getNameHerbs() {
return nameHerbs;
}
public boolean getSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
this is the search from edittext on mainactivity
search.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
});
private void filter(String s) {
ArrayList<herbsModel> filteredlist = new ArrayList<>();
for (herbsModel item : herbsModels)
{
if(item.getNameHerbs().toLowerCase().contains(s.toLowerCase()))
{
filteredlist.add(item);
}
}
adapter.filterlist(filteredlist);
}
this is how to check the checked items
sb = new StringBuffer();
for (herbsModel h : adapter.checkedHerbs)
{
sb.append(h.getNameHerbs());
sb.append("\n");
}
if (adapter.checkedHerbs.size()>0)
{
Toast.makeText(getActivity(),sb.toString(),Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getActivity(),"Please Check Plants",Toast.LENGTH_SHORT).show();
}
this is the onbindviewholder on adapter that use logic to get checked or unchecked item and store checked item to new array and set checked the items
#Override
public void onBindViewHolder(#NonNull final herbsPredictViewHolder herbsPredictViewHolder, int i) {
final herbsModel detailherbs = herbsModelList.get(i);
herbsPredictViewHolder.name.setText(detailherbs.getNameHerbs());
if(detailherbs.getSelected()==true)
{
herbsPredictViewHolder.checkBox.setChecked(true);
}
else{
herbsPredictViewHolder.checkBox.setChecked(false);
herbsPredictViewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onItemClick(View v, int pos) {
CheckBox checkBox = (CheckBox) v;
if (checkBox.isChecked())
{
checkedHerbs.add(herbsModelList.get(pos));
detailherbs.setSelected(true);
}
else if(!checkBox.isChecked())
{
checkedHerbs.remove(herbsModelList.get(pos));
detailherbs.setSelected(false);
}
}
});
}
}
this is the filter function on adapter
public void filterlist (ArrayList<herbsModel> filteredList)
{
herbsModelList =filteredList;
notifyDataSetChanged();
}
The problem is start after check something from searchview and then when recyclerview refresh all of my unchecked items become checked even it is null on checkeditems array list. If i not using the search it works fine
CheckBox checkBox = (CheckBox) v;
if (checkBox.isChecked())
{
checkedHerbs.add(herbsModelList.get(pos));
detailherbs.setSelected(true);
}
else if(!checkBox.isChecked())
{
checkedHerbs.remove(herbsModelList.get(pos));
detailherbs.setSelected(false);
}
I think you need to update this into this
if (!detailherbs.getSelected())
{
checkedHerbs.add(herbsModelList.get(pos));
detailherbs.setSelected(true);
}
else
{
checkedHerbs.remove(herbsModelList.get(pos));
detailherbs.setSelected(false);
}
And update your else code into
else
herbsPredictViewHolder.checkBox.setChecked(false);
herbsPredictViewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onItemClick(View v, int pos) {
CheckBox checkBox = (CheckBox) v;
if (checkBox.isChecked())
{
checkedHerbs.add(herbsModelList.get(pos));
detailherbs.setSelected(true);
}
else if(!checkBox.isChecked())
{
checkedHerbs.remove(herbsModelList.get(pos));
detailherbs.setSelected(false);
}
}
});
This sounds easy but it has been hard for me. I have an autocomplete textview that shows user location address. I also implemented a places api to get addresses if user enters a different location other than their location. Everything is working like it is supposed to but the places result is still showing even when there is already an address. To reduce cost I would like to get address results only when the user enters an address. I made a global boolean and set it true when the text is changed like so:
autocomplete.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
isTextEntered = true; //to get autocomplete get results only after user enters text
}
#Override
public void afterTextChanged(Editable s) {
}
});
Then I check if the boolean is true when I set my adapter as such:
if (isTextEntered) {
autocomplete.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
autocomplete.setText(selectedPlace);
}
});
}
But doing this in oncreate method of my activity simply blocks the autocomplete from showing any places hint results. How can I accomplish my goal here? As always any help is much appreciated.
This is my custom adapter class:
class GooglePlacesAutocompleteAdapter extends ArrayAdapter implements Filterable {
private ArrayList resultList;
//private ArrayList<HashMap<String, Place>> results = new ArrayList<>();
public GooglePlacesAutocompleteAdapter(Context context, int list, int textViewResourceId) {
super(context, list, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.get(index).toString();
}
//#Override
//public HashMap<String, Place> getItem(int index) {return results.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;
}
}
Here is my idea to achieve this.
complete.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 1. init autocomplete
// 2. show autocomplete
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 1. get enter text
// 2. update the datasource of autocomplete adapter
}
#Override
public void afterTextChanged(Editable s) {
// hide the autocomplete
}
});
Hope it help
What I will suggest to you is just refactor it as a method like here
public void setSelectedPlace() {
autocomplete.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
autocomplete.setText(selectedPlace);
}
});
}
And call it inside afterTextChanged method like here, and hide the autocomplete after that
#Override
public void afterTextChanged(Editable s) {
setSelectedPlace();
// hide the autocomplete
}
I have a ListView that contains an EditText to filter list result. Everything is working well during this phase.
My problem is after clicking an item in the list and then go back to the ListView, the item in the list is gone, most probably the because EditText contains an empty space after back button is pressed. Why is this so?
I've put a Log inside onTextChanged to see what character is being put when the back button is pressed. It's just blank. I'm not sure why this listener adds an empty space and filter the result after back button is pressed.
Here is the code in ListFragment:
public class CategoryFragment extends ListFragment {
...
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// filter result
etFilterCategory.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
// Log.d(TAG, "ontextchanged= " + charSequence);
va.getFilter().filter(charSequence);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
}
The BaseAdapter implementing Filterable:
public class CategoryAdapter extends ArrayAdapter<CategoryModel> implements
Filterable {
...
#Override
public Filter getFilter() {
return mFilter;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final ArrayList<CategoryModel> list = originalData;
int count = list.size();
final ArrayList<CategoryModel> nlist = new ArrayList<CategoryModel>(count);
String filterableString;
for (int i = 0; i < count; i++) {
filterableString = list.get(i).getName();
if(filterableString.toLowerCase().contains(filterString)) {
// add the whole custom row
nlist.add(list.get(i));
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
arrCategory = (ArrayList<CategoryModel>) results.values;
notifyDataSetChanged();
}
}
}
How do I NOT make the ListView item filtered after every back button pressed?
EDIT:
After back button is pressed, the only way to make the ListView visible is to add a space into the EditText and remove it. That will show the previous list. What did I missed?
you may be re-creating your listAdapter object, this will nullify the previously created listAdapter.
Your call of textwatcher is pointing to older listadapter where in onResume/OnCreateView event your new instance of listadapter is generated.
I solved my problem by declaring listadapter object as member in my fragment and checking it in onResume method as follow
#Override
public void onResume() {
super.onResume();
if (news == null) {
if(CommonFun.isInternetOn(activity))
new NewsRetrivalTask().execute();
else
ToastMsg.showToast(activity, Msg.alertInternetMsg);
} else {
newsListAdapter = new NewsListAdapter(getActivity(), news);
lv.setAdapter(newsListAdapter);
lv.setOnItemClickListener(MAP_FragmentChatRoom.this);
lv.setVisibility(View.VISIBLE);
tvNoRecord.setVisibility(View.GONE);
}
}
and textwatcher in onCreateView method
etSearch.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) {
newsListAdapter.getFilter().filter(s);
}
});