Android中如何使用Java将Recyclerview内容转换为PDF文件?
Android Java实现RecyclerView转PDF的完整方案
1. 前置权限配置
先在AndroidManifest.xml中添加存储相关权限:
<!-- Android 12及以下存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13及以上媒体文件读取权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
Android 6.0及以上版本需要在代码中动态申请上述权限,获取用户授权后再执行PDF生成逻辑。
2. 核心实现逻辑
2.1 获取RecyclerView全部条目的拼接Bitmap
由于RecyclerView只会渲染可见区域的条目,需要手动遍历所有条目测量并拼接为完整Bitmap:
private Bitmap getRecyclerViewFullBitmap(RecyclerView recyclerView) { RecyclerView.Adapter<?> adapter = recyclerView.getAdapter(); if (adapter == null) return null; int itemCount = adapter.getItemCount(); int totalHeight = 0; Bitmap fullBitmap = null; Canvas canvas = null; for (int i = 0; i < itemCount; i++) { // 创建并绑定ViewHolder int viewType = adapter.getItemViewType(i); RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, viewType); adapter.onBindViewHolder(holder, i); // 测量Item尺寸 holder.itemView.measure( View.MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ); holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight()); // 生成单条目的Bitmap holder.itemView.setDrawingCacheEnabled(true); holder.itemView.buildDrawingCache(); Bitmap itemBitmap = Bitmap.createBitmap(holder.itemView.getDrawingCache()); holder.itemView.setDrawingCacheEnabled(false); // 初始化总Bitmap if (fullBitmap == null) { fullBitmap = Bitmap.createBitmap( holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight() * itemCount, Bitmap.Config.ARGB_8888 ); canvas = new Canvas(fullBitmap); } // 把单条目Bitmap绘制到总Bitmap上 canvas.drawBitmap(itemBitmap, 0, totalHeight, null); totalHeight += holder.itemView.getMeasuredHeight(); itemBitmap.recycle(); } return fullBitmap; }
2.2 将Bitmap写入PDF文件
使用Android系统自带的PdfDocument类实现PDF生成,无需引入第三方依赖:
private void saveBitmapToPdf(Bitmap sourceBitmap, File outputFile) throws IOException { PdfDocument pdfDocument = new PdfDocument(); // 创建和Bitmap尺寸匹配的PDF页面 PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder( sourceBitmap.getWidth(), sourceBitmap.getHeight(), 1 ).create(); PdfDocument.Page page = pdfDocument.startPage(pageInfo); // 把Bitmap绘制到PDF页面 page.getCanvas().drawBitmap(sourceBitmap, 0, 0, null); pdfDocument.finishPage(page); // 写入到输出文件 FileOutputStream fos = new FileOutputStream(outputFile); pdfDocument.writeTo(fos); // 资源释放 pdfDocument.close(); fos.close(); sourceBitmap.recycle(); }
2.3 生成PDF输出文件路径
适配不同Android版本的存储规则:
private File getPdfOutputFile() throws IOException { String fileName = "RecyclerView内容_" + System.currentTimeMillis() + ".pdf"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10及以上保存到公共下载目录 ContentValues values = new ContentValues(); values.put(MediaStore.Downloads.DISPLAY_NAME, fileName); values.put(MediaStore.Downloads.MIME_TYPE, "application/pdf"); values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); Uri fileUri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (fileUri == null) throw new IOException("文件创建失败"); return new File(getRealPathFromUri(fileUri)); } else { // 低版本直接操作本地存储 File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); if (!downloadDir.exists()) downloadDir.mkdirs(); return new File(downloadDir, fileName); } } // Uri转本地路径辅助方法 private String getRealPathFromUri(Uri uri) { String path = null; Cursor cursor = getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DATA}, null, null, null); if (cursor != null && cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)); cursor.close(); } return path; }
2.4 调用入口
建议在子线程执行转换逻辑,避免阻塞主线程:
public void startConvert(RecyclerView targetRecyclerView) { new Thread(() -> { try { Bitmap fullBitmap = getRecyclerViewFullBitmap(targetRecyclerView); if (fullBitmap == null) { runOnUiThread(() -> Toast.makeText(this, "列表内容获取失败", Toast.LENGTH_SHORT).show()); return; } File pdfFile = getPdfOutputFile(); saveBitmapToPdf(fullBitmap, pdfFile); runOnUiThread(() -> Toast.makeText(this, "PDF已保存到下载目录", Toast.LENGTH_LONG).show()); } catch (Exception e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(this, "生成失败:" + e.getMessage(), Toast.LENGTH_SHORT).show()); } }).start(); }
3. 优化建议
- 若RecyclerView条目数量过多,拼接后的单Bitmap过大可能触发OOM,可按照A4纸标准尺寸分页渲染,每渲染满一页就新增一个PDF页面。
- 若条目包含网络图片,需等图片加载完成后再生成对应条目的Bitmap,避免出现空白区域。
- 可以添加PDF水印、页脚页码等自定义内容,直接在PdfDocument的Canvas上绘制即可。
内容的提问来源于stack exchange,提问作者Priyadharshini Baskaran
相关产品推荐
相关产品推荐

