如何使用 PDFRender 更高效地渲染 pdf 并在 JetPack Compose 的 LazyColumn 中显示

How to do more efficient render a pdf with PDFRender and show in LazyColumn in JetPack Compose

我想从 FireStorage 下载一个 pdf 文件,呈现该文件,将每个页面(如 ImageBitMap)添加到列表中并显示在 LazyColumn 中。问题是尝试滚动非常慢,甚至在页面切换时冻结 4-5 秒。

关于如何使其更高效、更快速的任何想法?是否有 JetPack Compose 库可用于改进它?

谢谢

PDFScreen.kt

@Composable
fun PDFScreen(obraId: ObraId, autorId: AuthorId) {

    val storageRef = FirebaseStorage.getInstance().reference
    val pathReference = storageRef.child("obras/${autorId}/${obraId}.pdf")
    val localFile = File.createTempFile("obra", "pdf")
    var imageList = remember { mutableStateListOf<ImageBitmap?>(null) }

    pathReference.getFile(localFile).addOnSuccessListener {
        imageList.clear()
        if (it.task.isSuccessful) {
            val input = ParcelFileDescriptor.open(localFile, ParcelFileDescriptor.MODE_READ_ONLY)
            val renderer = PdfRenderer(input)

            for (i in 0 until renderer.pageCount) {

                val page = renderer.openPage(i)
                val bitmap =
                    Bitmap.createBitmap(Constants.width, Constants.height, Bitmap.Config.ARGB_8888)
                page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
                imageList.add(bitmap.asImageBitmap())
                page.close()
                
            }
            renderer.close()
        }
    }

    LazyColumn(modifier = Modifier.fillMaxSize()) {
        items(items = imageList) { imagen ->

            if (imagen != null) {

                Image(
                    modifier = Modifier
                        .fillMaxSize(), bitmap = imagen, contentDescription = "Prueba"
                )
            }
        }
    }
}

您不应进行任何繁重的计算或直接从可组合函数中 db/network 调用。

所有复合函数都是视图构造函数,并且可以在可变状态的值发生变化时重新配置(例如,重新调用),对于动画 - 直到每一帧。在您的代码中,getFile 查询在将新项目添加到可变状态列表时执行,从而形成无限循环。

我建议你从解释基本原理的state in compose documentation, including this youtube video开始。

在您的情况下,应该将 fething 代码移至 view modelPDFScreenViewModel 对象将在视图出现时创建,并在视图离开视图层次结构时销毁:

class PDFScreenViewModel: ViewModel() {
    val imageList = mutableStateListOf<ImageBitmap?>(null)
    
    private val storageRef = FirebaseStorage.getInstance().reference
    private val pathReference = storageRef.child("obras/${autorId}/${obraId}.pdf")
    private val localFile = File.createTempFile("obra", "pdf")
    
    init {
        load()
    }
    
    fun load() {
        pathReference.getFile(localFile).addOnSuccessListener {
            imageList.clear()
            if (it.task.isSuccessful) {
                val input = ParcelFileDescriptor.open(localFile, ParcelFileDescriptor.MODE_READ_ONLY)
                val renderer = PdfRenderer(input)

                for (i in 0 until renderer.pageCount) {

                    val page = renderer.openPage(i)
                    val bitmap =
                        Bitmap.createBitmap(Constants.width, Constants.height, Bitmap.Config.ARGB_8888)
                    page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
                    imageList.add(bitmap.asImageBitmap())
                    page.close()

                }
                renderer.close()
            }
        }
    }
}

@Composable
fun PDFScreen(obraId: ObraId, autorId: AuthorId) {
    val viewModel: PDFScreenViewModel = viewModel()
    LazyColumn(modifier = Modifier.fillMaxSize()) {
        items(items = viewModel.imageList) { imagen ->

            if (imagen != null) {

                Image(
                    modifier = Modifier
                        .fillMaxSize(), bitmap = imagen, contentDescription = "Prueba"
                )
            }
        }
    }
}