Android含ScrollView的RelativeLayout完整转PDF问题求助
解决ScrollView嵌套ListView生成PDF仅显示部分内容的问题
我之前也碰到过一模一样的问题——ScrollView里套ListView,直接拿getDrawingCache生成Bitmap转PDF时,ListView只会渲染当前屏幕可见的item,而且整个布局的高度测量也不对,导致PDF只显示部分内容。下面给你几个可行的解决思路,按推荐程度排序:
方法一:改用iText直接构建PDF(最推荐)
用Bitmap转PDF不仅容易出现内容截断,还会有分辨率低、文件大的问题。直接用iText的API把每个部分的内容手动添加到PDF里,既灵活又可靠。
步骤:
- 先修正你代码里的路径错误(原来的
pdfFolder + timeStamp + ".pdf"会导致路径拼接错误,应该用File.separator分隔) - 用iText的
Document、PdfPTable、Paragraph等组件逐个添加发票的各个部分:商家信息、客户信息、产品表头、产品列表、总计等。
示例代码:
private void createPdfWithiText() throws IOException, DocumentException { File pdfFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "pdfdemo"); if (!pdfFolder.exists()) { pdfFolder.mkdirs(); // 用mkdirs确保多级目录都能创建 } String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); // 正确拼接路径 File myFile = new File(pdfFolder, timeStamp + ".pdf"); OutputStream output = new FileOutputStream(myFile); // 初始化PDF文档,用A4纸 Document document = new Document(PageSize.A4); PdfWriter.getInstance(document, output); document.open(); // 1. 添加商家信息 Paragraph businessName = new Paragraph("Business Name", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 16)); businessName.setAlignment(Element.ALIGN_CENTER); document.add(businessName); document.add(new Paragraph("Business Address", FontFactory.getFont(FontFactory.HELVETICA, 14))); document.add(new Paragraph("Business contactNo", FontFactory.getFont(FontFactory.HELVETICA, 14))); // 添加黑色分隔线 document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1)); document.add(new Paragraph("\n")); // 换行 // 2. 添加客户信息,用表格排版更整齐 PdfPTable customerTable = new PdfPTable(2); customerTable.setWidthPercentage(100); customerTable.addCell(new Phrase("GSTIN:", FontFactory.getFont(FontFactory.HELVETICA_BOLD))); customerTable.addCell(new Phrase("xyz1234566", FontFactory.getFont(FontFactory.HELVETICA))); customerTable.addCell(new Phrase("Bill date:", FontFactory.getFont(FontFactory.HELVETICA_BOLD))); customerTable.addCell(new Phrase("20-12-2017", FontFactory.getFont(FontFactory.HELVETICA))); document.add(customerTable); document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1)); document.add(new Paragraph("\n")); // 3. 添加产品表头 PdfPTable productHeader = new PdfPTable(3); productHeader.setWidthPercentage(100); productHeader.addCell(new Phrase("Product Name", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14))); productHeader.addCell(new Phrase("GST%", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14))); productHeader.addCell(new Phrase("Total", FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14))); document.add(productHeader); document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1)); // 4. 添加产品列表 PdfPTable productTable = new PdfPTable(3); productTable.setWidthPercentage(100); for (ItemModel item : itemsList) { productTable.addCell(new Phrase(item.getProductName(), FontFactory.getFont(FontFactory.HELVETICA, 12))); productTable.addCell(new Phrase(String.valueOf(item.getGstPercent()), FontFactory.getFont(FontFactory.HELVETICA, 12))); productTable.addCell(new Phrase(String.valueOf(item.getTotal()), FontFactory.getFont(FontFactory.HELVETICA, 12))); } document.add(productTable); document.add(new LineSeparator(3f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1)); document.add(new Paragraph("\n")); // 5. 添加总计 Paragraph netTotal = new Paragraph("Net Total: " + invoiceTotal, FontFactory.getFont(FontFactory.HELVETICA_BOLD, 14)); netTotal.setAlignment(Element.ALIGN_CENTER); document.add(netTotal); document.close(); // 生成后可以打开PDF Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(myFile), "application/pdf"); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent); }
方法二:修复Bitmap生成逻辑,确保完整渲染布局
如果你坚持要用Bitmap转PDF,需要先手动计算ListView的总高度,强制它渲染所有item,再重新测量整个ScrollView的高度。
步骤:
- 写一个方法计算ListView的总高度(包括所有item和分割线)
- 在生成Bitmap前,设置ListView的高度为计算出的总高度
- 重新测量并布局整个根布局,再生成Bitmap
示例代码:
// 计算ListView总高度的工具方法 private int getListViewTotalHeight(ListView listView) { ListAdapter adapter = listView.getAdapter(); if (adapter == null || adapter.getCount() == 0) return 0; int totalHeight = 0; // 遍历所有item,计算每个item的高度 for (int i = 0; i < adapter.getCount(); i++) { View listItem = adapter.getView(i, null, listView); // 测量item的高度 listItem.measure( View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ); totalHeight += listItem.getMeasuredHeight(); } // 加上分割线的总高度 totalHeight += listView.getDividerHeight() * (adapter.getCount() - 1); // 加上ListView的padding totalHeight += listView.getPaddingTop() + listView.getPaddingBottom(); return totalHeight; } // 修改你的createPdf方法 private void createPdf() throws IOException, DocumentException { File pdfFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "pdfdemo"); if (!pdfFolder.exists()) pdfFolder.mkdirs(); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); File myFile = new File(pdfFolder, timeStamp + ".pdf"); OutputStream output = new FileOutputStream(myFile); // 先处理ListView,强制显示所有item int listTotalHeight = getListViewTotalHeight(listView); ViewGroup.LayoutParams listParams = listView.getLayoutParams(); listParams.height = listTotalHeight; listView.setLayoutParams(listParams); // 重新测量根布局的完整高度 main.setDrawingCacheEnabled(true); // 强制测量布局的完整尺寸 main.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ); main.layout(0, 0, main.getMeasuredWidth(), main.getMeasuredHeight()); main.buildDrawingCache(true); Bitmap b = Bitmap.createBitmap(main.getDrawingCache()); main.setDrawingCacheEnabled(false); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); b.compress(Bitmap.CompressFormat.PNG, 100, bytes); // 用PNG比JPEG更清晰,没有压缩失真 Image myImg = Image.getInstance(bytes.toByteArray()); // 调整图片适应PDF页面,这里用A4纸 Document document = new Document(PageSize.A4); PdfWriter.getInstance(document, output); document.open(); // 缩放图片到A4纸宽度 myImg.scaleToFit(document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin(), document.getPageSize().getHeight()); myImg.setAlignment(Element.ALIGN_CENTER); document.add(myImg); document.close(); // 打开PDF Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(myFile), "application/pdf"); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent); }
额外注意事项
- 先修正你XML布局里的语法错误:
- 有一处
<TextView <TextView的标签重复,要删除多余的标签 <View android:id="@+id/v4"标签没有闭合,要加上/>
- 有一处
- 安卓10及以上版本访问外部存储需要申请
MANAGE_EXTERNAL_STORAGE权限,或者用MediaStore来保存文件,避免权限问题 - 用iText的时候,要确保依赖的版本正确,比如在build.gradle里添加:
implementation 'com.itextpdf:itextg:5.5.10'
内容的提问来源于stack exchange,提问作者Neo
相关产品推荐
相关产品推荐

