I get correct objects in filtered list in publishResults() function but the filtered list is not displayed on the screen after filtering.
Following is the code for CityAdapter class inhereted from ArrayAdapter class.
IT seems like notifyDataSetChanged() isn't working?
Please inform why list is not displaying.What's wrong in the code.
import...
public class CityListAdapter extends ArrayAdapter<City> implements Filterable {
private ArrayList<City> cities;
private ArrayList<City> filtered_list;
public CityListAdapter(Context context, int resource, ArrayList<City>cities) {
super(context, resource, cities);
this.cities = cities;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, ViewGroup parent) {
Holder holder = new Holder();
if(convertView == null) {
LayoutInflater inflator = LayoutInflater.from(getContext());
convertView = inflator.inflate(R.layout.adapter_view,parent,false);
holder.box = (CheckBox) convertView.findViewById(R.id.checkBox);
holder.name = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(holder);
}
else {
holder = (Holder) convertView.getTag();
}
City city = getItem(position) ;
holder.name.setText(city.getName());
return convertView;
}
private class Holder {
CheckBox box;
TextView name;
}
#NonNull
#Override
public Filter getFilter() {
return new CityFilter();
}
private class CityFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
filtered_list = new ArrayList<>();
if (constraint != null && constraint.length()!= 0) {
for (int i = 0; i < cities.size(); i++) {
if (cities.get(i).getName().contains(constraint)) {
filtered_list.add(cities.get(i));
showMessage(cities.get(i).getName() + "added");
}
}
}
results.values = filtered_list;
results.count = filtered_list.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filtered_list = (ArrayList<City>) results.values;
int size =filtered_list.size();
showMessage("Data set changed"+String.format("%d", size));
notifyDataSetChanged();
}
}
public void showMessage(String message) {
Toast toast = Toast.makeText(getContext(), message, Toast.LENGTH_LONG);
toast.show();
}
}
You need to have a full list of items in the very beginning (i.e. in the constructor) assigned to the filtered list; so that they can have the entire list when there is no filtering of results (empty search) and that occurs whenever you instantiate the adapter.
public CityListAdapter(Context context, int resource, ArrayList<City>cities) {
super(context, resource, cities);
this.cities = cities;
this.filtered_list = cities; // <<<<< Change here
}
Side Note
Also override getCount() to avoid IndexOutOfBoundsException when reaching the end of the list on scroll whenever the original list size not equal to the filtered list size.
As the original list is cities, when you make filtering, its size will shrink down, so you need only to get the filtered list size not the original list size to avoid IndexOutOfBoundsException.
So add this to the adapter:
#Override
public int getCount() {
return filtered_list.size();
}
Related
Problem: Adding a list item prevents filter method from working. Otherwise the filter works correctly.
Expected: That the items and database are correctly updated from the both add item and filter methods.
Tested:
Filter on current list works and applies the layout.
Filter on filtered list works and applies the layout.
Filter with no constraint loads the full list and applies the layout.
Adding item before filter works and applies the layout.
Adding item after filter works and applies the layout.
Filter after adding item fails to filter results and applies no changes to the layout. No run time errors are provided.
Probable Solution: I thought I was missing an assignment to the items list to grab the updated version of the list. After checking it seems that both add item and filter methods are grabbing the updated list. I am starting to think I don't understand how the filter method works and that I am missing a method or line that the filter needs to refresh. Would appreciate suggestions on how to find what I am missing.
private void addProjectItem() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = getLayoutInflater().inflate(R.layout.project_add_layout, null);
final EditText title = view.findViewById(R.id.addProjectTitle);
final EditText description = view.findViewById(R.id.addProjectDescription);
builder.setNegativeButton(
android.R.string.cancel,
(dialog, which) -> dialog.cancel()
);
builder.setPositiveButton(
android.R.string.ok,
(dialog, which) -> {
if (!title.getText().toString().isEmpty() && !description.getText().toString().isEmpty()) {
ProjectItem item = new ProjectItem(
title.getText().toString(),
description.getText().toString(),
appDataManager.getUid(),
false
);
projectListManager.addItem(item);
adapter.updateItems(projectListManager.getList());
} else {
Toast.makeText(SecondActivity.this, projectAddError, Toast.LENGTH_SHORT).show();
}
}
);
builder.setView(view);
builder.show();
}
private class ProjectItemAdapter extends ArrayAdapter<ProjectItem> implements Filterable {
private Context context;
private List<ProjectItem> items;
private ImageButton projectCompleteButton;
private ImageButton projectDescriptionButton;
private TextView itemTitle;
private ImageButton projectJoinButton;
private ProjectItemAdapter( Context context, List<ProjectItem> items) {
super(context, -1, items);
this.context = context;
this.items = items;
}
#NonNull
#Override
public Filter getFilter() {
return projectFilter;
}
private final Filter projectFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<ProjectItem> found = new ArrayList<>();
if(constraint == null || constraint.length() == 0) {
found.addAll(projectListManager.getList());
} else {
String filterPattern = constraint.toString().toLowerCase().trim();
for(ProjectItem item : items){
if(item.getTitle().toLowerCase().contains(filterPattern)) {
found.add(item);
}
}
}
results.values = found;
results.count = found.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
addAll((List)results.values);
notifyDataSetChanged();
}
};
public void updateItems(List<ProjectItem> items) {
this.items = items;
notifyDataSetChanged();
}
#Override
public int getCount() {
return items.size();
}
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
// Removed as there is nothing that manipulates the list item.
return convertView;
}
}
}
Problem was with the publish results not referencing the items list and properly casting results as a collections.
Solution:
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
items.clear();
items.addAll((Collection<? extends ProjectItem>) results.values);
notifyDataSetChanged();
}
I have a Search filter which has to filter the items based on the text input. Each list item consists of two TextViews and one ImageView. I have attached adapters and the filter is working fine with the two textviews but the corresponding ImageViews are not getting filtered (the order of images is not changing).
I tried to change the code in several ways but its not working. In here, my Adapter extends ArrayAdapter. I tried using the BaseAdapter too, but still doesn't work
My Adapter class:
import de.hdodenhof.circleimageview.CircleImageView;
public class ChefSearchResultAdapter extends ArrayAdapter<ChefSearchItem> implements Filterable {
private ArrayList<ChefSearchItem> modelValues;
private List<ChefSearchItem> mOriginalValues;
private Context context;
private static class ViewHolder {
TextView userName;
TextView userAdmirers;
CircleImageView userProfileImage;
}
public ChefSearchResultAdapter(Context context, ArrayList<ChefSearchItem> chefs) {
super(context, 0, chefs);
modelValues = chefs;
this.context=context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
ChefSearchItem chef = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
final ViewHolder viewHolder; // view lookup cache stored in tag
if (convertView == null) {
// If there's no view to re-use, inflate a brand new view for row
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.chef_search_listitem, parent, false);
viewHolder.userName = convertView.findViewById(R.id.user_name);
viewHolder.userAdmirers = convertView.findViewById(R.id.user_admirers);
viewHolder.userProfileImage = convertView.findViewById(R.id.profile_image);
// Cache the viewHolder object inside the fresh view
convertView.setTag(viewHolder);
} else {
// View is being recycled, retrieve the viewHolder object from tag
viewHolder = (ViewHolder) convertView.getTag();
}
final StorageReference storageReference = FirebaseStorage.getInstance().getReference("Users").child(chef.userUID);
storageReference.child("Profile Pic").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
//Picasso.get().load(uri).into(viewHolder.userProfileImage);
Picasso.get()
.load(uri)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(viewHolder.userProfileImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError(Exception e) {
Picasso.get()
.load(uri)
.into(viewHolder.userProfileImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError(Exception e) {
Log.v("Picasso","Could not fetch image");
}
});
}
});
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
// Populate the data from the data object via the viewHolder object
// into the template view.
viewHolder.userName.setText(chef.userName);
viewHolder.userAdmirers.setText(chef.userAdmirers);
//Set on click to listitem
LinearLayout listItem = convertView.findViewById(R.id.chef_listitem);
listItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Navigate user to selected chef profile
Intent i=new Intent(context,ChefsViewActivity.class);
i.putExtra("UID",chef.userUID);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
});
// Return the completed view to render on screen
return convertView;
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
modelValues.clear();
modelValues.addAll((ArrayList<ChefSearchItem>) results.values);
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
// Holds the results of a filtering values
List<ChefSearchItem> FilteredArrList = new ArrayList<>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<>(modelValues); // saves
}
/********
*
* If constraint(CharSequence that is received) is null returns
* the mOriginalValues(Original) values else does the Filtering
* and returns FilteredArrList(Filtered)
*
********/
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
Locale locale = Locale.getDefault();
constraint = constraint.toString().toLowerCase(locale);
for (int i = 0; i < mOriginalValues.size(); i++) {
ChefSearchItem model = mOriginalValues.get(i);
String data = model.userName;
if (data.toLowerCase(locale).contains(constraint.toString())) {
FilteredArrList.add(model);
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
My ViewHolder:
public class ChefSearchItem {
public String userName;
public String userAdmirers;
public String userUID;
public ChefSearchItem(String userName, String userAdmirers, String userUID) {
this.userName = userName;
this.userAdmirers = userAdmirers;
this.userUID = userUID;
}
}
More simpler way to filter out data is filter them locally
In your activity where your searchview is on submit call this method and pass the search string as parameter.
Make a temporary list which will consist of filterdata.
filter(String filter){
ArrayList<ChefSearchItem> filtereModelValues = new ArrayList<ChefSearchItem>();
for (ChefSearchItem chefSearchItem : <LIST YOU ARE SENDING TO ADAPTER>){
if(filter.toLowerCase().equals(chechefSearchItem.userUID.toLowerCase())
filtereModelValues.add(chefSearchItem );
}
//outside the for loop send the filtered list to adapter by creating method inside adapter name "filter" and pass filterList as parameter.
YOUR_ADAPTER.filter(filtereModelValues);
}
And in your adapter
public void filter(ArrayList<ChefSearchItem> filterModelValues){
modelValues = filterModelValues;
notifyDataChanged();
}
I have a Listview with a Custom BaseAdapter. I have already done the code for my MainActivity.java to apply SearchView. However, I have problem with the performFiltering method in my Adapter class due to the reason that I have a ImageView that would be shown on my ListView. The ImageView is originally in the form of String because I would like to get the image from an url in my Firebase Storage and then convert it to Image by using the Picasso library.
TL:DR. My problem is that everytime I perform a search, the url of the image stored in Firebase is showing up instead of the actual Image. Help is appreciated thanks.
Below is my Adapter Class:
public class GridAdapter extends BaseAdapter implements Filterable {
CustomFilter mCustomFilter;
ArrayList<GridItem> filterList;
private Context mContext;
private ArrayList<GridItem> gtem;
//Constructor
public GridAdapter(Context mContext, ArrayList<GridItem> gtem) {
this.filterList = gtem;
this.mContext = mContext;
this.gtem = gtem;
}
#Override
public int getCount() {
return gtem.size();
}
#Override
public Object getItem(int position) {
return gtem.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = View.inflate(mContext,R.layout.grid_item, null);
ImageView putPic = (ImageView)v.findViewById(R.id.imageHere);
TextView putTitle = (TextView)v.findViewById(R.id.titleHere);
TextView putPrice = (TextView)v.findViewById(R.id.per_price);
TextView putCountry = (TextView)v.findViewById(R.id.countryHere);
//
putCountry.setText("Country: " + gtem.get(position).getCountry());
putTitle.setText(gtem.get(position).getmTitleHere());
putCountry.setText(gtem.get(position).getCountry());
putPrice.setText("Price: " + "$" + gtem.get(position).getmPriceHere());
Picasso.with(mContext).load(gtem.get(position).getmImageView()).resize(450 , 500).into(putPic);
return v;
}
#Override
public Filter getFilter() {
//To-do Auto-generated method stub
if (mCustomFilter == null )
{
mCustomFilter = new CustomFilter();
}
return mCustomFilter;
}
//Inner Class
class CustomFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null && constraint.length()>0)
{
//Constraint to Upper
constraint = constraint.toString().toUpperCase();
ArrayList<GridItem> filters = new ArrayList<GridItem>();
//to get specific items
for (int i = 0; i<filterList.size(); i++)
{
if (filterList.get(i).getmTitleHere().toUpperCase().contains(constraint))
{
GridItem g = new GridItem(filterList.get(i).getmTitleHere() , filterList.get(i).getmPriceHere(), filterList.get(i).getmImageView()
, filterList.get(i).getCountry());
filters.add(g);
}
}
results.count = filters.size();
results.values = filters;
}else {
results.count = filterList.size();
results.values = filterList;
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
gtem = (ArrayList<GridItem>) results.values;
notifyDataSetChanged();
}
}
}
After creating a list view containing over 100 items (111 to be precise) and deploying the app, it runs as expected but as soon as I scroll through my list, the app crashes and an ArrayIndexOutOfBoundsException is returned. I really don't understand why the length and index are returning '19'. Does anyone know why 19 is shown? What needs to be done to resolve this issue?
java.lang.ArrayIndexOutOfBoundsException: length=19; index=19
at com.helloapps.helloworldapp.adapters.OrangeListAdapter.getPositionForSection(OrangeListAdapter.java:160)
XML
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:choiceMode="singleChoice"
android:fastScrollEnabled="true"
android:scrollbarStyle="outsideInset"/>
Java
public class OrangeListAdapter extends BaseAdapter implements Filterable, SectionIndexer {
private List<Orange> mData;
private List<Orange> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
private Object[] mSections;
private int[] mSectionsIndexedByPosition;
private int[] mPositionsIndexedBySection;
public OrangeListAdapter (List<Orange> data, Context context) {
mData = data;
mFilteredData = new ArrayList(mData);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
setupSections();
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public Orange getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_dualline, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_name);
holder.description = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Orange orange = getItem(position);
holder.title.setText(orange.getName());
holder.description.setText(orange.getDescrption());
if (orange.isSelected()) {
convertView.setBackgroundColor(Color.parseColor("#FF6600"));
holder.title.setTextColor(Color.parseColor("#FFFFFF"));
holder.description.setTextColor(Color.parseColor("#FFFFFF"));
} else {
convertView.setBackgroundColor(Color.TRANSPARENT);
holder.title.setTextColor(Color.parseColor("#FFFFFF"));
holder.description.setTextColor(Color.parseColor("#B5B5B5"));
}
holder.title.setText(mFilteredData.get(position).getStation());
holder.description.setText(mFilteredData.get(position).getZone());
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView title;
private TextView description;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = new ArrayList(mData);
} else {
//Create a new list to filter on
List<Orange> resultList = new ArrayList<Orange>();
for (Orange str : mData) {
if (str.getStation().toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
mFilteredData.clear();
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<Orange>)results.values;
notifyDataSetChanged();
}
setupSections();
}
}
#Override
public int getPositionForSection(int section) {
return mPositionsIndexedBySection[section];
}
#Override
public int getSectionForPosition(int position) {
return mSectionsIndexedByPosition[position];
}
#Override
public Object[] getSections() {
return mSections;
}
private void setupSections() {
String initial = "\0";
List<String> sections = new ArrayList<String>();
mSectionsIndexedByPosition = new int[mFilteredData.size()];
mPositionsIndexedBySection = new int[mFilteredData.size()];
int section = 0;
for (int pos = 0; pos < mFilteredData.size(); pos++) {
Orange orange = mFilteredData.get(pos);
if (initial.charAt(0) != orange.getName().charAt(0)) {
initial = orange.getName().substring(0, 1);
section = sections.size();
sections.add(initial);
mPositionsIndexedBySection[section] = pos;
mSectionsIndexedByPosition[pos] = section;
} else {
mSectionsIndexedByPosition[pos] = section;
}
}
mSections = sections.toArray();
mPositionsIndexedBySection = Arrays.copyOf(mPositionsIndexedBySection, mSections.length);
}
}
Your array declared to have only 19 places, that means you can access maximum index of 18 as in arrays index starts with 0, but in above code you are accessing 19th index, then it will complain that your index is out of bound that is 18.
This line looks problematic to me:
mPositionsIndexedBySection = Arrays.copyOf(mPositionsIndexedBySection, mSections.length);
**To solve the problem**: Ensure that you are putting things in an array based on its size. If arrays has size n i.e. 19 then you are supposed to put element only till n-1 index i.e. 18.
Fast Scroller only read your sections from SectionIndexer.getSections() once, and will not change afterwards.
So the problem seems to occur when your first list contains over 19 sections, and afterwards reload a list where you want to reduce the number of sections to less than 19, which reduce the size of mPositionsIndexedBySection as well.
Because the Fast Scroller still remember the old sections, and try to get the position of the 19th section, error occurs.
To solve the problem, have fixed number of sections containing all posible section, only change the mPositionsIndexedBySection and mSectionsIndexedByPosition on data change.
Edit:
Check the link below on how to properly implements SectionIndexer
http://androidopentutorials.com/android-listview-fastscroll/
http://responsiveandroid.com/2013/04/13/android-sectionindexer-with-alphabet.html
I'm trying to get my list to show all my items again whenever I cancel a search from my search view but for some strange reason, the list gets stuck with the results only from the previous search. Does anyone know what is wrong with my code and how to fix this? I believe something is wrong with the filter related code but I don't know what it is.
FilterListFragment.java
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<Item> mData;
private List<Item> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<Item> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position).getItem();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_title);
holder.description = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(mData.get(position).getItem());
holder.description.setText(mData.get(position).getItemDescription());
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView title;
private TextView description;
}
/**
* Filter for filtering list items
*/
private class ItemFilter extends Filter {
/**
* Invoked on a background thread. This is where all the filter logic should go
* #param constraint the constraint to filter on
* #return the resulting list after applying the constraint
*/
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<Item> resultList = new ArrayList<Item>();
for (Item str : mData) {
if (str.getItem().toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<Item>)results.values;
notifyDataSetChanged();
}
}
}
}
List in normal state
List in filtered state
You are operating on the original data instead of filtered data. You should maintain a reference to original data and use the filtered data for all other purposes. So that the original data is displayed when search is cleared.
Replace all usages of mData with mFilteredData as below and only use the original data to generate the filtered data:
private List<String> mData;
private List<String> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<String> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String strItem = mFilteredData.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.mTvItem = (TextView) convertView.findViewById(R.id.tv_item);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTvItem.setText(strItem);
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView mTvItem;
}
/**
* Filter for filtering list items
*/
private class ItemFilter extends Filter {
/**
* Invoked on a background thread. This is where all the filter logic should go
* #param constraint the constraint to filter on
* #return the resulting list after applying the constraint
*/
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<String> resultList = new ArrayList<>();
for (String str : mData) {
if (str.toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<String>)results.values;
notifyDataSetChanged();
}
}
}
Add a log statement to onQueryTextChange() so you can see how your filter string is changing. Note that SearchView does not call onQueryTextChange() with an empty string when the search view is closed. You need to decide when you want the search string cleared, perhaps by adding a control for that, and do the clearing yourself.