Reputation: 561
i'm trying to implement search filter into my RecyclerView, just as in this post
I've debugged it and it does filter the items as expected, but the list does not seems to change in the application. I.E.: i filter for a name and there are 7 results, and in the screen it is the original list.
My onCreateMenuOptions on activity is:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
final MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override public boolean onQueryTextSubmit(String filterString) {
adapter.getFilter().filter(filterString);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
return true;
}
my adapter looks like this:
public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHolder> implements INameableAdapter {
private List<Anime> items;
private List<Anime> filteredItems;
private AnimeFilter animeFilter;
final private Context context;
@Override
public Character getCharacterForElement(int element) {
Character c = items.get(element).getTitle().charAt(0);
if(Character.isDigit(c)){
c = '#';
}
return c;
}
public static class AnimeViewHolder extends RecyclerView.ViewHolder {
// Campos respectivos de un item
final public ImageView img;
final public TextView title;
public AnimeViewHolder(View v) {
super(v);
img = (ImageView) v.findViewById(R.id.image);
title = (TextView) v.findViewById(R.id.title);
}
}
public AnimeAdapter(List<Anime> items, Context context) {
this.items = items;
this.context = context;
this.filteredItems = new ArrayList<>();
}
@Override
public int getItemCount() {
return items.size();
}
@Override
public AnimeViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.anime_card, viewGroup, false);
return new AnimeViewHolder(v);
}
@Override
public void onBindViewHolder(AnimeViewHolder viewHolder, int i) {
if (BuildConfig.DEBUG) {
//This is made to check if the image cache is working as expected.
Picasso.with(context).setIndicatorsEnabled(true);
Picasso.with(context).setLoggingEnabled(true);
}
final int position = i;
final ImageView imageView = viewHolder.img;
Picasso.with(context)
.load(items.get(i).getImageUrl())
.into(imageView);
viewHolder.title.setText(items.get(i).getTitle());
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
Intent intent = new Intent(context, TabActivity.class);
Bundle b = new Bundle();
b.putString("title", items.get(position).title);
b.putString("playlist", items.get(position).playlist);
imageView.buildDrawingCache();
Bitmap image = imageView.getDrawingCache();
b.putParcelable("imageBitmap", image);
intent.putExtras(b);
v.getContext().startActivity(intent);
}
});
}
public Filter getFilter() {
if(animeFilter == null)
animeFilter = new AnimeFilter(this, items);
return animeFilter;
}
private static class AnimeFilter extends Filter {
private final AnimeAdapter adapter;
private final List<Anime> originalList;
private final List<Anime> filteredList;
private AnimeFilter(AnimeAdapter adapter, List<Anime> originalList) {
super();
this.adapter = adapter;
this.originalList = new LinkedList<>(originalList);
this.filteredList = new ArrayList<>();
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
filteredList.clear();
final FilterResults results = new FilterResults();
if (constraint.length() == 0) {
filteredList.addAll(originalList);
} else {
final String filterPattern = constraint.toString().toLowerCase().trim();
for (final Anime anime : originalList) {
if (anime.getTitle().toLowerCase().contains(filterPattern)) {
filteredList.add(anime);
}
}
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
adapter.filteredItems.clear();
adapter.filteredItems.addAll((ArrayList<Anime>) results.values);
adapter.notifyDataSetChanged();
}
}
}
What i'm forgetting here?
Thanks in advance!
Upvotes: 0
Views: 890
Reputation: 133
The problem is that you populate your RecyclerView
with the items from the items
list instead of filteredItems
.
In your filter you only update the filteredItems
list and notify the adapter that something has changed. But in fact the list that the adapter uses to populate the RecyclerView
stays the same.
So change your adapter to always use the filtered list.
Upvotes: 2
Reputation: 3273
I do some changes to your code.
Check this:
public class AnimeAdapter extends RecyclerView.Adapter<AnimeAdapter.AnimeViewHolder> implements INameableAdapter {
private List<Anime> items;
private List<Anime> filteredItems;
private AnimeFilter animeFilter;
final private Context context;
@Override
public Character getCharacterForElement(int element) {
Character c = items.get(element).getTitle().charAt(0);
if(Character.isDigit(c)){
c = '#';
}
return c;
}
public static class AnimeViewHolder extends RecyclerView.ViewHolder {
// Campos respectivos de un item
final public ImageView img;
final public TextView title;
public AnimeViewHolder(View v) {
super(v);
img = (ImageView) v.findViewById(R.id.image);
title = (TextView) v.findViewById(R.id.title);
}
}
public AnimeAdapter(List<Anime> items, Context context) {
this.items = items;
this.context = context;
this.filteredItems = new ArrayList<>();
// we copy the original list to the filter list and use it for setting row values
this.filteredItems.addAll(items);
}
@Override
public int getItemCount() {
return filteredItems.size();//you must always return count of filtered rows
}
@Override
public AnimeViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.anime_card, viewGroup, false);
return new AnimeViewHolder(v);
}
@Override
public void onBindViewHolder(AnimeViewHolder viewHolder, int i) {
if (BuildConfig.DEBUG) {
//This is made to check if the image cache is working as expected.
Picasso.with(context).setIndicatorsEnabled(true);
Picasso.with(context).setLoggingEnabled(true);
}
final int position = i;
final ImageView imageView = viewHolder.img;
Picasso.with(context)
.load(filteredItems.get(i).getImageUrl())//we you filteredItems instead of items
.into(imageView);
viewHolder.title.setText(filteredItems.get(i).getTitle());
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
Intent intent = new Intent(context, TabActivity.class);
Bundle b = new Bundle();
b.putString("title", filteredItems.get(position).title);
b.putString("playlist", filteredItems.get(position).playlist);
imageView.buildDrawingCache();
Bitmap image = imageView.getDrawingCache();
b.putParcelable("imageBitmap", image);
intent.putExtras(b);
v.getContext().startActivity(intent);
}
});
}
public Filter getFilter() {
if(animeFilter == null)
animeFilter = new AnimeFilter(this, items);
return animeFilter;
}
private static class AnimeFilter extends Filter {
private final AnimeAdapter adapter;
private final List<Anime> originalList;
private final List<Anime> filteredList;
private AnimeFilter(AnimeAdapter adapter, List<Anime> originalList) {
super();
this.adapter = adapter;
this.originalList = new LinkedList<>(originalList);
this.filteredList = new ArrayList<>();
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
filteredList.clear();
final FilterResults results = new FilterResults();
if (constraint.length() == 0) {
filteredList.addAll(originalList);
} else {
final String filterPattern = constraint.toString().toLowerCase().trim();
for (final Anime anime : originalList) {
if (anime.getTitle().toLowerCase().contains(filterPattern)) {
filteredList.add(anime);
}
}
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
adapter.filteredItems.clear();
adapter.filteredItems.addAll((ArrayList<Anime>) results.values);
adapter.notifyDataSetChanged();
}
}
}
Upvotes: 3