this is my first question on Stackoverflow. I have very recently started Android Development.
What I want to do:
I have a list of people and I want to filter/Search the RecyclerView (using Custom Adapter) list (Initial List) based on user input in the search box, in my toolbar(not ActionBar).
My Problem:
Initially, nothing was happening. The list was not changing at all. Now, the list changes, but only one fixed list item (Current Search output) gets displayed no matter what the input. My recycler adapter is not refreshing to the new filtered list. And I am not getting any errors in the bulid or the logcat.
What I have tried:
After debugging for a whole day, and fixing some "NullPointerExceptions" in my Layout Manager and the RecycleView adapter and having tried 2 different method for filtering. Even though I am able to generate a filtered array (List values in Logcat) I am not able to get the results. I tried using , the "notifyDataSetChanged()", "notifyItemRangeChanged()" and "*notifyAll();, but all in vain....
Please help.....
public class MainActivity extends AppCompatActivity {
ArrayList<Consultant> consultants= new ArrayList<>();
ArrayList<Consultant> allConsultants = new ArrayList<>();
int rating=5;
int price = 10000;
String categoryFilter;
String ratingFilter;
RecyclerView recyclerView;
Consultant_RecyclerAdapter.RecyclerViewClickListener listener;
Consultant_RecyclerAdapter adapter= new Consultant_RecyclerAdapter(consultants,listener);
Spinner s_categories;
Spinner s_rating;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recyclerView= findViewById(R.id.RecycleView);
s_categories=findViewById(R.id.Spinner_Category);
s_rating=findViewById(R.id.Spinner_Rating);
addToList();
setUpAdapter();
}
private void setUpAdapter() {
Log.i("Adapter","Started");
setOnClickListener();
Consultant_RecyclerAdapter adapter = new Consultant_RecyclerAdapter(consultants,listener);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
Log.i("Adapter","Ended");
}
private void addToList(){
consultants.add(new Consultant("Dr Kapoor", "consultant_id", "profilePic_src", new String[]{"Dentist","Dermatology"}, 1000, 5));
consultants.add(new Consultant("Dr Pankaj Kumar", "consultant_id", "profilePic_src", new String[]{"Psychology","Respiratory"}, 3000, 1));
consultants.add(new Consultant("Dr Harsh Jha", "consultant_id", "profilePic_src", new String[]{"Internal Medicine"}, 1500, 5));
consultants.add(new Consultant("Dr Gaurav Singh", "consultant_id", "profilePic_src", new String[]{"Dentist","Cardiologist"}, 3500, 4));
consultants.add(new Consultant("Dr Aman", "consultant_id", "profilePic_src", new String[]{"Lungs","Neuro"}, 1000, 2));
consultants.add(new Consultant("Dr Jatin Chabra", "consultant_id", "profilePic_src", new String[]{"Psychology"}, 5000, 3));
Log.i("List","Done");
allConsultants=new ArrayList<>(consultants);
}
private void setOnClickListener() {
listener = new Consultant_RecyclerAdapter.RecyclerViewClickListener() {
@Override
public void onClick(View v, int position) {
Intent intent = new Intent(getApplicationContext(), ConsultantProfileActivity.class);
intent.putExtra("consultant_name", consultants.get(position).getName());
intent.putExtra("pic_src", consultants.get(position).getProfilePic_src());
intent.putExtra("about", consultants.get(position).getAbout());
intent.putExtra("consultant_id",consultants.get(position).getConsultant_id());
intent.putExtra("categories",consultants.get(position).getCategories());
startActivity(intent);
}
};
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search, menu);
MenuItem menuItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) menuItem.getActionView();
searchView.setQueryHint("Search....");
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
Log.i("Search","QueryChanged");
//adapter.getFilter().filter(newText);
filter(newText);
return false ;
}
});
return true;
}
private void filter(String text) {
// creating a new array list to filter our data.
ArrayList<Consultant> filteredlist = new ArrayList<>();
// running a for loop to compare elements.
for (Consultant item : allConsultants) {
Log.i("Filter: ",item.getName()+"...."+text);
// checking if the entered string matched with any item of our recycler view.
if (item.getName().toLowerCase().contains(text.toLowerCase())) {
Log.i("Match","Found");
// if the item is matched we are
// adding it to our filtered list.
filteredlist.add(item);
}
}
Log.i("Filter: ",(filteredlist.toString()));
if (filteredlist.isEmpty()) {
// if no item is added in filtered list we are
// displaying a toast message as no data found.
Toast.makeText(this, "No Data Found..", Toast.LENGTH_SHORT).show();
} else {
// at last we are passing that filtered
// list to our adapter class.
adapter.filterList(filteredlist);
}
}
}
Please, ignore the search method in the following Recycler Adapter, I am currently using the search method in the mainActivity
public class Consultant_RecyclerAdapter extends RecyclerView.Adapter<Consultant_RecyclerAdapter.MyViewHolder>{ ArrayList consultants; ArrayList AllConsultants,preFilteredList;
private RecyclerViewClickListener listener;
public Consultant_RecyclerAdapter(ArrayList<Consultant> consultants, RecyclerViewClickListener listener) {
this.consultants = consultants;
this.listener = listener;
//AllConsultants = new ArrayList<>(consultants);
//preFilteredList = new ArrayList<>(consultants);
}
//NOTIFYING THE ADAPTER ABOUT UPDATE
public void filterList(ArrayList<Consultant> filteredlist) {
consultants.clear();
consultants.addAll(filteredlist);
notifyDataSetChanged();
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView name, rating, price, category;
private ImageView profile;
public MyViewHolder(@NonNull View view) {
super(view);
name = view.findViewById(R.id.textView_Name);
profile = view.findViewById(R.id.imageView_ProfilePic);
category = view.findViewById(R.id.textView_Category);
rating = view.findViewById(R.id.textView_Rating);
price = view.findViewById(R.id.textView_Price);
view.setOnClickListener(this);
}
@Override
public void onClick(View view) {listener.onClick(view, getAdapterPosition()); }
}
@NonNull
@Override
public Consultant_RecyclerAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.consultant_list_item, parent, false);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
String name = consultants.get(position).getName();
String profilePic_src = consultants.get(position).getProfilePic_src();
float rating = consultants.get(position).getRating();
String categories = "";
//Converting Array to string
for (String i : consultants.get(position).getCategories()) {
categories = i + ", " + categories;
}
int price = consultants.get(position).getPrice();
holder.name.setText(name);
holder.category.setText(categories);
holder.price.setText(String.valueOf(price));
holder.rating.setText(String.valueOf(rating));
holder.profile.setImageResource(R.drawable.consultant);}
@Override
public int getItemCount() {
return consultants.size();
}
public interface RecyclerViewClickListener {
void onClick(View v, int position);
}
public void listUpdate(String categoryFilter, String ratingFilter) {
preFilteredList = new ArrayList<>();
if (categoryFilter != null || ratingFilter != null) {
for (Consultant item : AllConsultants) {
if (String.valueOf(item.getRating()).toLowerCase(Locale.ROOT).contains(ratingFilter) && item.getCategories().toString().toLowerCase(Locale.ROOT).contains(categoryFilter)) {
preFilteredList.add(item);
}
}
}else{
preFilteredList= new ArrayList<Consultant>(AllConsultants);
}
consultants.clear();
consultants.addAll(preFilteredList);
getFilter();
}
@Override
public Filter getFilter() {
return theFilter;
}
public Filter theFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<Consultant> filteredList = new ArrayList<>();
if (constraint == null || constraint.length() == 0) {
filteredList.addAll(AllConsultants);
} else {
String filterPattern = constraint.toString().toLowerCase().trim();
for (Consultant item : AllConsultants) {
Log.i("Filter: ",item.getName()+"...."+filterPattern);
if (item.getName().toLowerCase().contains(filterPattern)) {
Log.i("Match","Found");
filteredList.add(item);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
Log.i("Filter ","Published");
consultants.clear();
consultants.addAll((List) results.values);
notifyDataSetChanged();
}
};
}
Do not Use Activity To filter the Results this kinda degrade the performance . use Filterable interface in Adapter it performs filtering on a background thread and post the result on Main thread. Which you already implemented i see .
By looking at your code of adapter i think it should work because you are maintaining different references for Both list
completeandfiltered.The main culprit i thing is You have initialized Adapter as Global variable but the adapter you are setting to
RecyclerViewis a another one which you have created locally . Fix this and it should work .