Glide RecyclerView整合的优势有哪些?

16

我刚刚尝试使用Glide与RecyclerView集成,并阅读了相关文档,文档中提到:“RecyclerView集成库使得RecyclerViewPreloader可在您的应用程序中使用。RecyclerViewPreloader可以自动在用户在RecyclerView滚动之前加载图像”,但是我不明白Glide与RecyclerView集成和只使用Glide之间有什么区别,请解释一下Glide RecyclerView集成的优势,并说明如何看出它们之间的区别。

以下是我的代码:

GlideModule.kt

@GlideModule
class GlideModule : AppGlideModule() {
    override fun applyOptions(context: Context?, builder: GlideBuilder?) {
        val requestOp = RequestOptions.noAnimation()
                .priority(Priority.LOW)
        builder?.setDefaultRequestOptions(requestOp)
                ?.setLogLevel(Log.VERBOSE)
        super.applyOptions(context, builder)
    }

    // Disable manifest parsing to avoid adding similar modules twice.
    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}

MyPreloadModelProvide.kt

class MyPreloadModelProvide(val listUrls: List<String>, val context: Context) : PreloadModelProvider<Any> {
    override fun getPreloadItems(position: Int): MutableList<Any> {
        val url = listUrls.get(position)
        if (TextUtils.isEmpty(url)) {
            return Collections.emptyList();
        }
        return Collections.singletonList(url);
    }

    override fun getPreloadRequestBuilder(url: Any?): RequestBuilder<*>? {
        return GlideApp.with(context)
                .load(url)
    }

}

MyAdapter.kt

class MyAdapter(val listUrl: List<String>, val context: Context) : RecyclerView.Adapter<MyViewHolder>() {
    override fun getItemCount(): Int = listUrl.size

    @SuppressLint("CheckResult")
    override fun onBindViewHolder(holder: MyViewHolder?, position: Int) {

        GlideApp.with(context)
                .load(listUrl[position])
                .into(holder?.imageView)

        holder?.imageView?.setOnClickListener { Toast.makeText(context, listUrl[position], Toast.LENGTH_LONG).show() }
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder = MyViewHolder(LayoutInflater.from(parent?.context).inflate(R.layout.item, parent, false))
}

class MyViewHolder(view: View?) : RecyclerView.ViewHolder(view) {
    var imageView: ImageView

    init {
        imageView = view!!.findViewById(R.id.img)
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var preloadSizeProvider: ViewPreloadSizeProvider<Any>

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

        // glide
        var listUrls = listOf(
                "https://img.pokemondb.net/artwork/bulbasaur.jpg",
                "https://img.pokemondb.net/artwork/ivysaur.jpg",
                "https://img.pokemondb.net/artwork/komala.jpg",
                "https://img.pokemondb.net/artwork/turtonator.jpg",
                "https://img.pokemondb.net/artwork/togedemaru.jpg",
                "https://img.pokemondb.net/artwork/mimikyu.jpg",
                "https://img.pokemondb.net/artwork/nihilego.jpg",
                "https://img.pokemondb.net/artwork/buzzwole.jpg",
                "https://img.pokemondb.net/artwork/pheromosa.jpg",
                "https://img.pokemondb.net/artwork/xurkitree.jpg",
                "https://img.pokemondb.net/artwork/celesteela.jpg",
                "https://img.pokemondb.net/artwork/kartana.jpg",
                "https://img.pokemondb.net/artwork/guzzlord.jpg",
                "https://img.pokemondb.net/artwork/necrozma.jpg",
                "https://img.pokemondb.net/artwork/magearna.jpg",
                "https://img.pokemondb.net/artwork/marshadow.jpg"
        )

        preloadSizeProvider = ViewPreloadSizeProvider<Any>()
        val modelProvider = MyPreloadModelProvide(listUrls, this)
        val preloader = RecyclerViewPreloader(GlideApp.with(this), modelProvider, preloadSizeProvider, 2 /*maxPreload*/)

        // recycler view
        recycler_view.layoutManager = LinearLayoutManager(this)
        recycler_view.setHasFixedSize(true)
        recycler_view.adapter = MyAdapter(listUrls, this)

        // THERE ARE NO DIFFERENCES IF I COMMENT THIS LINE
        recycler_view.addOnScrollListener(preloader)
    }
}

如果我注释掉这一行 recycler_view.addOnScrollListener(preloader),不会有任何区别。

4个回答

3
RecyclerView集成库使RecyclerViewPreloader在您的应用程序中可用。
而RecyclerViewPreloader可以自动加载用户在RecyclerView中滚动到的位置之前的图像。

结合正确的图像大小和有效的磁盘缓存策略,该库可以通过确保用户即将到达的图像已经在内存中来显著减少用户在浏览图像列表时看到的加载瓷砖/指示器的数量。

要使用RecyclerView集成库,请在build.gradle文件中添加对它的依赖项:

compile ("com.github.bumptech.glide:recyclerview-integration:4.4.0") {
  /*Excludes the support library 
    because it's already included by Glide.*/
  transitive = false
}

谢谢您的回答,我该如何查看差异? - vuhung3990
3
请使用我们的应用程序在网络连接较慢或使用2G网络时,您将能够看到差异。 - chandrakant sharma
我理解你所说的,但这并不取决于网络类型,因为当启用Glide的日志记录setLogLevel(Log.VERBOSE)时,可以看到它加载相同数量的请求。感谢你的回答,但这还不够。 - vuhung3990
添加依赖关系是不够的。 - apex39
2
这是从Glide文档中复制粘贴的内容,仅返回翻译后的文本。 - Papps

3
你代码的问题在于创建了类型为ViewPreloadSizeProvider的preloadSizeProvider,但是没有对其调用preloadSizeProvider.setView(...)。因此它不知道目标视图的大小,无法预加载正确尺寸的图像,因此看不到任何改进。
我建议首先尝试使用固定大小来解决问题。所以不要使用ViewPreloadSizeProvider,而是创建FixedPreloadSizeProvider(WIDTH, HEIGHT),并确保在通过Glide加载图像时使用相同的尺寸,如下所示:Glide.with(this).load(imageUri).override(WIDTH, HEIGHT).into(imageView);
如果你想检查它是否有效,请启用Glide请求的日志记录,像这样(我在此处使用Java代码,以简化代码):
MyPreloadModelProvide.kt
class MyPreloadModelProvide(val listUrls: List<String>, val context: Context) : PreloadModelProvider<Any> {

    override fun getPreloadRequestBuilder(url: Any?): RequestBuilder<*>? {
        return GlideApp.with(context)
                .override(250, 250)
                .dontTransform()
                .listener(new RequestListener<Drawable>() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                        Log.d("IMAGE PRELOAD", "onResourceReady() called with: model = [" + model + "], target = [" + target + "], dataSource = [" + dataSource + "]");
                        return false;
                    }
                })
                .load(url)
    }

}

MyAdapter.kt

class MyAdapter(val listUrl: List<String>, val context: Context) : RecyclerView.Adapter<MyViewHolder>() {
    override fun getItemCount(): Int = listUrl.size

    @SuppressLint("CheckResult")
    override fun onBindViewHolder(holder: MyViewHolder?, position: Int) {

        GlideApp.with(context)
                .load(listUrl[position])
                .override(250, 250)
                .dontTransform()
                .listener(new RequestListener<Drawable>() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                        Log.d("IMAGE LOAD", "onResourceReady() called with: model = [" + model + "], target = [" + target + "], dataSource = [" + dataSource + "]");
                        return false;
                    }
                })
                .into(holder?.imageView)

        holder?.imageView?.setOnClickListener { Toast.makeText(context, listUrl[position], Toast.LENGTH_LONG).show() }
    }
}

在logcat中,您应该看到PRELOAD请求的数据源为[LOCAL]或[RESOURCE_DISK_CACHE] 而LOAD请求的数据源为[MEMORY_CACHE]

如果它能够正常工作,那就太好了。然后您可以重写它以使用ViewPreloadSizeProvider。在这种情况下,您需要从Glide请求中删除固定图像大小(.override(250,250)),然后不要忘记在例如onCreateViewHolder方法中调用preloadSizeProvider.setView(holder.imageView)


1
假设用户正在滚动加载大型图像的RecyclerView,因此,在看到相应列表项中的图像之前,用户应该等待图像加载的延迟。RecyclerViewPreloader提前加载图像,因此当用户滚动时,它们已经被加载并立即显示。
在您的代码中,您使用了一个小列表和一个小的maxPreload值,因此您不会注意到使用RecyclerViewPreloader的差异。要查看其效果,您应该使用具有许多不同图像的长列表,并快速滚动,并且使用和不使用预加载器。没有预加载器,您将看到空白图像直到加载完成,而有了它,图像应该快速平稳地显示。

1

我之前回答这个问题的答案被删除了,可能是由于版主快速浏览后认为它与问题完全无关,因此我会尝试重新表述并加入一些内容。

Glide有一个RecyclerView集成页面,你可能会认为这是使用Glide在Android RecycerView上运行所必需的方式。但它实际上只添加了一个内存缓存预加载器,这种方法有点复杂; 在RecyclerView的onBindViewHolder中简单地添加Glide.with(view.context).load(url)即可,并且不会导致任何内存泄漏。并且默认情况下,Glide有一个内置的磁盘缓存,效果很好。

所以OP的问题是这个内存预加载器有哪些优势。明显的优势是,在用户滚动到图片之前预加载图片。

但更好的问题是,这些优势是否值得额外的努力来使其工作。这是一个主观的问题。只有互联网连接缓慢的用户才能注意到它,即使是在这种情况下,我也不确定它是否值得麻烦。你不知道用户将要滚动到哪里,你可能会预加载图片,然后用户会滚动到底部,而图片仍需要被新加载。所以在我看来,这不值得麻烦。但你必须自己做出决定。


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