奇怪的位图闪烁
我想优化我的recyclerview,我正在开发一个pdf阅读器,我发现了一个小函数来显示我的pdf文件的第一页:
private fun pdfToBitmap(pdfFile: File): Bitmap? {
var bitmap: Bitmap? = null
try {
val renderer = PdfRenderer(ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY))
val pageCount = renderer.pageCount
if (pageCount > 0) {
val page = renderer.openPage(0)
bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.WHITE)
canvas.drawBitmap(bitmap, page.width.toFloat(), page.height.toFloat(), null)
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
page.close()
renderer.close()
}
} catch (ex: Exception) {
ex.printStackTrace()
}
return bitmap
}
我在默认协程调度程序中执行它,并在主线程中显示它,它给了我这种非常奇怪的行为。预览在它们之间随机交换,而且我想缓存这些位图,也许它可以帮助解决这个问题。
这是我的错误:
谢谢
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
RecyclerView
的工作原理是创建一些项目布局,并重用它们来显示其他项目(回收它们,因此得名)。当ViewHolder
需要显示新项目时,onBindViewHolder
会被调用,这就是您在项目布局中设置视图的位置 - 设置文本、更改图像等。因此,如果ViewHolder #4 显示某个图像,然后向下滚动到重用 ViewHolder #4 的位置,它仍然会显示该图像,直到您更改它。据猜测,您的图像渲染代码非常慢,因此需要很长时间才能真正发生该更改。您还可能会遇到这样的情况:如果滚动得足够远,就会再次使用#4,并且现在您有两个渲染任务正在排队,因此您可能会看到图像更改两次 > 等。
您最简单的解决方案可能是始终显示占位符 PDF 图像,然后触发渲染器任务 - 这样,当您滚动到新项目时,它永远不会暂时显示错误的图像。但是您仍然需要取消过时的协程作业 - 可能值得将
display
函数放在ViewHolder
本身中,这样它就可以触发任务,保留其任务自己的Job
引用,并在新的display
调用进入时取消它。您可以使用缓存,例如 LRU 缓存,或磁盘缓存 - 它肯定会对性能有所帮助,但它使事情变得有点复杂,并且有很多不同的解决方案。如果你想这样做,你就必须做一些调查,看看什么是适合你正在做的事情的正确方法。
这是 Android 文档中关于高效位图加载的页面 - 我'我主要链接这个,因为它们链接到一些您可以研究的推荐库。其中一些处理从文件加载,因此如果您正在执行临时磁盘缓存路由,它可能会很有用。您必须了解将它们与您正在使用的渲染器集成是多么容易(因为您正在做的事情比“加载图像”要复杂一些)
不过,一个建议 - 看起来您正在创建完整 PDF 页面大小的位图。我不确定您是否需要在渲染阶段执行此操作,但您肯定会希望根据列表中图像视图的实际大小调整它们的显示大小。这些库处理智能调整大小以适应视图 - 但即使您不使用这些库,您自己的缓存也将受益于处理更小的位图
RecyclerView
s work by creating a handful of item layouts, and reusing them to display other items (recycling them, hence the name). When aViewHolder
needs to display a new item,onBindViewHolder
gets called, and that's where you set up the views in the item layout - setting text, changing images etc.So if ViewHolder #4 displays a certain image, and then you scroll down to where ViewHolder #4 is reused, it will still display that image until you change it. At a guess, your image rendering code is quite slow, so it takes a long time for that change to actually happen. You also might get a situation where if you scroll far enough, #4 is used again, and now you have two rendering tasks queued up, so you might see the image change twice, etc.
Your simplest solution is probably to always display your placeholder PDF image, then fire off the renderer task - that way when you scroll to a new item, it'll never have the wrong image displayed temporarily. But you'll still need to cancel the stale coroutine job - it might be worth putting the
display
function in theViewHolder
itself, so it can fire off the task, keep its ownJob
reference, and cancel it if a newdisplay
call comes in.You could use a cache, like an LRU cache, or a disk cache - it would definitely help with performance, but it complicates things a little and there are a lot of different solutions out there. You'll have to do some investigation to see what the right approach is for what you're doing, if you want to go that way.
Here's a page from the Android docs about efficient bitmap loading - I'm mainly linking this because they link to some recommended libraries you could look into. Some of them handle loading from files, so if you're doing the temp disk cache route, it could be useful. You'll have to see how easy it is to integrate them with the renderer you're using (since what you're doing is a little more complicated than just "load image")
One suggestion though - it looks like you're creating bitmaps at full PDF page size. I'm not sure if you need to do that for the rendering stage, but you'll definitely want to resize those for display, depending on the actual size of the image view in your list. Those libraries handle intelligent resizing to fit views - but even if you're not using one, your own caching will benefit from handling much smaller bitmaps