基于Jetpack Compose与PdfRenderer实现PDF查看器的优化咨询
Compose PDF查看器优化方案
现有问题根因
- 位图加载后不自动刷新:你通过
remember存储的Bitmap是普通不可观察对象,协程中完成位图渲染后Compose无法感知内容变化,不会触发重组刷新Image组件,只有滚动触发Item重绑定才会读取到最新的Bitmap内容 - 未实现可见区懒加载:现有逻辑只要Item被预组合就会触发加载,不会判断是否进入可视区域
优化后实现代码
@Composable fun PdfViewer( modifier: Modifier = Modifier, uri: Uri, verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp) ) { val context = LocalContext.current val mutex = remember { Mutex() } val imageLoader = LocalContext.current.imageLoader // 初始化PdfRenderer,增加资源释放逻辑 val renderer = remember(uri) { // 兼容Content Uri的写法:val input = context.contentResolver.openFileDescriptor(uri, "r") val input = ParcelFileDescriptor.open(uri.toFile(), ParcelFileDescriptor.MODE_READ_ONLY) PdfRenderer(input) } DisposableEffect(renderer) { onDispose { renderer.close() } } 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") // 用State持有Bitmap,更新自动触发重组 var bitmap by remember(uri, index) { mutableStateOf<Bitmap?>(imageLoader.memoryCache[cacheKey] as? Bitmap) } // 只有Item进入可视区时才会执行加载逻辑 LaunchedEffect(uri, index) { if (bitmap != null) return@LaunchedEffect val targetBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) mutex.withLock { Timber.d("Loading $uri - page $index") renderer.openPage(index).use { it.render(targetBitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) } } // 存入内存缓存 imageLoader.memoryCache[cacheKey] = targetBitmap // 更新状态触发重绘 bitmap = targetBitmap } val request = ImageRequest.Builder(context) .size(width, height) .memoryCacheKey(cacheKey) .data(bitmap) // 可自定义加载中占位符 .placeholder(Color.White) .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}" ) } } } }
核心优化点
- 采用
mutableStateOf持有Bitmap实例,渲染完成后更新状态自动触发UI重组,彻底解决加载完成不显示的问题 - 用
LaunchedEffect包裹加载逻辑,只有LazyColumn的Item进入可视区域时才会触发加载,Item滑出可视区时未完成的加载协程会自动取消,避免无用资源消耗 - 新增
DisposableEffect处理PdfRenderer的资源释放,避免内存泄漏 - 支持扩展加载中占位符、加载失败重试等自定义UI逻辑
内容的提问来源于stack exchange,提问作者Filippo Vigani
相关产品推荐
相关产品推荐

