【问题标题】:RecyclerView laggy scrollingRecyclerView 延迟滚动
【发布时间】:2015-10-22 18:45:43
【问题描述】:

我在 RecyclerView 中加载 400x200 图像,但在 2k 设备上滚动很慢。我正在使用 Picasso 从资源中加载图像。

正如您在演示图像中看到的那样,在 2k 屏幕上是模糊的,但如果我加载更高分辨率的图像,情况会变得更糟。 如何解决这个问题,我什至没有加载 400x200 的大图像?

Demo

这是我的代码 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:我刚刚尝试从网络加载图像,一切似乎都很顺利,可能从资源加载图像存在一些问题。

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

【问题讨论】:

  • 只有向下滚动时才会滞后吗?还是上下滚动都会滞后?
  • @LucasCrawford 只有在向下滚动时才会滞后。编辑:它上下滞后,一旦加载图像时滞后较少,但它仍然存在。
  • 你找到解决方案了吗??

标签: android android-recyclerview picasso


【解决方案1】:

如果您使用两个或多个回收站视图,例如(RecyclerView 和 NestedView) 试试这个

recyclerView.setNestedScrollingEnabled(false);

来源:Recyclerview inside Nested Scrollview scroll but does not fast scroll like normal Recyclerview or Nested Scrollview

【讨论】:

  • 不确定我的问题是否与原始海报相同,但这里解决了我的问题。
  • 对我来说也是一样,搜索把我带到了这里,所以当我找到我的答案时,我喜欢把它贴在这里
【解决方案2】:

RecyclerView.setHasFixedSize() 是否设置为 true?我无法重现这种滞后...您能否将整个项目发布在 git 上?看看这个,也许对你有帮助:http://antonioleiva.com/recyclerview/

【讨论】:

  • 是的。这是android工作室项目。 drive.google.com/file/d/0BypaioeS7MXPNHY3ZUFiNVBtcTg/…
  • 我正在使用 genymotion 虚拟机,但我似乎无法重现滞后。我有 3 个可能的改进: 1. 在 activity_main.xml 中,将 RecyclerView.layout_height 更改为“match_parent” 2. 尝试仅在可绘制文件夹中使用 png,因为它们解压缩速度更快 3. 设置 adapter.setHasStableIds(true);并在 CardAdapter 中添加:@Override public long getItemId(int position) { return position; }
  • 非常感谢,这解决了大部分滞后问题。现在,当图像仍未加载时,应用程序仅在第一次滚动时滞后。加载图像后,一切都很顺利。对此有什么想法吗?您是否以 2k 分辨率运行模拟器,请尝试运行 n6 或 n6p 。它仅在 2k 分辨率上滞后。
  • 是的...我在 nexus 6 模拟器上试过。我会试试nexus 6p。我的一位朋友建议您尝试 Glide 而不是 Picasso,让我知道它是如何进行的。 github.com/bumptech/glide
【解决方案3】:

我相信有时您可能会因为只尝试一些 可绘制的静态图像来显示您的回收站视图而生气。而且它非常滞后

为什么? 在使用某些库从 url 加载图像时可能不会遇到此问题(Picasso、Glide、...),但是对于 相同的图像、相同的大小和就在你的drawable中(根本不需要下载)。过了一会儿,我发现,android 做了一些技巧来调整可绘制图像的大小,以便我们在不同分辨率的设备上获得合适的图像。所以我的建议使用drawable-nodpi来存储你的图片,以便android不会干扰我们的图片。

【讨论】:

  • 我已经尝试了所有其他方法但没有奏效,但这就是解决方案。非常感谢!!
  • 这是问题的症结所在,尤其是当您在同一个适配器中有不同的视图时。
  • 这就是我的答案。奇怪的是我使用了完全相同的图像/回收器/......在我的应用程序的另一部分,相同的活动,相同的布局,没有问题。真的很奇怪
【解决方案4】:

这是因为图片加载需要时间。 首先,将加载图像的代码更改为以下代码...

Picasso.get().load(list.get(position).id).fit().centerCrop()
                .placeholder(R.drawable.ic_launcher)
                .into(holder.cardimage);

然后,之前将recyclerview上的适配器设置为MainActivity内的CardAdapter实例,添加这一行..

        yourAdapter_name.setHasStableIds(true);

另一个提示...如果有人使用大型可绘制对象(xxxhdpi、xxhdpi),请先将它们转换为 mdpi 或 ldpi 。这也将大大提高性能并减少apk大小。您可以使用此网站NativeScript Image Builder

【讨论】:

  • .fit().centerCrop() - 解决了这个问题。不要放yourAdapter_name.setHasStableIds(true) - 适配器不会回收;
  • 使用滑行。它比毕加索更快。
【解决方案5】:

这可能是因为毕加索需要时间来加载图片。使用以下快照并进行相应调整。

Picasso.with (context)
                    .load (url).fit().centerCrop()
                    .error (R.drawable.UserImage)         
                    .into (imageView);

使用fit()centerCrop() 避免冗长。

【讨论】:

    【解决方案6】:

    您需要异步获取图像。就像现在一样,它会停止实际下载图像。

    在创建图像视图时将其留空,但在下载图像时使用某种侦听器来设置图像。

    【讨论】:

    • 我不是在下载图片,我是从资源中设置图片。
    【解决方案7】:

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

    如谷歌文档中所述,NestedScrollView 与 ScrollView 类似,但它支持在新旧版本的 Android 上充当嵌套滚动父项和子项。默认启用嵌套滚动。

    【讨论】:

    • 如果一个 ScrollView 中有多个 Recycler View 怎么办?
    • 对于多个 Recycler 视图,您还必须使用“NestedScrollView”来处理滚动
    【解决方案8】:

    当 recyclerView 中的一个 textView 接收空值时,我遇到了延迟的 recyclerView 问题。我解决了这个问题,我的 recyclerView 停止了滞后。

    【讨论】:

      【解决方案9】:

      它可能会迟到,但也许有人会发现它很有用。遇到了同样的问题,问题出在:

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

      所以我像这样添加了 fit().centerCrop():

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

      这解决了我的问题

      【讨论】:

        【解决方案10】:

        我有点晚了,但这可以通过使用 Glide 的缩略图来解决。

             Glide.with(context)
                        .load(URL)
                        .dontAnimate()
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .thumbnail(0.5f)
                        .centerCrop()
                        .into(imageView);
        

        【讨论】:

          【解决方案11】:

          在我的例子中,位图创建会导致滚动延迟。所以我将位图创建放在后台线程中,并在 UI 线程中设置为图像。这样,UI 不会被阻止创建位图

          new Thread(new Runnable() {
                              @Override
                              public void run() {
                                  Bitmap myBitmap = BitmapFactory.decodeFile(filepath);
                                  activity.runOnUiThread(new Runnable() {
                                      public void run() {
                                          vItem.imgProfile.setImageBitmap(myBitmap);
                     
                                      }
                                  });
                              }
                          }).start();
          

          【讨论】:

            【解决方案12】:

            根据我对 recyclerview 的经验,我发现可能的原因可能是图像大小。 除了将属性 (setNestedScrollingEnabled(false); ) 添加到 recyclerview 和 (setHasStableIds) 到适配器确实有助于使 recyclerview 顺利工作。 因此,底线是,如果您的 recyclerview 有要从网络加载的图像,请确保覆盖它们的大小或使用缩略图属性(在滑行中)。 这个骗局救了我的命,我希望它也能救别人的命。 快乐编码:)

            【讨论】:

              【解决方案13】:

              有点晚了,但我发现利用 Kotlin 协程是一种有效的方法,可以通过将图像加载卸载到后台线程来降低 onBindViewHolder() 调用的成本。我使用 Glide 加载 384x384 图像,并且滚动流畅,最多可容纳数千张图像。

              // Here, defaultScope = CoroutineScope(Dispatchers.Default + SupervisiorJob())
              // Key idea is to launch the Glide call on a background thread.
              
              defaultScope.launch(exceptionHandler) {
                  Glide.with(view)
                      .load(uri)
                      .also {
                          // When Glide is done preparing the resource, 
                          // use Main thread to load into ImageView.
                          withContext(Dispatchers.Main) {
                              it.into(view)
                          }
                      }
              }
              

              对于毕加索来说,这将是类似的事情(虽然我没有亲自尝试过),其中必须从 Main 线程调用 into() 并在后台线程上进行准备。

              【讨论】:

                【解决方案14】:

                我认为原因是 Picasso 会缓存您的图像,但由于您有一个与应用程序捆绑在一起的可绘制对象列表,因此理论上您不需要缓存图像。缓存仅在您从 Internet 下载图像并且您不希望应用程序在您每次在回收站视图上向上或向下滑动时重新下载图像时才有用。

                我会通过更改内存策略来调整毕加索的工作方式,所以试试这个:

                 Picasso.with(getContext()).load(data.get(pos).getFeed_thumb_image()).memoryPolicy(MemoryPolicy.NO_CACHE).into(image);
                

                【讨论】:

                • 谢谢,但没有缓存的情况更糟。我发现在加载所有图像时使用调整大小情况会变得更好,但在第一次向下滚动时仍然滞后。但是在我的情况下设置修复大小不起作用,因为我希望图像填充卡片重量。
                • 问题是否包含在内存中。如果您在 RecyclerView 期间加载可绘制对象,这将导致卡顿。在 RecyclerView 启动之前,需要将 Drawable 加载到内存中。在 RecyclerView 启动之前,可能需要缓存所有未包含的可绘制对象。我不确定 RecyclerView(或您使用的其他部分)是否使用 UI 线程或 BG 线程。如果它已经在使用 BG 线程,您可能需要确保它们没有竞争
                • 这里是 drive.google.com/file/d/0BypaioeS7MXPNHY3ZUFiNVBtcTg/… 项目的演示。我正在使用 picasso 加载可绘制对象,如何在 recycleView 之前加载它们?我尝试在创建后调用 initializeData() 方法,但在第一次滚动时仍有延迟。请记住,这只发生在 2k 分辨率上。在全高清上一切都很顺利。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-10-14
                • 1970-01-01
                • 2012-03-28
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多