【问题标题】:Create a PDF Viewer in Jetpack Compose using PdfRenderer使用 PdfRenderer 在 Jetpack Compose 中创建 PDF 查看器
【发布时间】:2021-11-12 12:46:15
【问题描述】:

我正在尝试使用PdfRendererCoil 创建一个可组合的PDF 查看器,用于将位图加载到LazyColumn 中。 这是我到目前为止得到的:

@Composable
fun PdfViewer(
    modifier: Modifier = Modifier,
    uri: Uri,
    verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp)
) {
    val loaderScope = rememberCoroutineScope()
    val renderer = remember(uri) {
        val input = ParcelFileDescriptor.open(uri.toFile(), ParcelFileDescriptor.MODE_READ_ONLY)
        PdfRenderer(input)
    }
    val context = LocalContext.current
    val mutex = remember { Mutex() }
    val imageLoader = LocalContext.current.imageLoader
    BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
        val width = with(LocalDensity.current) { maxWidth.toPx() }.toInt()
        val height = (width * sqrt(2f)).toInt()
        LazyColumn(
            verticalArrangement = verticalArrangement
        ) {
            items(
                count = renderer.pageCount,
                key = { it }
            ) { index ->
                val cacheKey = MemoryCache.Key("$uri-$index")
                val bitmap = remember(uri, index) {
                    val cachedBitmap = imageLoader.memoryCache[cacheKey]
                    if (cachedBitmap != null) cachedBitmap else {
                        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
                        loaderScope.launch(Dispatchers.IO) {
                            mutex.withLock {
                                Timber.d("Loading $uri - page $index")
                                renderer.openPage(index).use {
                                    it.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
                                }
                            }
                        }
                        bitmap
                    }
                }
                val request = ImageRequest.Builder(context)
                    .size(width, height)
                    .memoryCacheKey(cacheKey)
                    .data(bitmap)
                    .build()

                Image(
                    modifier = Modifier.background(Color.White).aspectRatio(1f / sqrt(2f)).fillMaxWidth(),
                    contentScale = ContentScale.Fit,
                    painter = rememberImagePainter(request),
                    contentDescription = "Page ${index + 1} of ${renderer.pageCount}"
                )
            }
        }
    }
}

这种方法有效,但是当第一次加载位图时,它不会显示在列表中,直到我滚动(即重绘之后)。我想利用LazyColumn 的功能,只在PDF 页面可见时才加载它们。

有没有更好的方法来实现这一点?

【问题讨论】:

    标签: android kotlin pdf android-jetpack-compose coil


    【解决方案1】:

    我设法解决了它:

    @Composable
    fun PdfViewer(
        modifier: Modifier = Modifier,
        uri: Uri,
        verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp)
    ) {
        val rendererScope = rememberCoroutineScope()
        val mutex = remember { Mutex() }
        val renderer by produceState<PdfRenderer?>(null, uri) {
            rendererScope.launch(Dispatchers.IO) {
                val input = ParcelFileDescriptor.open(uri.toFile(), ParcelFileDescriptor.MODE_READ_ONLY)
                value = PdfRenderer(input)
            }
            awaitDispose {
                val currentRenderer = value
                rendererScope.launch(Dispatchers.IO) {
                    mutex.withLock {
                        currentRenderer?.close()
                    }
                }
            }
        }
        val context = LocalContext.current
        val imageLoader = LocalContext.current.imageLoader
        val imageLoadingScope = rememberCoroutineScope()
        BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
            val width = with(LocalDensity.current) { maxWidth.toPx() }.toInt()
            val height = (width * sqrt(2f)).toInt()
            val pageCount by remember(renderer) { derivedStateOf { renderer?.pageCount ?: 0 } }
            LazyColumn(
                verticalArrangement = verticalArrangement
            ) {
                items(
                    count = pageCount,
                    key = { index -> "$uri-$index" }
                ) { index ->
                    val cacheKey = MemoryCache.Key("$uri-$index")
                    var bitmap by remember { mutableStateOf(imageLoader.memoryCache[cacheKey]) }
                    if (bitmap == null) {
                        DisposableEffect(uri, index) {
                            val job = imageLoadingScope.launch(Dispatchers.IO) {
                                val destinationBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
                                mutex.withLock {
                                    Timber.d("Loading PDF $uri - page $index/$pageCount")
                                    if (!coroutineContext.isActive) return@launch
                                    try {
                                        renderer?.let {
                                            it.openPage(index).use { page ->
                                                page.render(
                                                    destinationBitmap,
                                                    null,
                                                    null,
                                                    PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY
                                                )
                                            }
                                        }
                                    } catch (e: Exception) {
                                        //Just catch and return in case the renderer is being closed
                                        return@launch
                                    }
                                }
                                bitmap = destinationBitmap
                            }
                            onDispose {
                                job.cancel()
                            }
                        }
                        Box(modifier = Modifier.background(Color.White).aspectRatio(1f / sqrt(2f)).fillMaxWidth())
                    } else {
                        val request = ImageRequest.Builder(context)
                            .size(width, height)
                            .memoryCacheKey(cacheKey)
                            .data(bitmap)
                            .build()
    
                        Image(
                            modifier = Modifier.background(Color.White).aspectRatio(1f / sqrt(2f)).fillMaxWidth(),
                            contentScale = ContentScale.Fit,
                            painter = rememberImagePainter(request),
                            contentDescription = "Page ${index + 1} of $pageCount"
                        )
                    }
                }
            }
        }
    }
    

    这也应该处理 pdf 渲染器的处置。

    【讨论】:

      【解决方案2】:

      使用 Jetpack Compose 显示 PDF 的绝佳方式。

      但是,要正确使用,您必须更改此行:

      var bitmap by remember { mutableStateOf(imageLoader.memoryCache[cacheKey]) }
      

      通过这个:

      var bitmap by remember { mutableStateOf(imageLoader.memoryCache?.get(cacheKey) as? Bitmap?) }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-12
        • 2022-07-16
        • 2020-02-24
        • 2022-10-15
        • 2021-05-23
        • 1970-01-01
        • 1970-01-01
        • 2022-08-22
        相关资源
        最近更新 更多