james04
james04

Reputation: 1920

Multiple viewType RecyclerView imageView size changes on scroll

I have a recyclerview that has multiple viewTypes. One of them has an imageview in the layout

<ImageView
    android:id="@+id/img_rcv_msg_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitXY"
    app:imgBottomRightCorner="10dp"
    app:imgTopLeftCorner="10dp"
    app:srcCompat="@android:drawable/ic_menu_report_image"
    tools:ignore="ContentDescription" />

Inside the BindViewHolder i use Glide to load the image to the view

// I have added this
rcvImageView.apply {
     layout(0,0,0,0)
}
Glide.with(activityContext).load(previewFile).into(rcvImageView)

I have programmed the code so the image file has dimensions valid so it fits the screen. However, when I scroll the recyclerView the same image are shown with different size...!! Why is that happening?

EDITED: I have added layout(0,0,0,0) before setting the image with Glide, but now the problem is that the moment view is created image has 0 dimensions and the moment after Glide loads the images and has corrent dimensions. That moment all the above items are getting pushed upwards because an already created item changed its height!

Upvotes: 1

Views: 684

Answers (3)

james04
james04

Reputation: 1920

Well after a lot of research i ended to make a custom solution because there is no way that you can have an ImageView with wrap_content to Width AND Height and expect from the recyclerview not to change the size after scrolling. What i did is to have to ImageViews, one for Portrait and one for LandScape and Cube

 <ImageView
            android:id="@+id/img_snt_msg_image_landscape"
            android:layout_width="320dp"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:scaleType="fitXY"
            android:maxWidth="320dp"
            android:maxHeight="320dp"
            android:minWidth="150dp"
            android:minHeight="150dp"
            android:src="@drawable/error_image"
            app:imgBottomRightCorner="10dp"
            app:imgTopLeftCorner="10dp"
            tools:ignore="ContentDescription" />

 <ImageView
            android:id="@+id/img_snt_msg_image_portrait"
            android:layout_width="wrap_content"
            android:layout_height="320dp"
            android:adjustViewBounds="true"
            android:scaleType="fitXY"
            android:maxWidth="320dp"
            android:maxHeight="320dp"
            android:minWidth="150dp"
            android:minHeight="150dp"
            android:src="@drawable/error_image"
            app:imgBottomRightCorner="10dp"
            app:imgTopLeftCorner="10dp"
            tools:ignore="ContentDescription" />

As you can see both have a min and max for each dimension. But the toggle wrap_content between their width and height. So Landscape images have fixed width and portrait images have fixed height. Inside the onbind viewholder according with the orientation i hide the proper imageView and show the other!

 sntImageViewLand.setImageDrawable(ContextCompat.getDrawable(itemView.context, R.drawable.loading_image))
            sntImageViewLand.visibility = View.VISIBLE
            sntImageViewPortrait.visibility = View.GONE
when (it.orientation) {
    LANDSCAPE, CUBE -> {
      Glide
       .with(activityContext)
       .load(previewFile)
       .diskCacheStrategy(DiskCacheStrategy.NONE)
       .into(sntImageViewLand)

       sntImageViewLand.setOnClickListener {
          itemCallback.onRowClicked(adapterPosition, messageItem)
       }
       sntImageViewLand.visibility = View.VISIBLE
       sntImageViewPortrait.visibility = View.GONE
     }
     PORTRAIT -> {
       Glide
        .with(activityContext)
        .load(previewFile)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .into(sntImageViewPortrait)

        sntImageViewPortrait.setOnClickListener {
           itemCallback.onRowClicked(adapterPosition, messageItem)
        }
        sntImageViewPortrait.visibility = View.VISIBLE
        sntImageViewLand.visibility = View.GONE
     }
}

Upvotes: 0

Zain
Zain

Reputation: 40830

Try to reset the image in the ViewHolder to the original size every time the views are recycled in onBindViewHolder method of the adapter.

So that when Views are recycled (while scrolling), the ImageView can reset to the original size.

onBindViewHolder(...)
    holder.imageView.layout(0, 0, 0, 0);

Source: comment of the @TWiStErRob in here

Upvotes: 1

SlothCoding
SlothCoding

Reputation: 1706

This happenes because ViewHolders reuse objects and as you scroll it will show some of the previously loaded images. You can stop this by using .diskCacheStrategy(DiskCacheStrategy.NONE) with Glide like this:

Glide.with(activityContext)
     .load(yourFileDataModel)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(yourImageView);

Or you can use Signature like this:

Glide.with(activityContext)
     .load(yourFileDataModel)
     .signature(new StringSignature(String.valueOf(System.currentTimeMillis())))
     .into(yourImageView);

Upvotes: 1

Related Questions