I am using architecture component view model's LiveData to populate recycler view and want to add a searchview filter but couldn't find any solution. I tried to use filterable interface in the adapter but its also not working, possible because view model doesn't let it change.
my adapter is as-
public class NetworkAdapter extends RecyclerView.Adapter<NetworkAdapter.NetworksViewHolder> implements Filterable {
// Member variable to handle item clicks
final private ItemClickListener mItemClickListener;
// Class variables for the List that holds task data and the Context
private List<NetworkEntry> mNetworkEntries;
private List<NetworkEntry> mFilteredNetworkEntries = new ArrayList<>();
private Context mContext;
/**
* Constructor for the TaskAdapter that initializes the Context.
*
* @param context the current Context
* @param listener the ItemClickListener
*/
public NetworkAdapter(Context context, ItemClickListener listener) {
mContext = context;
mItemClickListener = listener;
}
/**
* Called when ViewHolders are created to fill a RecyclerView.
*
* @return A new TaskViewHolder that holds the view for each task
*/
@Override
public NetworksViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Inflate the task_layout to a view
View view = LayoutInflater.from(mContext)
.inflate(R.layout.network_list_item, parent, false);
return new NetworksViewHolder(view);
}
/**
* Called by the RecyclerView to display data at a specified position in the Cursor.
*
* @param holder The ViewHolder to bind Cursor data to
* @param position The position of the data in the Cursor
*/
@Override
public void onBindViewHolder(NetworksViewHolder holder, int position) {
// Determine the values of the wanted data
holder.onBindData(position);
}
/**
* Returns the number of items to display.
*/
@Override
public int getItemCount() {
if (mNetworkEntries == null) {
return 0;
}
return mNetworkEntries.size();
}
/**
* When data changes, this method updates the list of taskEntries
* and notifies the adapter to use the new values on it
*/
public void setNetworks(List<NetworkEntry> networkEntries) {
mNetworkEntries = networkEntries;
mFilteredNetworkEntries.addAll(mNetworkEntries);
notifyDataSetChanged();
}
public List<NetworkEntry> getNetworks() {
return mNetworkEntries;
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
String charString = charSequence.toString();
if (charString.isEmpty()) {
mFilteredNetworkEntries = mNetworkEntries;
} else {
List<NetworkEntry> filteredList = new ArrayList<>();
for (NetworkEntry row : mNetworkEntries) {
// name match condition. this might differ depending on your requirement
// here we are looking for name or phone number match
if (row.getNetworkName().toLowerCase().contains(charString.toLowerCase())) {
filteredList.add(row);
}
}
mFilteredNetworkEntries = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = mFilteredNetworkEntries;
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
mFilteredNetworkEntries = (ArrayList<NetworkEntry>) filterResults.values;
notifyDataSetChanged();
}
};
}
public interface ItemClickListener {
void onItemClickListener(NetworkData networkData);
}
public class NetworksViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView networkLogoImageView;
TextView networkNameTextView;
TextView joinTextView;
TextView commissionTextView;
TextView minPayTextView;
TextView payFrequencyTextView;
TextView offerTextView;
TextView likeTextView;
View containerView;
public NetworksViewHolder(View itemView) {
super(itemView);
networkLogoImageView = (ImageView) itemView.findViewById(R.id.iv_logo);
networkNameTextView = (TextView) itemView.findViewById(R.id.tv_network_name);
joinTextView = (TextView) itemView.findViewById(R.id.bt_join);
commissionTextView = (TextView) itemView.findViewById(R.id.iv_commission);
minPayTextView = (TextView) itemView.findViewById(R.id.tv_min_pay);
payFrequencyTextView = (TextView) itemView.findViewById(R.id.tv_pay_frequency);
offerTextView = (TextView) itemView.findViewById(R.id.iv_offers);
likeTextView = (TextView) itemView.findViewById(R.id.iv_likes);
containerView = itemView.findViewById(R.id.layoutContainer);
joinTextView.setOnClickListener(this);
containerView.setOnClickListener(this);
}
public void onBindData(int position) {
NetworkEntry networkEntry = mNetworkEntries.get(getAdapterPosition());
Picasso.with(mContext).load(networkEntry.getNetworkImageUrl()).into(networkLogoImageView);
networkNameTextView.setText(networkEntry.getNetworkName());
commissionTextView.setText(networkEntry.getNetworkCommission());
minPayTextView.setText(mContext.getString(R.string.tv_min_pay, networkEntry.getNetworkMinPay()));
payFrequencyTextView.setText(networkEntry.getNetworkPayFrequency());
offerTextView.setText(networkEntry.getNetworkOffers());
}
@Override
public void onClick(View view) {
int adapterPosition = getAdapterPosition();
NetworkEntry networkEntry = mNetworkEntries.get(adapterPosition);
if (view.getId() == R.id.bt_join) {
AppUtilities.openWebPage(mContext, networkEntry.getNetworkJoinUrl());
} else {
NetworkData networkData = new NetworkData(networkEntry);
Log.v(NetworkAdapter.class.getSimpleName(), "Network SNO : " + networkData.getSno());
mItemClickListener.onItemClickListener(networkData);
}
}
}
}
And search view OnQueryTextListener is as -
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.search_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setOnQueryTextListener(this);
searchView.setQueryHint("Search");
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onQueryTextSubmit(String query) {
adapter.getFilter().filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.getFilter().filter(newText);
return false;
}
The problem happens because you should be using the FilteredList in your adapter, not the original list:
public class NetworkAdapter extends RecyclerView.Adapter<NetworkAdapter.NetworksViewHolder> implements Filterable {
// Member variable to handle item clicks
final private ItemClickListener mItemClickListener;
// Class variables for the List that holds task data and the Context
private List<NetworkEntry> mNetworkEntries;
private List<NetworkEntry> mFilteredNetworkEntries = new ArrayList<>();
private Context mContext;
/**
* Constructor for the TaskAdapter that initializes the Context.
*
* @param context the current Context
* @param listener the ItemClickListener
*/
public NetworkAdapter(Context context, ItemClickListener listener) {
mContext = context;
mItemClickListener = listener;
}
/**
* Called when ViewHolders are created to fill a RecyclerView.
*
* @return A new TaskViewHolder that holds the view for each task
*/
@Override
public NetworksViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Inflate the task_layout to a view
View view = LayoutInflater.from(mContext)
.inflate(R.layout.network_list_item, parent, false);
return new NetworksViewHolder(view);
}
/**
* Called by the RecyclerView to display data at a specified position in the Cursor.
*
* @param holder The ViewHolder to bind Cursor data to
* @param position The position of the data in the Cursor
*/
@Override
public void onBindViewHolder(NetworksViewHolder holder, int position) {
// Determine the values of the wanted data
holder.onBindData(position);
}
/**
* Returns the number of items to display.
*/
@Override
public int getItemCount() {
if (mFilteredNetworkEntries == null) {
return 0;
}
return mFilteredNetworkEntries.size();
}
/**
* When data changes, this method updates the list of taskEntries
* and notifies the adapter to use the new values on it
*/
public void setNetworks(List<NetworkEntry> networkEntries) {
mNetworkEntries = networkEntries;
mFilteredNetworkEntries.addAll(mNetworkEntries);
notifyDataSetChanged();
}
public List<NetworkEntry> getNetworks() {
return mFilteredNetworkEntries;
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
String charString = charSequence.toString();
List<NetworkEntry> filteredList = new ArrayList<>();
if (charString.isEmpty()) {
filteredList = mNetworkEntries;
} else {
for (NetworkEntry row : mNetworkEntries) {
// name match condition. this might differ depending on your requirement
// here we are looking for name or phone number match
if (row.getNetworkName().toLowerCase().contains(charString.toLowerCase())) {
filteredList.add(row);
}
}
}
FilterResults filterResults = new FilterResults();
filterResults.values = filteredList;
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
mFilteredNetworkEntries = (ArrayList<NetworkEntry>) filterResults.values;
notifyDataSetChanged();
}
};
}
public interface ItemClickListener {
void onItemClickListener(NetworkData networkData);
}
public class NetworksViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView networkLogoImageView;
TextView networkNameTextView;
TextView joinTextView;
TextView commissionTextView;
TextView minPayTextView;
TextView payFrequencyTextView;
TextView offerTextView;
TextView likeTextView;
View containerView;
public NetworksViewHolder(View itemView) {
super(itemView);
networkLogoImageView = (ImageView) itemView.findViewById(R.id.iv_logo);
networkNameTextView = (TextView) itemView.findViewById(R.id.tv_network_name);
joinTextView = (TextView) itemView.findViewById(R.id.bt_join);
commissionTextView = (TextView) itemView.findViewById(R.id.iv_commission);
minPayTextView = (TextView) itemView.findViewById(R.id.tv_min_pay);
payFrequencyTextView = (TextView) itemView.findViewById(R.id.tv_pay_frequency);
offerTextView = (TextView) itemView.findViewById(R.id.iv_offers);
likeTextView = (TextView) itemView.findViewById(R.id.iv_likes);
containerView = itemView.findViewById(R.id.layoutContainer);
joinTextView.setOnClickListener(this);
containerView.setOnClickListener(this);
}
public void onBindData(int position) {
NetworkEntry networkEntry = mFilteredNetworkEntries.get(getAdapterPosition());
Picasso.with(mContext).load(networkEntry.getNetworkImageUrl()).into(networkLogoImageView);
networkNameTextView.setText(networkEntry.getNetworkName());
commissionTextView.setText(networkEntry.getNetworkCommission());
minPayTextView.setText(mContext.getString(R.string.tv_min_pay, networkEntry.getNetworkMinPay()));
payFrequencyTextView.setText(networkEntry.getNetworkPayFrequency());
offerTextView.setText(networkEntry.getNetworkOffers());
}
@Override
public void onClick(View view) {
int adapterPosition = getAdapterPosition();
NetworkEntry networkEntry = mFilteredNetworkEntries.get(adapterPosition);
if (view.getId() == R.id.bt_join) {
AppUtilities.openWebPage(mContext, networkEntry.getNetworkJoinUrl());
} else {
NetworkData networkData = new NetworkData(networkEntry);
Log.v(NetworkAdapter.class.getSimpleName(), "Network SNO : " + networkData.getSno());
mItemClickListener.onItemClickListener(networkData);
}
}
}
}
The idea is that in your adapter you use the filtered list, the original list is in the adapter only to serve the purpose of being filtered by the performFiltering
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With