19

I'm developing a search contact feature, in that screen, there is a RecyclerView inside NestedScrolView (fillViewport = true). Screen design: (This design is accepted by customer, I can't change it)
enter image description here
After loading all contacts of current device into an ArrayList, the search results are filtered from this array.
There is several cases that make the app very laggy:
1. When user type an input that have no result, then user clear search, I have to show all results again. The NestedScrollView has to render UI for all items of RecyclerView (for example: 300 items).
2. When the quantity of results has many changes (for example, from 1 to 300 items). The NestedScrollView has to render UI for a lot of items of RecyclerView

I know this design breaks recycling technique of RecyclerView, but I can't change it.
What I tried:

recyclerView.setNestedScrollingEnabled(false);

In AndroidManifest:

android:windowSoftInputMode="adjustNothing"

The adapter:

public class RecyclerContactAdapter extends RecyclerView.Adapter<RecyclerContactAdapter.ViewHolder> {

    private List<MobileContact> contacts;
    private Context context;

    public RecyclerContactAdapter() {
        contacts = new ArrayList<>();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        this.context = parent.getContext();
        View view = LayoutInflater.from(context)
                .inflate(R.layout.item_recycler_contact, parent, false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //set data for view
    }

    @Override
    public int getItemCount() {
        return contacts.size();
    }

    protected class ViewHolder extends RecyclerView.ViewHolder {
        private TextView tvAlphabetHeader;
        private CircleImageView civAvatar;
        private TextView tvContactName;
        private TextView tvStatus;
        private CheckBox cbInvited;
        private RelativeLayout rlAlphabetHeader;
        private RelativeLayout rlContainer;

        protected ViewHolder(View itemView) {
            super(itemView);
            tvAlphabetHeader = itemView.findViewById(R.id.item_recycler_contact_tv_alphabet_header);
            civAvatar = itemView.findViewById(R.id.item_recycler_contact_civ_avatar);
            tvContactName = itemView.findViewById(R.id.item_recycler_contact_tv_name);
            tvStatus = itemView.findViewById(R.id.item_recycler_contact_tv_status);
            cbInvited = itemView.findViewById(R.id.item_recycler_contact_cb_contact);
            rlAlphabetHeader =  itemView.findViewById(R.id.item_recycler_contact_rl_alphabet);
            rlContainer = itemView.findViewById(R.id.item_recycler_contact_rl_contact);
        }
    }

    public void addAll(List<MobileContact> mobileContacts) {
        this.contacts.clear();
        this.contacts.addAll(mobileContacts);
        notifyDataSetChanged();
    }

    public void add(MobileContact mobileContact) {
        this.contacts.add(mobileContact);
    }

    public List<MobileContact> getContacts() {
        return this.contacts;
    }

}
Phong Nguyen
  • 6,897
  • 2
  • 26
  • 36

1 Answers1

19

You are using RecyclerView incorrectly. Instead of putting your RecyclerView inside NestedScrollView put your "Header" and "Search box" inside RecyclerView as different view types.

This answer is a good example.

Okas
  • 2,664
  • 1
  • 19
  • 26
  • this method sounds appropriate, but there will be 4 different view types: header (TextView), a button, a search box, normal item, it's complicated when handle all events for each. – Phong Nguyen Dec 18 '17 at 09:01
  • It only seems complicated at first. Actually this is standard practice. Also looking at your ui it seems you can put your header, button and search box together in one layout, so it will be only two view types. – Okas Dec 18 '17 at 09:02
  • oh I see, I wish I could show you the exact screen design. I will try your solution – Phong Nguyen Dec 18 '17 at 09:09
  • 1
    What if I have a fragment over the List, I cannot move all the logic from the fragment to the header in the adapter? And fragments cannot be used inside adapters. – David Jan 18 '18 at 13:12
  • @David, you probably have to redesign. – Okas Jan 18 '18 at 13:59
  • What if the recyclerview has GridLayoutManager?!! How to make header for example be horizontal? – hiddeneyes02 Nov 30 '18 at 21:51
  • @hiddeneyes02 for GridLayoutManager you can configer span amount depending of view type – BekaBot Sep 22 '20 at 05:08
  • ConcatAdapter would fit perfectly for this situation since you will have two adapters and you will not bother yourself with view type at all; because adding both in one adapter will need a lot of handling if your data changes frequently specially if you are using ListAdapter since you will need to add your header always at the beginning of the data. on the other side using ConcatAdapter will give you two separate adapters and you can update them separately here is good article on how to make it. https://medium.com/androiddevelopers/get-ahead-using-headers-in-recyclerview-2909a69b19 – Amr Feb 02 '22 at 16:13