1

I'm having an issue where when the RecyclerView has a big amount of items (say 2000) the scrolling is really laggy.

I would be really really thankful if someone helps me improving it. Thanks in advance.

EDIT: The lag may be caused when using this FastScroll library. If someone is able to do some pull requests to improve it, I'm sure the dev will be really thankful. https://github.com/plusCubed/recycler-fast-scroll

Here's the Fragment code:

package jahirfiquitiva.apps.iconshowcase.fragments;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.pluscubed.recyclerfastscroll.RecyclerFastScroller;

import java.util.ArrayList;
import java.util.Locale;

import jahirfiquitiva.apps.iconshowcase.R;
import jahirfiquitiva.apps.iconshowcase.adapters.IconsAdapter;
import jahirfiquitiva.apps.iconshowcase.utilities.Preferences;
import jp.wasabeef.recyclerview.adapters.AlphaInAnimationAdapter;
import jp.wasabeef.recyclerview.adapters.ScaleInAnimationAdapter;

public class IconsFragment extends Fragment {

    private IconsAdapter mAdapter;
    private Preferences mPrefs;
    private ArrayList<String> iconsNames, filteredIconsList;
    private ArrayList<Integer> iconsInts, filteredIconsInts;
    private ViewGroup layout;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mPrefs = new Preferences(getActivity());

        if (layout != null) {
            ViewGroup parent = (ViewGroup) layout.getParent();
            if (parent != null) {
                parent.removeView(layout);
            }
        }
        try {
            layout = (ViewGroup) inflater.inflate(R.layout.icons_grid, container, false);
        } catch (InflateException e) {

        }

        RecyclerFastScroller fastScroller =
                (RecyclerFastScroller) layout.findViewById(R.id.rvFastScroller);
        fastScroller.setVisibility(View.GONE);

        RecyclerView iconsGrid = (RecyclerView) layout.findViewById(R.id.iconsGrid);
        iconsGrid.setHasFixedSize(true);
        iconsGrid.setLayoutManager(new GridLayoutManager(getActivity(),
                getResources().getInteger(R.integer.icon_grid_width)));

        mAdapter = new IconsAdapter(getActivity(), new ArrayList<String>(), new ArrayList<Integer>());

        if (getArguments() != null) {
            iconsNames = getArguments().getStringArrayList("iconsNamesList");
            iconsInts = getArguments().getIntegerArrayList("iconsArray");
            mAdapter.setIcons(iconsNames, iconsInts);
        }

        iconsGrid.setAdapter(mPrefs.getAnimationsEnabled() ? animAdapter(mAdapter) : mAdapter);
        fastScroller.setRecyclerView(iconsGrid);
        fastScroller.setHideDelay(500);
        fastScroller.setVisibility(View.VISIBLE);

        return layout;
    }

    public static IconsFragment newInstance(ArrayList<String> iconsNames, ArrayList<Integer> iconsArray) {
        IconsFragment fragment = new IconsFragment();
        Bundle args = new Bundle();
        args.putStringArrayList("iconsNamesList", iconsNames);
        args.putIntegerArrayList("iconsArray", iconsArray);
        fragment.setArguments(args);
        return fragment;
    }

    public void performSearch(String query) {
        filter(query, mAdapter);
    }

    private synchronized void filter(CharSequence s, IconsAdapter adapter) {
        if (s == null || s.toString().trim().isEmpty()) {
            if (filteredIconsList != null) {
                filteredIconsList = null;
            }
            if (filteredIconsInts != null) {
                filteredIconsList = null;
            }
            adapter.clearIconsList();
            adapter.setIcons(iconsNames, iconsInts);
            adapter.notifyDataSetChanged();
        } else {
            if (filteredIconsList != null) {
                filteredIconsList.clear();
            }
            if (filteredIconsInts != null) {
                filteredIconsList = null;
            }
            filteredIconsList = new ArrayList<String>();
            filteredIconsInts = new ArrayList<Integer>();
            for (int i = 0; i < iconsNames.size(); i++) {
                String name = iconsNames.get(i);
                if (name.toLowerCase(Locale.getDefault())
                        .startsWith(s.toString().toLowerCase(Locale.getDefault()))) {
                    filteredIconsList.add(iconsNames.get(i));
                    filteredIconsInts.add(iconsInts.get(i));
                }
            }
            adapter.clearIconsList();
            adapter.setIcons(filteredIconsList, filteredIconsInts);
            adapter.notifyDataSetChanged();
        }
    }

    private ScaleInAnimationAdapter animAdapter(IconsAdapter iconsAdapter) {
        AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(iconsAdapter);
        ScaleInAnimationAdapter scaleAdapter = new ScaleInAnimationAdapter(alphaAdapter);
        scaleAdapter.setFirstOnly(true);
        return scaleAdapter;
    }

}

And RecyclerView adapter:

package jahirfiquitiva.apps.iconshowcase.adapters;

import android.content.Context;
import android.content.res.Resources;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.afollestad.materialdialogs.MaterialDialog;

import java.util.ArrayList;
import java.util.Locale;

import jahirfiquitiva.apps.iconshowcase.R;
import jahirfiquitiva.apps.iconshowcase.utilities.Util;

public class IconsAdapter extends RecyclerView.Adapter<IconsAdapter.IconsHolder> implements View.OnClickListener {

    private final Context context;
    private ArrayList<String> iconsList = new ArrayList<>();
    private ArrayList<Integer> iconsArray = new ArrayList<>();

    public IconsAdapter(Context context, ArrayList<String> iconsList, ArrayList<Integer> iconsArray) {
        this.context = context;
        this.iconsList = iconsList;
        this.iconsArray = iconsArray;
    }

    public void setIcons(ArrayList<String> iconsList, ArrayList<Integer> iconsArray) {
        this.iconsList.addAll(iconsList);
        this.iconsArray.addAll(iconsArray);
        this.notifyItemRangeInserted(0, iconsList.size() - 1);
    }

    public void clearIconsList() {
        this.iconsList.clear();
        this.iconsArray.clear();
    }

    @Override
    public IconsHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        return new IconsHolder(inflater.inflate(R.layout.item_icon, parent, false));
    }

    @Override
    public void onBindViewHolder(IconsHolder holder, int position) {
        if (iconsArray.size() > 0) {
            holder.icon.setImageResource(iconsArray.get(position));
        }
        holder.view.setTag(position);
        holder.view.setOnClickListener(this);
        setAnimation(holder.icon, position);
    }

    private int lastPosition = -1;

    private void setAnimation(View viewToAnimate, int position) {
        if (position > lastPosition) {
            viewToAnimate.setHasTransientState(true);
            lastPosition = position;
        }
    }

    @Override
    public int getItemCount() {
        return iconsList == null ? 0 : iconsList.size();
    }

    @Override
    public void onClick(View v) {
        int position = (Integer) v.getTag();
        int resId = iconsArray.get(position);
        String name = iconsList.get(position).toLowerCase(Locale.getDefault());

        MaterialDialog dialog = new MaterialDialog.Builder(context)
                .customView(R.layout.dialog_icon, false)
                .title(Util.makeTextReadable(name))
                .positiveText(R.string.close)
                .show();

        if (dialog.getCustomView() != null) {
            ImageView dialogIcon = (ImageView) dialog.getCustomView().findViewById(R.id.dialogicon);
            dialogIcon.setImageResource(resId);
        }
    }

    class IconsHolder extends RecyclerView.ViewHolder {

        final View view;
        final ImageView icon;

        IconsHolder(View v) {
            super(v);
            view = v;
            icon = (ImageView) v.findViewById(R.id.icon_img);
        }
    }

}
Tom11
  • 2,199
  • 6
  • 26
  • 50
Jahir Fiquitiva
  • 1,393
  • 3
  • 18
  • 42
  • i too had this problem. okay. can you please tell me is there any lag when u put only one image for all your list.? – Mr Robot Dec 19 '15 at 02:03
  • If there are few items, say even 50... The scroll is good enough. – Jahir Fiquitiva Dec 19 '15 at 02:08
  • Can't you implement pagination – Mightian Dec 19 '15 at 02:22
  • It's simple. Profile: http://developer.android.com/tools/performance/traceview/index.html That should show you if something blocks the ui thread. Like the call to [`ImageView#setImageResource(int)`](http://developer.android.com/reference/android/widget/ImageView.html#setImageResource%28int%29) which does *"Bitmap reading and decoding on the UI thread, which can cause a latency hiccup"* as mentioned in the documentation. – zapl Dec 21 '15 at 10:27

2 Answers2

4

From your comment you have mentioned that, you are not using any library for loading images, that's might be the issue, since libraries like Picasso ,glide... Use an asynctask to load images load on the main that is refused. You can do the same by writing it yourself but you will end up re-inventing the wheel again

Mightian
  • 6,853
  • 3
  • 37
  • 50
0

How big are the images? Try to downscale them (for example resize through Picasso)

aelimill
  • 1,015
  • 1
  • 10
  • 14