RecyclerView 卡顿滚动

23

我正在将400x200的图像加载到RecyclerView中,但在2k设备上滚动不流畅。我正在使用Picasso从资源中加载图像。

正如您在演示中所看到的,2k屏幕上的图像有些模糊,但如果我加载更高分辨率的图像,情况会变得更糟。

演示

这是我的代码: card_view.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    card_view:cardPreventCornerOverlap="false"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    card_view:cardCornerRadius="2dp">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:id="@+id/rel">

        <ImageView
            android:id="@+id/cardimage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:src="@drawable/p7"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="title"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:paddingTop="16dp"
            android:paddingBottom="24dp"
            android:textStyle="bold"
            android:textSize="24sp"
            android:id="@+id/cardtitle"
            android:layout_gravity="center_vertical"/>

    </LinearLayout>

</android.support.v7.widget.CardView>

我的适配器代码

public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {

private Context mContext;
List<Flower> list = new ArrayList<>();

public CardAdapter(Context mContext, List<Flower> list) {
    this.mContext = mContext;
    this.list = list;

}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)   {

    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_view, parent, false);
    return new ViewHolder(itemView);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position)  {

    holder.Flower=getItem(position);
    holder.cardtitle.setText(list.get(position).name);

    Picasso.with(mContext)
            .load(list.get(position).id)
            .placeholder(R.drawable.ic_launcher)
            .into(holder.cardimage);
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}

@Override
public int getItemCount() {
    return list.size();
}

public Flower getItem(int i) {
    return list.get(i);
}

public class ViewHolder extends RecyclerView.ViewHolder {

    ImageView cardimage;
    TextView cardtitle;
    Flower Flower;

    public ViewHolder(View itemView) {
        super(itemView);

        cardimage = (ImageView) itemView.findViewById(R.id.cardimage);
        cardtitle = (TextView) itemView.findViewById(R.id.cardtitle);
    }
}
}

更新:我正在从资源中加载图片,而不是下载任何内容。

这是我的数组:

 private void initializeData() {
    flowers = new ArrayList<>();
    flowers.add(new Flower("Flower 1", R.drawable.p8));
    flowers.add(new Flower("Flower 2", R.drawable.p10));
    flowers.add(new Flower("Flower 3", R.drawable.p11));
    flowers.add(new Flower("Flower 4", R.drawable.p8));
    flowers.add(new Flower("Flower 5", R.drawable.photo2));
    flowers.add(new Flower("Flower 6", R.drawable.photo6));
    flowers.add(new Flower("Flower 7", R.drawable.p12));
    flowers.add(new Flower("Flower 8", R.drawable.p9));
    flowers.add(new Flower("Flower 9", R.drawable.p8));
    flowers.add(new Flower("Flower 10", R.drawable.p8));
    flowers.add(new Flower("Flower 11", R.drawable.p8));
    flowers.add(new Flower("Flower 12", R.drawable.p10));
}

更新2:大家好,我通过设置adapter.setHasStableIds(true) 解决了大部分的延迟问题,但是当图片还未加载时,在第一次滚动时应用程序仍然会有些卡顿,该如何解决?

更新3:我刚刚尝试从网络加载图片,一切都很流畅,可能从资源加载图片存在问题。

好的,谢谢大家,我将从网络加载我的图片。


滚动时它只有卡顿吗?还是向上和向下滚动都会卡顿? - Lucas Crawford
@LucasCrawford 只有在向下滚动时才会出现延迟。编辑:上下滚动都会出现延迟,一旦加载图像,延迟就会减少,但仍然存在。 - phlosopher_mk
你找到解决办法了吗? - Venkat Ramana
14个回答

26

1
不确定我的问题是否与原帖相同,但这个方法解决了我的问题。 - Larpus
对我来说也是一样的,我的搜索结果导致了我来到这里,所以当我找到答案后,我想在这里发布它。 - Dasser Basyouni

12

请确认RecyclerView.setHasFixedSize()是否设置为true?我无法重现这种延迟...您可以将整个项目上传到git上吗?看看这个链接,也许它可以帮助您:http://antonioleiva.com/recyclerview/


是的就是真的。这里是Android Studio项目。https://drive.google.com/file/d/0BypaioeS7MXPNHY3ZUFiNVBtcTg/view?usp=sharing - phlosopher_mk
3
我正在使用Genymotion虚拟机,但似乎无法重现卡顿问题。 我有三个可能的改进建议:
  1. 在activity_main.xml中,将RecyclerView.layout_height改为“match_parent”。
  2. 尝试仅在drawable文件夹中使用PNG格式的图片,因为它们解压缩更快。
  3. 设置adapter.setHasStableIds(true); 并在CardAdapter中添加以下内容: @Override public long getItemId(int position) { return position; }
- Andrei Verdes
非常感谢,这解决了大部分的延迟问题。现在应用程序只有在第一次滚动时才会出现卡顿,因为图片还没有加载完成。一旦图片加载完成,一切都很流畅。你对此有什么想法吗?你是否在2k分辨率下运行模拟器?尝试运行n6或n6p。只有在2k分辨率下才会出现卡顿。 - phlosopher_mk
是的...我在Nexus 6模拟器上尝试过了。我会尝试Nexus 6p。我的一个朋友建议你尝试Glide而不是Picasso,让我知道它的效果如何。 https://github.com/bumptech/glide - Andrei Verdes

11

我认为有时候你可能会感到非常恼火,因为只是尝试在drawable中使用一些静态图片来展示你的recycler视图,而且这样做会导致极其缓慢。

为什么呢?如果你使用某个库从url加载图片(例如Picasso、Glide等),你就不会遇到这个问题,但如果是相同的图像、相同的大小,并且它就在你的drawable中(根本不需要下载),情况又是怎样的呢?一段时间后,我发现Android对drawable中的图像进行了调整,以便我们能够在不同分辨率的设备上获得合适的图像。因此,我的建议是使用drawable-nodpi来存储你的图像,以便Android不会干扰我们的图片。


我尝试了所有其他方法,但都没有起作用,但这个就是解决方案。非常感谢!! - Nuhman
这就是问题的关键,特别是当您在同一个适配器中拥有不同的视图持有者时。 - yUdoDis
这对我来说是答案。奇怪的是,我在我的应用程序的另一个部分中使用了完全相同的图像/回收器/...,同一活动,同一布局,但没有问题。真的很奇怪。 - Feuby

4
这是因为图片需要加载的时间较长。首先,将加载图片的代码更改为以下代码...
Picasso.get().load(list.get(position).id).fit().centerCrop()
                .placeholder(R.drawable.ic_launcher)
                .into(holder.cardimage);

然后,在将适配器设置到 MainActivity 中 CardAdapter 的实例之前,请添加此行代码至 RecyclerView 上。
        yourAdapter_name.setHasStableIds(true);

还有一个提示……如果有人正在使用大的drawable(xxxhdpi, xxhdpi),首先将它们转换为mdpi或ldpi。这也可以极大地提高性能并减小apk文件大小。您可以使用此网站 NativeScript Image Builder


.fit().centerCrop() - 解决了问题。不要放置 yourAdapter_name.setHasStableIds(true) - 适配器不会回收; - ekashking
使用Glide。它比Picasso更快。 - Dharmishtha

3
这可能是因为Picasso加载图片需要时间。请使用下面的快照并相应地进行调整。
Picasso.with (context)
                    .load (url).fit().centerCrop()
                    .error (R.drawable.UserImage)         
                    .into (imageView);

使用fit()centerCrop()来避免图片过长或失真。

2

你需要异步获取图像。目前的做法会导致程序停滞,直到下载完成。

在创建图片视图时先不显示图像,但要有某种监听器,在图像下载完成后设置图像。


2
我不是在下载图片,而是从资源中设置图片。 - phlosopher_mk

2

如果您在垂直模式下使用Recyclerview,并且您的活动包含其他项目,例如ScrollView,那么您必须使用NestedScrollView而不是ScrollView

如Google文档所述,NestedScrollView与ScrollView类似,但它支持在Android的新版本和旧版本上作为嵌套滚动父项和子项。默认情况下启用嵌套滚动。


1
如果在 ScrollView 中有多个 Recycler Views,会发生什么? - Srikar Reddy
对于多个Recycler View,你还必须使用“NestedScrollView”来处理滚动。 - MSH

1

也许已经有点晚了,但愿这篇文章对某些人有所帮助。我遇到了同样的问题,问题出在以下代码行:

Picasso.get().load(image).into(holder.imageView); 

所以我加了 fit().centerCrop(),就像这样:
Picasso.get().load(image).fit().centerCrop().into(holder.imageView);

那解决了我的问题。


1
当recyclerView中的一个textView接收到null值时,我遇到了卡顿的问题。我解决了这个问题,我的recyclerView就不再卡顿了。

0

从我使用RecyclerView的经验来看,可能的原因是图像大小。 此外,将属性(setNestedScrollingEnabled(false);)添加到RecyclerView和(setHasStableIds)添加到适配器确实有助于使RecyclerView平稳运行。 因此,底线是,如果您的RecyclerView需要从网络加载图像,请确保覆盖其大小或使用缩略图属性(在Glide中)。 这个技巧拯救了我的生命,我希望它也能拯救其他人的生命。 愉快的编码:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接