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

Time:09-22

i want to download a pdf file from FireStorage, render the file, add every page like ImageBitMap in a List and show in a LazyColumn. The problem is that it is very slow trying to scroll, even freezing 4-5 secs when page change.

Any ideas on how to make it more efficient and faster? Is there a library for JetPack Compose that I can use to make this better?

Thanks

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"
                )
            }
        }
    }
}

CodePudding user response:

You should not make any heavy calculations or db/network calls directly from the composable functions.

All composite functions are view constructors and can be reconfigured (e.g., recalled) whenever the values of a changeable state change, and for animations - up to every frame. In your code, getFile query is executed when a new item is added to the list of changeable states making an endless cycle.

I suggest you start with the state in compose documentation, including this youtube video which explains the basic principles.

In your case, the fething code should be moved to view model: The PDFScreenViewModel object will be created when the view appears and destroyed when the view leaves the view hierarchy:

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"
                )
            }
        }
    }
}
  • Related