PDF 查看与渲染
源:PDF 库
在 Android Jetpack 中,处理 PDF 主要有两种方式:使用新的 Jetpack PDF 库 (androidx.pdf) 快速集成 PDF 查看器,或者使用原生的 PdfRenderer 进行底层的页面渲染。
1. Jetpack PDF 库 (推荐)
Android X PDF 库(androidx.pdf:pdf-viewer-fragment)允许你在应用中嵌入一个功能齐全的 PDF 查看器,它支持缩放、滚动、搜索和文本选择等功能。这是在 Android App 中显示 PDF 的最简单方法。
注意:该库目前处于 Alpha 阶段 (截至 2024 年),API 可能会发生变化。它通常要求 API Level 35 (Android 15) 或通过 SDK 扩展支持到更低版本 (Android 11+, API 30+ via SdkExtensions)。
添加依赖
kotlin
[versions]
pdf-viewer = "1.0.0-alpha01"
[libraries]
androidx-pdf-viewer-fragment = { group = "androidx.pdf", name = "pdf-viewer-fragment", version.ref = "pdf-viewer" }kotlin
dependencies {
implementation(libs.androidx.pdf.viewer.fragment)
}使用 PdfViewerFragment
你可以将 PdfViewerFragment 添加到你的 Activity 或 Fragment 中。
kotlin
import androidx.pdf.viewer.fragment.PdfViewerFragment
import android.net.Uri
class PdfActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pdf)
// 1. 创建 Fragment 实例
val pdfFragment = PdfViewerFragment()
// 2. 将 Fragment 添加到容器中
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, pdfFragment)
.commit()
// 3. 加载 PDF 文档 (必须是本地文件 Uri 或 Content Uri)
// 假设你已经有了一个 PDF 文件的 Uri
val fileUri: Uri = ...
// 注意:DocumentUri 是必须的
pdfFragment.documentUri = fileUri
}
}布局文件示例
xml
<!-- activity_pdf.xml -->
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />2. PdfRenderer (底层渲染)
如果你需要将 PDF 页面渲染为图片(Bitmap)以便在 ImageView 中显示,或者需要完全自定义 UI,可以使用 android.graphics.pdf.PdfRenderer。这是 Android 5.0 (API 21) 引入的原生 API。
使用步骤
- 打开 ParcelFileDescriptor:
PdfRenderer需要一个ParcelFileDescriptor指向 PDF 文件。 - 创建 PdfRenderer: 使用文件描述符初始化。
- 打开页面: 使用
openPage(index)打开特定页面。 - 渲染: 使用
render()方法将页面内容绘制到 Bitmap 上。 - 关闭: 使用完后必须关闭页面和渲染器。
代码示例
kotlin
import android.graphics.Bitmap
import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import android.widget.ImageView
import java.io.File
class SimplePdfRenderer(private val file: File) {
private var pdfRenderer: PdfRenderer? = null
private var currentPage: PdfRenderer.Page? = null
private var parcelFileDescriptor: ParcelFileDescriptor? = null
init {
openRenderer()
}
private fun openRenderer() {
parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
if (parcelFileDescriptor != null) {
pdfRenderer = PdfRenderer(parcelFileDescriptor!!)
}
}
fun renderPageToBitmap(pageIndex: Int, imageView: ImageView) {
if (pdfRenderer == null || pdfRenderer!!.pageCount <= pageIndex) return
// 1. 确保之前的页面已关闭
currentPage?.close()
// 2. 打开新页面
currentPage = pdfRenderer!!.openPage(pageIndex)
// 3. 创建 Bitmap (宽高根据页面尺寸或 ImageView 尺寸决定)
val bitmap = Bitmap.createBitmap(
currentPage!!.width,
currentPage!!.height,
Bitmap.Config.ARGB_8888
)
// 4. 渲染内容到 Bitmap
// null 表示渲染整个页面,RENDER_MODE_FOR_DISPLAY 表示为屏幕显示优化
currentPage!!.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
// 5. 显示 Bitmap
imageView.setImageBitmap(bitmap)
}
fun close() {
currentPage?.close()
pdfRenderer?.close()
parcelFileDescriptor?.close()
}
}注意事项
- 线程安全:
PdfRenderer不是线程安全的,建议在后台线程中执行渲染操作。 - 资源管理: 务必在使用后调用
close()方法,否则可能会导致内存泄漏或文件锁定。 - 页面限制: 一次只能打开一个页面。在打开新页面前必须关闭当前页面。