RecyclerView (Custom Adapter) not refreshing the List on search

153 Views Asked by At

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();

    }
};

}

1

There are 1 best solutions below

2
SpiritCrusher On

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 complete and filtered .

The main culprit i thing is You have initialized Adapter as Global variable but the adapter you are setting to RecyclerView is a another one which you have created locally . Fix this and it should work .

private Consultant_RecyclerAdapter adapter ; // Global variable


private void setUpAdapter() {
Log.i("Adapter","Started");
setOnClickListener();
adapter = new Consultant_RecyclerAdapter(consultants,listener);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
Log.i("Adapter","Ended");
}