当前位置: 首页 > 知识库问答 >
问题:

使用Glide在RecyclerView中加载图像时,UI滞后且不稳定

李华茂
2023-03-14

我有一个RecyclerView,它使用Glide从URL加载图像。现在使用分页从Firebase检索URL,如下所示。问题是,当MainActivity(包含下面的代码和recyclerview)首次初始化时,UI中有一个很大的滞后(非常滞后和断断续续的滚动,选项菜单需要3秒钟才能打开等),并且图像需要一段时间才能加载。在我向下滚动并到达第一页数据的RecyclerView的末尾后,OnScrollListener被触发,我开始从新查询加载新数据。我在另一篇帖子中根据用户的建议尽了最大努力优化Glide的功能,我还设置了适配器。如果运气不好,将hasfixedsize设置为true。知道这里发生了什么吗?尽管查询是异步的,但我是否挂起了UI线程?

编辑:Glide是否会导致主线程的延迟,因为它必须将多个图像加载到回收器视图的ImageViews中?如果是这样,我可以做些什么来解决这个问题?

以下是我如何处理从Firebase获得的数据的分页并通知适配器:

class MainActivity : AppCompatActivity() {

private val TAG: String = MainActivity::class.java.simpleName // Tag used for debugging
private var queryLimit : Long = 50 // how many documents should the query request from firebase
private lateinit var iconsRCV : RecyclerView // card icons recycler view
private lateinit var lastVisible:DocumentSnapshot

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val rootRef: FirebaseFirestore = FirebaseFirestore.getInstance()
    val urlsRef : CollectionReference = rootRef.collection("CardIconUrls")
    val query : Query = urlsRef.orderBy("resID",Query.Direction.ASCENDING).limit(queryLimit) // create a query for the first queryLimit documents in the urlsRef collection

    // Setting Toolbar default settings
    val toolbar : Toolbar = findViewById(R.id.mainToolbar)
    setSupportActionBar(toolbar) // set the custom toolbar as the support action bar
    supportActionBar?.setDisplayShowTitleEnabled(false) // remove the default action bar title

    // RecyclerView initializations
    iconsRCV = findViewById(R.id.cardIconsRCV)
    iconsRCV.layoutManager = GridLayoutManager(this,5) // set the layout manager for the rcv
    val iconUrls : ArrayList<String> = ArrayList() // initialize the data with an empty array list
    val adapter = CardIconAdapter(this,iconUrls) // initialize the adapter for the recyclerview
    iconsRCV.adapter = adapter // set the adapter
    iconsRCV.setHasFixedSize(true)

    iconsRCV.addOnScrollListener(object:OnScrollListener(){
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            super.onScrollStateChanged(recyclerView, newState)

            if(!iconsRCV.canScrollVertically(1) && (newState == RecyclerView.SCROLL_STATE_IDLE) && ((iconsRCV.layoutManager as GridLayoutManager).findLastVisibleItemPosition() == (iconsRCV.layoutManager as GridLayoutManager).itemCount-1)) {
                Log.d(TAG,"End of rcv-Starting query")
                val nextQuery = urlsRef.orderBy("resID",Query.Direction.ASCENDING).startAfter(lastVisible).limit(queryLimit).get().addOnCompleteListener { task ->
                    if(task.isSuccessful) {
                        Log.d(TAG,"Next query called")
                        for(document:DocumentSnapshot in task.result!!) {
                            iconUrls.add(document.get("url").toString())
                        }
                        lastVisible = task.result!!.documents[task.result!!.size()-1]
                        adapter.notifyDataSetChanged()
                    }
                }
            }
        }
    })

    query.get().addOnCompleteListener {task: Task<QuerySnapshot> ->
        if(task.isSuccessful) {
            Log.d(TAG,"Success")
            for(document:DocumentSnapshot in task.result!!) {
                Log.d(TAG,"Task size = " + task.result!!.size())
                iconUrls.add(document.get("url").toString()) // add the url to the list
            }
            lastVisible = task.result!!.documents[task.result!!.size()-1]
            adapter.notifyDataSetChanged() // notify the adapter about the new data
        }
    }
}

这是recyclerview适配器:

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

    private List<String> urlsList;
    private Context context;

    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView iconImg;
        ViewHolder(@NonNull View view) {
            super(view);
            iconImg = view.findViewById(R.id.cardIcon);
        }
    }

    public CardIconAdapter(Context cntxt, List<String> data) {
        context = cntxt;
        urlsList = data;
    }

    @NonNull
    @Override
    public CardIconAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view =  LayoutInflater.from(parent.getContext()).inflate(R.layout.card_icons_rcv_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CardIconAdapter.ViewHolder holder, int position) {
        RequestOptions requestOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL);
        GlideApp.with(context).load(urlsList.get(position)).thumbnail(0.25f).centerCrop().dontTransform().apply(requestOptions).into(holder.iconImg);
    }

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

共有3个答案

欧阳乐生
2023-03-14

尝试将分页库与recyclerview结合使用

链接参考:

使用Kotlin的Android分页库:创建无限列表

Android Jetpack:使用RecycleView和分页管理无限列表(Google I/O'18)

秦炜
2023-03-14

加载大量图像时修复延迟的一些方法

>

  • 将固定大小设置为您的循环视图的真实大小,但如果分页涉及到

    mRecyclerView.setHasFixedSize(true);
    

    像这样使用滑动覆盖图像的像素

    Glide.with(this)
         .load(YOUR_URL)
         .apply(new RequestOptions().override(dimenInPx, dimenInPx)
         .placeholder(R.drawable.placeHolder).error(R.drawable.error_image))
         .into(imageview);
    

    将hardwareAccelerated=“true”属性添加到清单内的活动标记中,如

    <activity
         ...
         android:hardwareAccelerated="true"
         ...
    />
    

    我认为这可能会有所帮助。

  • 翟棋
    2023-03-14

    经过反复试验,我终于解决了这个问题。我的第一个错误是没有发布recyclerview项的xml布局文件,因为这是性能问题的根源。第二个错误是,我使用的是LinearLayout,并将其自身的layout\u width和layout\u height属性设置为75dp,而不是嵌套在LinearLayout内部的ImageView,并将wrap\u内容用于ImageView的各个属性。因此,为了解决性能问题,我做了以下工作:

    1. 我将线性布局更改为约束布局(ConstraintLayout)(我了解到它通常优化得多)

    以下是更改后recyclerview每个项目的最终布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="horizontal"
        android:padding="5dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    
        <ImageView
            android:id="@+id/cardIcon"
            android:layout_width="75dp"
            android:layout_height="75dp"
            android:contentDescription="cardIcon"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/avatars" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    
     类似资料:
    • 我正在使用Glide库在imageview中加载图像,并使用以下代码。 灰色占位符在图像未加载之前可见,但在图像在imageview中加载后,占位符仍会出现,并在该图像后显示一些空白。 我如何解决这个问题。如果你有任何想法,请帮助我。

    • 我使用滑翔库来显示网格视图中的图像,但是在我的图像视图中显示了注释。 E/Glide:class com。邦普泰克。滑行负载发动机GlideException:无法加载资源 我的代码在使用位图时工作正常。这是我的密码:

    • 我可以在占位符中加载旋转动画的微调器,直到使用Glide加载图像吗? 我正在尝试使用占位符(R.Drawable.spinner)来实现这一点,没有动画出现? 如果有人能帮我,那太好了? 谢谢

    • 问题内容: 我们正在尝试将 图像预加载 到缓存中,以便稍后加载(图像位于应用程序的 Asset文件夹 中) 我们尝试了什么: 问题:仅当我们尝试加载/显示图像时才对它们进行缓存:必须先将它们加载到内存中,这样它们才能更快地显示出来。 我们还尝试使用GlideModule来增加CacheMemory的大小: 在清单中: 到目前为止没有任何工作。任何想法? 我们尝试使用不可见的1 dp imageVi

    • 我的Build.Gradle文件 UPD.这个简单的应用程序可以加载图片从互联网,但它不能加载图片从我的服务器。我的服务器的一些图片加载得很好,但其他的不是。我已经迷路了

    • 我正在尝试加载带有数据绑定的图像。但我一直没有忘记。我的问题在哪里?下面是我的代码和布局结构。 MyItemViewModel。kt 布局xml