Reputation: 588
I have a simple MainActivity
with only 1 androidx.recyclerview.widget.RecyclerView
.
Dataset is fixed at 8 items. Each row is a single ImageView
like this item.xml
:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop"/>
Here is my adapter:
class MyAdapter(private val drawableResIds: List<Int>) :
RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
class MyViewHolder(val imageView: ImageView) : RecyclerView.ViewHolder(imageView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val rootView = LayoutInflater.from(parent.context)
.inflate(R.layout.item, parent, false)
return MyViewHolder(rootView as ImageView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// #1: lagging
// holder.imageView.setImageResource(drawableResIds[position])
// #2: lagging
// holder.imageView.setImageDrawable(
// ContextCompat.getDrawable(holder.imageView.context, drawableResIds[position])
// )
// #3: lagging
// holder.imageView.setImageBitmap(
// BitmapFactory.decodeResource(holder.imageView.context.resources, drawableResIds[position])
// )
// #4: smooth
Glide.with(holder.imageView.context)
.load(drawableResIds[position])
.into(holder.imageView);
}
override fun getItemCount() = drawableResIds.size
}
Problem: The recycler view scrolling is lagging.
What I tried: I already set setHasFixedSize(true)
and setItemViewCacheSize(20)
.
You can see I tried 4 approaches to set image. But only with Glide, the scrolling is smooth.
Question: Why is it lagging? And what is the correct way to set an image for smooth scrolling?
(I expected a simple method, avoid fancy caching or use lib)
Also, the set image operations only happen once, since the "list's slots" are big enough, don't need to re-bind while scrolling I guess. So is it an issue with the Android rendering image? The images are JPEG 800x400 (size on disk 70-200Kb)
Demo source code can be found here: https://github.com/akivamu/rv-demo
UPDATE 1: I replaced RecyclerView
with ScrollView
+ 8 static ImageView
, the phenomenon is the same
UPDATE 2: I move those images from drawable
-> drawable-xxxhdpi
. And it's scrolling smoothly!!! Not sure if will work on other density devices
Upvotes: 0
Views: 1961
Reputation: 588
I move the images used in RecyclerView
from folder drawable
-> drawable-xxxhdpi
.
And it's scrolling smoothly on my device (guess it's xxxhdpi device)
Not sure if will work on other density devices (other than xxxhdpi)
Still don't know the reason behind, should ask in another question.
Upvotes: 1
Reputation: 5598
The problem is your image resolutions. Each pixel is stored in 4 bytes of memory (red, green, blue, alpha) so for a 800×400 image it will consume ~1.22MB of RAM. While your image view dimensions is much less than your image resolutions. In this cases you need to resize your image to fit the view dimensions before setting it on the view to avoid large memory consumptions or else drawing your views will take more than 16ms and results to laggy scrolls. Using libraries like Glide will just save you the time and effort of resizing or caching images on your own.
Upvotes: 1
Reputation: 2343
Loading static image from drawable
folder is a heavy task. Whatever code you write in onBindViewHolder()
will be executed whenever an item is displayed. It means that holder.imageView.setImageResource(drawableResIds[position])
will be called repeatedly for each item when you scroll the list. Therefor, if you are sure that you have just 8 images and no more, check memory available and load the 8 images and save in an array first. Beside, resize images if need, it's meaningless when show a big size image as a thumbnail,...Also, Bitmap rendering affects android performance, refer caching mechanism too https://developer.android.com/topic/performance/graphics/cache-bitmap
Upvotes: 0
Reputation: 1
I've never experienced this before, I think it's lag because the image is large.but I usually resize the bitmap to ImageView size, then recycle the original bitmap instead of using 800x600 image to the ImageView.
ImageView iconView = itemView.findViewById(R.id.img_poster);
iconView.post(() -> {
final Bitmap bitmap = BitmapFactory.decodeFile("/your/image/path");
if (bitmap == null)
return;
final Matrix matrix = new Matrix();
matrix.setRectToRect(new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()), new RectF(0, 0, iconView.getWidth(), iconView.getHeight()), Matrix.ScaleToFit.CENTER);
final Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
iconView.setImageBitmap(scaledBitmap);
});
I'm not sure this will work in your case, but it's worth a try.
Upvotes: 0