Michael Svit Michael Svit - 2 years ago 199
Android Question

RecyclerView creating and binding all ViewHolders when scrolling

I have a weird issue which I cannot pinpoint. My app fetches movie data from a website, and specifically, a poster image. I created a RecyclerView that is supposed to show all of the posters in a 2-column grid.

Scrolling experience is extremely laggy, and for some reason the items are swapping places mid-scroll even though there are no changes in the data list.

I tried debugging the app, and found out that on the first time I call notifyDataSetChanged() my adapter would call onCreateViewHolder(...) for every item in my list (around 90 times, when only 6 posters are visible at any given moment), and same goes for onBindViewAdapter(...).
Then, when scrolling, it would recreate and rebind all ViewHolders again!

I have checked my code again and again and haven't been able to determine the cause. Your help will be greatly appreciated.


public class MovieRecyclerViewAdapter extends RecyclerView.Adapter<MovieRecyclerViewAdapter.ViewHolder> {
private final Cinema cinema;
private final List<Movie> movies;
private final Context context;
private final DisplayMetrics displayMetrics;
private final Picasso picasso;

public MovieRecyclerViewAdapter(Context context, Cinema cinema, List<Movie> movies) {
this.context = context;
this.cinema = cinema;
this.movies = movies;

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
displayMetrics = new DisplayMetrics();

// Set up Picasso with okHttp3
okhttp3.OkHttpClient okHttp3Client = new okhttp3.OkHttpClient();
OkHttp3Downloader okHttp3Downloader = new OkHttp3Downloader(okHttp3Client);
picasso = new Picasso.Builder(context)

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_movies, parent, false);
return new ViewHolder(view);

public void onBindViewHolder(final ViewHolder holder, int position) {
Movie movie = movies.get(position);
int imgViewWidth = displayMetrics.widthPixels / 2;
int imgViewHeight = (int)(imgViewWidth * 1.4);
.resize(imgViewWidth, imgViewHeight)

public int getItemCount() {
return movies.size();

public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;

public ViewHolder(View view) {
this.imageView = (ImageView) view.findViewById(R.id.fragment_movies_poster);

RecyclerView item layout:

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"


RecyclerView XML definition:

tools:listitem="@layout/fragment_movies" />

RecyclerView configuration, setting adapter:

public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_movies_grid, container, false);

Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.fragment_movies_recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(context, columnCount));
movies = new ArrayList<>();
adapter = new MovieRecyclerViewAdapter(context, cinema, movies);

fetchMovies(view); // Populates movies list

return view;


Since posting this question I found a similar question: Android RecyclerView Creates and Binds all the views on Dataset change

Indeed, once I set the item layout_height to a fixed value rather than "wrap_content" the issue is fixed. Apparently the RecyclerView tries to create all its views in order to determine its height.

However, I tried the solution offered in that question, calling the LayoutManager's
with layout_height set to "wrap_content" and the issue persisted. Are there any suggestions how would I have an adaptive layout_height without this issue occurring?

Answer Source

I ended up avoiding this issue by using a custom ImageView subclass for the item, overriding onMeasure() and giving it a fixed aspect ratio that determines height according to width.


public class FixedAspectRatioImageView extends ImageView{
    public FixedAspectRatioImageView(Context context) {

    public FixedAspectRatioImageView(Context context, AttributeSet attrs) {
        super(context, attrs);

    public FixedAspectRatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = (int) (widthSize * 1.4);
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));

RecyclerView item as defined in XML:

<?xml version="1.0" encoding="utf-8"?>
    android:layout_height="288dp"> // Value here does not matter, as it is dynamically set
                                   // to be 1.4 times width

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download