I have a recycler view within a fragment and basically I m trying to load song list in the recycler view .Each row of recycler view contains an imageview (for album art) and textview ( for song name). I am having trouble when the size of the dataset is huge, that is when there are too many songs, the recycler view lags and the app ends up giving an ANR.I am using Glide to load album arts in each row's imageview. How is google music player able to show such large number of songs without any lag?
Edit: This is my SongsFragment
public class SongsFragment extends Fragment {
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
ProgressBar progressBar; // progress bar to show after every 30 items
NestedScrollView nestedScrollView; //for smooth scrolling of recyclerview as well as to detect the end of recyclerview
RecyclerView recyclerView;
ArrayList<Song> songMainList = new ArrayList<>(); //partial list in which items are added
ArrayList<Song> songAllList = new ArrayList<>(); //Complete List of songs
SongAdapter songsAdapter;
private LinearLayoutManager layoutManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_songs, container, false);
nestedScrollView = (NestedScrollView) rootView.findViewById(R.id.nestedScrollView);
progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar);
String songJson = getActivity().getIntent().getStringExtra("songList");
songAllList = new Gson().fromJson(songJson, new TypeToken<ArrayList<Song>>() {
}.getType());
//Getting list of all songs in songAllList
if (songAllList.size() > 30) {
songMainList = new ArrayList<>(songAllList.subList(0,30));
} else {
songMainList = songAllList;
}
//if size of fetched songAllList>30 then add only 30 rows to songMainList
recyclerView = (RecyclerView) rootView.findViewById(R.id.songs);
int spanCount = 1; // 2 columns
int spacing = 4; // 50px
recyclerView.addItemDecoration(new GridItemDecoration(spanCount, spacing, true));
recyclerView.setHasFixedSize(true);
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
songsAdapter = new SongAdapter(getActivity(), songMainList, recyclerView);
nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
View view = (View) nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1);
int diff = (view.getBottom() - (nestedScrollView.getHeight() + nestedScrollView
.getScrollY()));
if (diff == 0) { //NestedScrollView scrolled to bottom
progressBar.setVisibility(View.VISIBLE); //show progressbar
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (songMainList.size() < songAllList.size()) {
int x = 0, y = 0;
if ((songAllList.size() - songMainList.size()) >= 30) {
x = songMainList.size();
y = x + 30;
} else {
x = songMainList.size();
y = x + songAllList.size() - songMainList.size();
}
for (int i = x; i < y; i++) {
songMainList.add(songAllList.get(i)); //Adding new items from songAllList to songMainList one by one
songsAdapter.notifyDataSetChanged();
}
}
progressBar.setVisibility(View.GONE);
}
}, 1500);
}
}
});
recyclerView.setAdapter(songsAdapter);
return rootView;
}
}
And this is my RecyclerViewAdapter along with viewholder
public class SongAdapter extends RecyclerView.Adapter {
private List<Song> songsList;
private Context c;
private RecyclerView.ViewHolder holder;
public SongAdapter(Context context) {
mainActivityContext = context;
}
public SongAdapter(Context context, List<Song> songs, RecyclerView recyclerView) {
songsList = songs;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
c = context;
}
public SongAdapter getInstance() {
return SongAdapter.this;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.song_list_row, parent, false);
return new SongViewHolder(view,c);
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (holder instanceof SongViewHolder) {
Song song = songsList.get(position);
this.holder = holder;
String name = song.getName();
String artist = song.getArtist();
String imagepath = song.getImagepath();
((SongViewHolder) holder).name.setText(name);
((SongViewHolder) holder).artist.setText(artist);
if (!imagepath.equalsIgnoreCase("no_image")) //if the album art has valid imagepath for this song
Glide.with(c).load(imagepath)
.centerCrop()
.into(((SongViewHolder) holder).iv);
else
((SongViewHolder) holder).iv.setImageResource(R.drawable.empty);
((SongViewHolder) holder).song = song;
}
}
@Override
public int getItemCount() {
return songsList.size();
}
static class SongViewHolder extends RecyclerView.ViewHolder{
ImageView iv;
TextView name, artist;
CardView songListCard;
private Context ctx;
private OnLongPressListener mListener;
SongViewHolder(View v, Context context) {
super(v);
this.ctx = context;
iv= (ImageView) v.findViewById(R.id.album_art);
name= (TextView) v.findViewById(R.id.name);
artist= (TextView) v.findViewById(R.id.artist_mini);
songListCard = (CardView) v.findViewById(R.id.song_list_card);
}
}
The recyclerview works fine when there are only 150-200 items but when reaching to 600-700 items , the whole app slows down. Could this be because of the way I have used glide in onBindViewHolder?