
示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载本文围绕 cw-omnibus 仓库中的 PdfRenderer 示例应用 展开系统讲解如何借助 Android SDK 自带的android.graphics.pdf.PdfRenderer在应用内直接渲染并浏览 PDF 文件。该示例是The Busy Coders Guide to Android Development一书中 PDF 章节的配套源码完整覆盖了用系统文档选择器挑选 PDF → 通过ParcelFileDescriptor打开 → 逐页渲染为 Bitmap → 翻页与缩放浏览的整条链路。读完本文你将掌握PdfRenderer的完整生命周期用法、位图渲染的参数细节以及如何用RecyclerViewSnapHelper 缩放图片控件搭建一个可横向翻页、可双指缩放的 PDF 阅读器。一、PdfRenderer 是什么何时选用android.graphics.pdf.PdfRenderer是 Android 5.0API 21Lollipop起由系统提供的 PDF 渲染器它基于系统内置的原生 PDF 解析能力不需要引入任何第三方解析库即可把 PDF 每一页渲染成一张 Android 位图Bitmap随后交给任何图片控件展示。它的适用前提非常明确设备/运行环境必须是API 21 及以上渲染的输入必须是一个可随机访问的ParcelFileDescriptor即已打开的文件描述符而不是裸的 Uri 或字节流它只负责把某页画到位图上不负责交互缩放、翻页动画等交互层需要自己搭建。在 app/build.gradle 中可以看到本示例的minSdkVersion 21、compileSdkVersion 25与PdfRenderer的 API 21 门槛完全吻合——这正是一个专为 Lollipop 及以上版本设计的示例。相比之下如果应用需要支持更低版本 Android就得退而求其次选用 Pdfium 示例基于com.github.barteksc:android-pdf-viewerminSdk 16或 PdfJS 示例WebView 方案。二、示例应用整体结构整个示例只有三个 Java 类职责划分非常清晰类职责MainActivity.java文档选择、状态保存、RecyclerView翻页容器、生命周期收尾PageAdapter.javaPdfRenderer的创建/关闭、页数查询、按位置openPage()PageController.java把某页渲染进Bitmap并交给SubsamplingScaleImageView展示核心工作流是用户点击菜单栏的Pick PDF→ 系统文档选择器返回一个Uri→ 通过ContentResolver.openFileDescriptor()拿到ParcelFileDescriptor→PageAdapter用它构造PdfRenderer→ 列表按页渲染、横向滑动浏览。三、选择 PDF 文档从 Uri 到 ParcelFileDescriptor示例没有内置任何 PDF 资源而是把选择权交给用户。菜单点击后MainActivity.java 发起系统文档选择private void open() { Intent inew Intent() .setType(application/pdf) .setAction(Intent.ACTION_OPEN_DOCUMENT) .addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(i, REQUEST_OPEN); }要点说明ACTION_OPEN_DOCUMENT是 Android 4.4API 19引入的 Storage Access Framework 入口弹出的是文档提供者如文件管理器界面而非简单的文件选择器应用因此无需申请存储权限即可读取所选文档.setType(application/pdf)把候选范围过滤为 PDF 文件CATEGORY_OPENABLE确保返回的 Uri 可以被打开读取REQUEST_OPEN常量在本示例中取值为1337仅用于区分不同请求。用户在结果回调中选择的 Uri 可能是临时授权temporary grant的因此onActivityResult里直接保存该 Uri 并使用protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCodeActivity.RESULT_OK) { pickedDocumentdata.getData(); show(pickedDocument); } }关键的打开动作发生在 show() 方法private void show(Uri uri) { try { adapternew PageAdapter(getLayoutInflater(), getContentResolver().openFileDescriptor(uri, r)); pager.setAdapter(adapter); } catch (java.io.IOException e) { Log.e(PdfRenderer, getString(R.string.toast_open), e); Toast.makeText(this, R.string.toast_open, Toast.LENGTH_LONG).show(); } }这里openFileDescriptor(uri, r)以只读方式打开文档并返回ParcelFileDescriptor——这正是PdfRenderer构造器要求的输入类型。整个流程会抛出IOException例如文件被删除、Uri 授权失效示例用 Toast 提示Could not open that document!定义于 strings.xml并记录日志。四、PdfRenderer 核心用法打开、计数、逐页渲染与关闭4.1 构造与关闭PageAdapter.java 在构造时创建渲染器PageAdapter(LayoutInflater inflater, ParcelFileDescriptor pfd) throws IOException { this.inflaterinflater; renderernew PdfRenderer(pfd); }而关闭则交给了 Activity 的生命周期。PdfRenderer内部持有原生资源必须显式释放否则会泄漏文件描述符与内存// MainActivity.onDestroy() protected void onDestroy() { if (adapter!null) { adapter.close(); } super.onDestroy(); } // PageAdapter.close() void close() { renderer.close(); }注意两点一是PdfRenderer.close()之后不能再使用二是ParcelFileDescriptor的生命周期与PdfRenderer绑定——本示例让渲染器持有 pfd 并在销毁时一并释放符合谁创建谁负责的资源管理原则。4.2 页数查询RecyclerView.Adapter必须知道条目总数这里直接取自渲染器Override public int getItemCount() { return(renderer.getPageCount()); }getPageCount()返回文档总页数之后列表的每个位置都对应一页。4.3 打开与关闭单页onBindViewHolder 中每次绑定条目时打开对应页Override public void onBindViewHolder(PageController holder, int position) { PdfRenderer.Page pagerenderer.openPage(position); holder.setPage(page); page.close(); }这是PdfRenderer的一个强制性使用规则openPage()返回的PdfRenderer.Page必须在用完后立刻调用close()并且在页面关闭期间不能再次调用openPage()同一渲染器同时只允许打开一页。示例严格遵循打开一页 → 渲染 → 关闭的短生命周期模式holder.setPage(page)内部同步完成渲染后再返回因此page.close()是安全的。五、页渲染细节位图尺寸、底色与渲染模式真正的渲染动作发生在 PageController.javavoid setPage(PdfRenderer.Page page) { if (bitmapnull) { int height2000; int widthheight * page.getWidth() / page.getHeight(); bitmapBitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } bitmap.eraseColor(0xFFFFFFFF); page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY); iv.resetScaleAndCenter(); iv.setImage(ImageSource.cachedBitmap(bitmap)); }几个值得展开的细节位图尺寸由页宽高比推导。page.getWidth()/page.getHeight()返回的是 PDF 页面的固有尺寸单位点示例固定渲染高度 2000px宽度按height × width/height等比计算。这样既保证所有页的渲染分辨率一致又避免拉伸变形。可以推算一页 A4 比例宽高比约 0.707的文档会得到约 2000×1414 的位图在ARGB_8888格式下约占用 11MB 内存——对单页浏览来说是可接受的开销但若要渲染大量页面仍需注意内存复用策略。位图只创建一次并复用。bitmapnull判断意味着只有第一页会触发createBitmap之后所有页共用同一块位图内存每次渲染前用bitmap.eraseColor(0xFFFFFFFF)擦成纯白底色防止上一页残留这是避免为每一页重复分配大位图的有效手段。渲染模式用RENDER_MODE_FOR_DISPLAY。PdfRenderer.Page.render()的签名是page.render(bitmap, destClip, transform, renderMode);destClip第二参数为null表示渲染整页到整个位图transform第三参数为null表示不做矩阵变换直接按页比例绘制渲染模式有两种RENDER_MODE_FOR_DISPLAY面向屏幕显示本示例采用RENDER_MODE_FOR_PRINT面向打印输出二者内部选用的图形后端不同不应混用。渲染结果交给缩放图片控件。示例引入第三方库subsampling-scale-image-view依赖声明见 app/build.gradle用ImageSource.cachedBitmap(bitmap)把位图喂给SubsamplingScaleImageView配合resetScaleAndCenter()在换页时重置缩放与居中状态从而获得双指缩放、平移查看细节的能力。六、翻页浏览RecyclerView PagerSnapHelper渲染好的页面通过横向滚动的RecyclerView组织起来。MainActivity.onCreate 中的搭建逻辑pager(RecyclerView)findViewById(R.id.pager); pager.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); snapperCarr.attachToRecyclerView(pager);LinearLayoutManager.HORIZONTAL让列表横向滑动一屏一页snapperCarr是PagerSnapHelperv7 支持库中的 SnapHelper 子类它把横向滚动的RecyclerView改造成一页对齐一屏的翻页效果滑动后自动吸附到最近的页面行为接近ViewPager对应布局 main.xml 仅包含一个RecyclerView根节点直接使用android.support.v7.widget.RecyclerView并设置了半透明灰色背景与clipToPaddingfalse。每个列表项由 page.xml 定义根节点就是SubsamplingScaleImageView四周留有 16dp 边距com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView android:idid/page xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:layout_margin16dp /菜单项定义在 pdf.xml图标使用 ic_folder_open_white_24dp.xml一个 Material 风格的 VectorDrawableshowAsActionalways使其常驻 ActionBar。七、配置变更与进程重建Uri 的保存与恢复文档 Uri 是应用运行状态的一部分。如果旋转屏幕或进程被系统回收Activity 会重建此时必须恢复之前选中的文档否则阅读位置会丢失。示例用标准的onSaveInstanceState机制处理Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(STATE_PICKED, pickedDocument); } // onCreate 中恢复 if (savedInstanceState!null) { pickedDocumentsavedInstanceState.getParcelable(STATE_PICKED); if (pickedDocument!null) { show(pickedDocument); } }因为Uri实现了Parcelable可以直接存入Bundle键名为STATE_PICKED值为picked。恢复时再次走show()通过openFileDescriptor重新打开文档并重建PdfRenderer——这也是为什么每次展示都新建PageAdapter而销毁时统一close()的原因渲染器无法跨 Activity 重建存活。八、工程配置速览构建参数与主题示例作为一个独立 Gradle 工程关键配置如下模块构建配置app/build.gradlecompileSdkVersion 25、buildToolsVersion 26.0.2、minSdkVersion 21、targetSdkVersion 21依赖只有两个——com.android.support:recyclerview-v7:25.1.0提供RecyclerView与PagerSnapHelper和com.davemorrissey.labs:subsampling-scale-image-view:3.6.0缩放展示。整个工程不依赖任何 PDF 解析库这正是PdfRenderer方案的标志性特征。顶层构建配置build.gradle使用 Android Gradle Plugin3.1.4仓库源为google()与jcenter()Gradle wrapper 指向 Gradle 4.4见 gradle-wrapper.properties。仓库根目录的 README.markdown 也提示该仓库出于安全考虑不内置gradlew可执行文件命令行构建需要自备对应版本的 Gradle。主题与配色主题定义在 values/styles.xmlHolo.Light.DarkActionBar 兜底与 values-v21/styles.xmlMaterial.Light.DarkActionBar 主色/强调色颜色值在 values-v21/colors.xml主色#3f51b5、深主色#1a237e、强调色#fbc02d——由于minSdk 21起步就是 Material 主题v21 版本必然生效。ManifestAndroidManifest.xml只有一个MainActivity作为 launcher声明了supports-screens关闭小屏、应用图标与标签无需任何权限声明文档读取走 SAF不涉及存储权限。九、与仓库内其他 PDF 方案的定位差异cw-omnibus 的PDF/目录下还有另外两个示例三者正好构成系统 API / 第三方库 / Web 方案的对照PdfRenderer本示例minSdk 21系统内置渲染能力零第三方 PDF 依赖代码量最小但交互缩放、翻页需要自行组合控件Pdfium 示例PDF/Pdfium基于android-pdf-viewer底层为 PDFiumminSdk 16通过PDFView.Configurator一行式配置滑动、双指缩放、滚动条适合需要兼容旧系统且不想手写交互的场景其 MainActivity.java 还演示了从 asset 加载 PDF 的用法PdfJS 示例PDF/PdfJS把开源 PDF.js 前端库装进 WebView渲染与交互全部由网页完成适合需要复杂排版还原能力的场景。三者各有利弊选择时主要权衡点在于最低支持版本、是否允许引入第三方库、以及所需的交互复杂度。十、关键注意事项清单综合源码实现使用PdfRenderer时有以下几点必须遵守版本门槛PdfRenderer仅 API 21 可用低版本需走其他方案输入必须是ParcelFileDescriptor从Uri获取请使用ContentResolver.openFileDescriptor(uri, r)并处理IOException单页短生命周期openPage()后必须close()且同一渲染器同时只能打开一页显式关闭渲染器在 Activity/Fragment 的销毁回调中调用renderer.close()防止文件描述符与原生资源泄漏位图内存可控优先复用同一块Bitmap并用eraseColor清空渲染高度如 2000px需根据内存预算调整渲染模式选型屏幕浏览用RENDER_MODE_FOR_DISPLAY打印输出用RENDER_MODE_FOR_PRINTUri 状态保存旋转或进程重建后靠onSaveInstanceState保存的 Uri 重新打开文档重建渲染器。掌握以上要点后参照 MainActivity.java、PageAdapter.java 与 PageController.java 三份源码即可在自己的应用中快速落地一个选择文档 → 逐页渲染 → 翻页缩放阅读的轻量 PDF 阅读器。赞分享示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载相关推荐Android PdfRenderer基本示例项目推荐Android PdfRenderer基本示例项目推荐 项目介绍 在数字化阅读日益普及的今天处理PDF文档成为了一个不可或缺的需求。 Android PdfR移动开发示例工程终极免费文档下载工具kill-doc让文档下载变得简单高效终极免费文档下载工具kill doc让文档下载变得简单高效 你是否曾经为了下载一份急需的学习资料在各种文档平台间反复切换忍受着烦人的登录验证、广告弹窗和示例工程教程Android PdfRenderer基本使用教程Android PdfRenderer基本使用教程 项目介绍 Google Samples 提供的 android PdfRendererBasic 是一个示例移动开发示例工程上一篇终极指南VSCode Bookmarks扩展完全入门教程下一篇Bundlephobia数据库查询优化避免N1查询问题创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考