Android 生成pdf文件
1.使用官方的方式
使用官方的方式也就是PdfDocument类的使用
1.1 基本使用
/*** * 将tv内容写入到pdf文件 */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void newPdf() { // 创建一个PDF文本对象 PdfDocument document = new PdfDocument(); //创建当前页的信息,Builder中的参数表示页面的宽高,以及第几页 PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(binding.pdfTv.getWidth(), binding.pdfTv.getHeight(), 1) .create(); // 生成当前页 PdfDocument.Page page = document.startPage(pageInfo); // 在当前页上画画,即把所需要的view的视图画到page的画布上 View content = pdfTv; content.draw(page.getCanvas()); // 结束当前页 document.finishPage(page); String filePath = getExternalFilesDir("").getAbsolutePath() + "/test.pdf"; try { FileOutputStream outputStream = new FileOutputStream(new File(filePath)); //写入到文件中 document.writeTo(outputStream); } catch (IOException e) { e.printStackTrace(); } //关闭 document.close(); }
注意事项
1.需要申请写入文件的权限
2.API最低是19,有api版本的限制
1.2 将根布局的内容生成pdf文件
/*** * 保存一个屏幕的内容(包含界面中的 文字、图片、按钮等) */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void pdfTvAndIv() { // 创建一个PDF文本对象 PdfDocument document = new PdfDocument(); //创建当前页的信息,Builder中的参数表示页面的宽高,以及第几页 PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(binding.getRoot().getWidth(), binding.getRoot().getHeight(), 1) .create(); // 生成当前页 PdfDocument.Page page = document.startPage(pageInfo); // 在当前页上画画,即把所需要的view的视图画到page的画布上 View content = binding.getRoot(); content.draw(page.getCanvas()); // 结束当前页 document.finishPage(page); String filePath = getExternalFilesDir("").getAbsolutePath() + "/tviv.pdf"; try { FileOutputStream outputStream = new FileOutputStream(new File(filePath)); document.writeTo(outputStream); } catch (IOException e) { e.printStackTrace(); } document.close(); }
也同样简单。binding.getRoot()就是xml文件的根布局
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> </data> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".pdf.PdfActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/pdf_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" /> <ImageView android:id="@+id/pdf_iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="fitXY" android:src="@mipmap/logo" android:visibility="visible" /> <Button android:id="@+id/pdf1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="官方方式生成pdf" /> </LinearLayout> </ScrollView> </layout>
1.3 TextView有很多行,超过一屏
/*** * 将多行的文字写入到pdf文件 */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void pdfInterviewContent() { TextView tv_content = binding.pdfTv; // 创建一个PDF文本对象 PdfDocument document = new PdfDocument(); // 一页pdf的高度 int onePageHeight = tv_content.getLineHeight() * 30; // TextView中总共有多少行 int lineCount = tv_content.getLineCount(); // 计算这个TextView需要分成多少页 int pdfCount = lineCount % 30 == 0 ? lineCount / 30 : lineCount / 30 + 1; for (int i = 0; i < pdfCount; i++) { PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(tv_content.getWidth(), onePageHeight + 120, 1) .setContentRect(new Rect(0, 60, tv_content.getWidth(), onePageHeight + 60)) .create(); PdfDocument.Page page = document.startPage(pageInfo); Canvas canvas = page.getCanvas(); canvas.translate(0, -onePageHeight * i); tv_content.draw(canvas); document.finishPage(page); } //document = pdfImageviewContent(document); File file = new File(getExternalFilesDir("").getAbsolutePath() + "/test.pdf"); try { FileOutputStream outputStream = new FileOutputStream(file); document.writeTo(outputStream); } catch (IOException e) { e.printStackTrace(); } document.close(); }
1.4 小结
1.保存的文件有些大 2.保存超过一屏的内容有些难,涉及到canvas的移动 3.同时保存文字与图片有些难度(如果超过一屏幕的话,图片保存后会是空白的情况。没有超过一屏,保存后能正常显示) 4.如果界面中有特殊的view,可能会保存失败!比如说SurfaceView。 [附上解决方案的链接](https://www.jianshu.com/p/1ebaf5e6fac1)
2.使用itext的方式
对于Itext,主要有两个版本,一个是5.x,另一个是7.x,这两个版本是完全是不兼容的,其区别可以参考官网:iText 7 and iText 5: roadmaps, differences, updates | iText PDF,
5.x的文档:iText Javadoc Home
7.x的文档:iText Javadoc Home
2.1 7.x
一、引入依赖
//iText 7.0+ implementation 'com.itextpdf:itext7-core:7.1.13'
目前使用的Android Studio版本是4.0.1,我使用的是jdk1.8,不能引入最新的7.1.15版本,会报错
Unsupported class file major version 59
这个问题对jdk有要求的。
二、申请权限
清单文件AndroidManifest.xml
2.1 申请权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
2.2 在<application>中添加
android:requestLegacyExternalStorage="true"
2.3 添加provider
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.demo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
2.4 在res目录下新建xml目录,并创建provider_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
三、创建pdf文件
/** * 创建PDF文件 */ private void createPDF(String path) { if (XXPermissions.isGranted(TextActivity.this, Permission.MANAGE_EXTERNAL_STORAGE)) { File file = new File(path); if (file.exists()) { file.delete(); } file.getParentFile().mkdirs(); // 创建Document PdfWriter writer = null; try { writer = new PdfWriter(new FileOutputStream(path)); } catch (FileNotFoundException e) { Log.e("FileNotFoundException", e.toString()); } PdfDocument pdf_document = new PdfDocument(writer); // 生成的PDF文档信息 PdfDocumentInfo info = pdf_document.getDocumentInfo(); // 标题 info.setTitle("First pdf file"); // 作者 info.setAuthor("Quinto"); // 科目 info.setSubject("test"); // 关键词 info.setKeywords("pdf"); // 创建日期 info.setCreator("2022-10-20"); Document document = new Document(pdf_document, PageSize.A4, false); // 文字字体(显示中文)、大小、颜色 PdfFont font = null; try { font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H"); } catch (IOException e) { Log.e("IOException", e.toString()); } float title_size = 36.0f; float text_title_size = 30.0f; float text_size = 24.0f; Color title_color = new DeviceRgb(0, 0, 0); Color text_title_color = new DeviceRgb(65, 136, 160); Color text_color = new DeviceRgb(43, 43, 43); // 行分隔符 // 实线:SolidLine() 点线:DottedLine() 仪表盘线:DashedLine() LineSeparator separator = new LineSeparator(new SolidLine()); separator.setStrokeColor(new DeviceRgb(0, 0, 68)); // 添加大标题 Text title = new Text("这里是pdf文件标题").setFont(font).setFontSize(title_size).setFontColor(title_color); Paragraph paragraph_title = new Paragraph(title).setTextAlignment(TextAlignment.CENTER); document.add(paragraph_title); for (int i = 1; i < 10; i++) { // 添加文本小标题 Text text_title = new Text("第" + i + "行:").setFont(font).setFontSize(text_title_size).setFontColor(text_title_color); Paragraph paragraph_text_title = new Paragraph(text_title); document.add(paragraph_text_title); // 添加文本内容 String content = "我是文本内容" + i + i + i + i + i + i + i + i + i + i; Text text = new Text(content).setFont(font).setFontSize(text_size).setFontColor(text_color); Paragraph paragraph_text = new Paragraph(text); document.add(paragraph_text); // 添加可换行空间 document.add(new Paragraph("")); // 添加水平线 document.add(separator); // 添加可换行空间 document.add(new Paragraph("")); } /** * 添加列表 */ List list = new List().setSymbolIndent(12).setListSymbol("\u2022").setFont(font); list.add(new ListItem("列表1")) .add(new ListItem("列表2")) .add(new ListItem("列表3")); document.add(list); /** * 添加图片 */ Text text_image = new Text("图片:").setFont(font).setFontSize(text_title_size).setFontColor(text_title_color); Paragraph image = new Paragraph(text_image); document.add(image); Image image1 = null; Image image2 = null; Image image3 = null; try { /*image1 = new Image(ImageDataFactory.create("/storage/emulated/0/DCIM/Camera/IMG_20221003_181926.jpg")).setWidth(PageSize.A4.getWidth() * 2 / 3); image2 = new Image(ImageDataFactory.create("/storage/emulated/0/Download/互传/folder/证件/XHS_159716343059020494b83-da6a-39d7-ae3b-13fd92cfbb53.jpg")).setWidth(PageSize.A4.getWidth() / 3); image3 = new Image(ImageDataFactory.create("/storage/emulated/0/Download/互传/folder/证件/XHS_1597163520524f0b4df77-8db1-35c6-9dfa-3e0aa74f1fef.jpg")).setWidth(PageSize.A4.getWidth() / 3);*/ image1 = new Image(ImageDataFactory.create("/storage/emulated/0/Pictures/JPEG_20230609_154240_8941441911022988116.jpg")).setWidth(PageSize.A4.getWidth() * 2 / 3); image2 = new Image(ImageDataFactory.create("/storage/emulated/0/Tencent/QQ_Images/-318f738d395e5630.jpg")).setWidth(PageSize.A4.getWidth() / 3); image3 = new Image(ImageDataFactory.create("/storage/emulated/0/Pictures/Screenshots/Screenshot_20230615_145522_com.hermes.wl.jpg")).setWidth(PageSize.A4.getWidth() / 3); } catch (MalformedURLException e) { Log.e("MalformedURLException", e.toString()); } Paragraph paragraph_image = new Paragraph().add(image1) .add(" ") .add(image2) .add(" ") .add(image3); document.add(paragraph_image); document.add(new Paragraph("")); /** * 添加表格 */ Text text_table = new Text("表单:").setFont(font).setFontSize(text_title_size).setFontColor(text_title_color); Paragraph paragraph_table = new Paragraph(text_table); document.add(paragraph_table); // 3列 float[] pointColumnWidths = {100f, 100f, 100f}; Table table = new Table(pointColumnWidths); // 设置边框样式、颜色、宽度 Color table_color = new DeviceRgb(80, 136, 255); Border border = new DottedBorder(table_color, 3); table.setBorder(border); // 设置单元格文本居中 table.setTextAlignment(TextAlignment.CENTER); // 添加单元格内容 Color table_header = new DeviceRgb(0, 0, 255); Color table_content = new DeviceRgb(255, 0, 0); Color table_footer = new DeviceRgb(0, 255, 0); Text text1 = new Text("姓名").setFont(font).setFontSize(20.0f).setFontColor(table_header); Text text2 = new Text("年龄").setFont(font).setFontSize(20.0f).setFontColor(table_header); Text text3 = new Text("性别").setFont(font).setFontSize(20.0f).setFontColor(table_header); table.addHeaderCell(new Paragraph(text1)); table.addHeaderCell(new Paragraph(text2)); table.addHeaderCell(new Paragraph(text3)); Text text4 = new Text("张三").setFont(font).setFontSize(15.0f).setFontColor(table_content); Text text5 = new Text("30").setFont(font).setFontSize(15.0f).setFontColor(table_content); Text text6 = new Text("男").setFont(font).setFontSize(15.0f).setFontColor(table_content); table.addCell(new Paragraph(text4)); table.addCell(new Paragraph(text5)); table.addCell(new Paragraph(text6)); Text text7 = new Text("丽萨").setFont(font).setFontSize(15.0f).setFontColor(table_footer); Text text8 = new Text("20").setFont(font).setFontSize(15.0f).setFontColor(table_footer); Text text9 = new Text("女").setFont(font).setFontSize(15.0f).setFontColor(table_footer); table.addFooterCell(new Paragraph(text7)); table.addFooterCell(new Paragraph(text8)); table.addFooterCell(new Paragraph(text9)); // 将表格添加进pdf文件 document.add(table); /** * 添加页眉、页脚、水印 */ Rectangle pageSize; PdfCanvas canvas; int n = pdf_document.getNumberOfPages(); Log.i("zxd", "createPDF: " + n); for (int i = 1; i <= n; i++) { PdfPage page = pdf_document.getPage(i); Log.i("zxd", "createPDF page: " + page.getPageSize()); pageSize = page.getPageSize(); canvas = new PdfCanvas(page); // 页眉 canvas.beginText().setFontAndSize(font, 7) .moveText(pageSize.getWidth() / 2 - 18, pageSize.getHeight() - 10) .showText("我是页眉") .endText(); // 页脚 canvas.setStrokeColor(text_color) .setLineWidth(.2f) .moveTo(pageSize.getWidth() / 2 - 30, 20) .lineTo(pageSize.getWidth() / 2 + 30, 20).stroke(); canvas.beginText().setFontAndSize(font, 7) .moveText(pageSize.getWidth() / 2 - 6, 10) .showText(String.valueOf(i)) .endText(); // 水印 Paragraph p = new Paragraph("Quinto").setFontSize(60); canvas.saveState(); PdfExtGState gs1 = new PdfExtGState().setFillOpacity(0.2f); canvas.setExtGState(gs1); document.showTextAligned(p, pageSize.getWidth() / 2, pageSize.getHeight() / 2, pdf_document.getPageNumber(page), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45); canvas.restoreState(); } // 关闭 document.close(); Toast.makeText(this, "PDF文件已生成", Toast.LENGTH_SHORT).show(); } else { //没权限 //requestPermission(); } }
注意事项
1.pdfdocument.getPageSize()未设置为对象iText7的实例,获取页面大小的时候竟然报错,原因是立即刷新的参数设置成true了
可以告诉文档
在默认情况下不要刷新其内容,方法是将false
传递给构造函数中的第三个参数(immediateflush
)
Document document = new Document(pdf_document, PageSize.A4, false);
pdfdocument.getPageSize()未设置为对象iText7的实例
2.如果图片资源没找到,会报io错误。最好在使用前,先判断下图片是否存在!
2.2 7.x的基础使用
2.2.1 显示中文
itext7在使用默认字体显示中文的时候,由于默认字库不支持中文,生成的pdf中的中文会显示空白,要解决这个问题需要自己引入字体。
1.下载一个中文的字体(.ttf文件,SourceHanSansCN.ttf)
2.加载本地字体样式
//InputStream inputStream = new FileInputStream(new File("SourceHanSansCN.ttf")); //第三个参数为embedded,是否为内置字体,这里是自己提供的所以传false //PdfFont font = PdfFontFactory.createFont(IOUtils.toByteArray(inputStream), PdfEncodings.IDENTITY_H, false); //Android 一般都是从资产文件获取字体,然后使用字体的byte[]创建禹卫硬笔字体 try { InputStream open = getAssets().open("禹卫硬笔.ttf"); byte[] datas = new byte[open.available()]; open.read(datas); font = PdfFontFactory.createFont(datas, PdfEncodings.IDENTITY_H, false); } catch (IOException e) { Log.e("IOException", e.toString()); }
3.设置字体
//默认是A4纸大小 Document document = new Document(pdfDocument, new PageSize()); document.setFont(font);
2.2.2 分辨率
如果有pdf打印的需求,涉及到分辨率的问题。
在itext中除了插入的图片外其他都是矢量图所以不必担心分辨率的问题,只需要保证插入图片的分辨率即可,在itext中生成图片的分辨率默认是72dpi,这里是需要做一些处理的,如下:
int defaultDpi = 72; int targetDpi = 300; Image test = new Image(ImageDataFactory.create("test.jpg")); test.scale(defaultDpi/targetDpi, defaultDpi/targetDpi);
2.2.3 布局
Itext坐标系如下图,当使用绝对布局的时候需要计算元素距离左侧和下侧的长度,当使用相对布局的时候,元素则是自上往下排列。
绝对布局
setFixedPosition(float left, float bottom, float width)
相对布局
使用相对布局时,不需要setFixedPosition,只需要设置距离上下左右的长度即可。
setMarginLeft(float value); setMarginRight(float value); setMarginBottom(float value); setMarginTop(float value); setMargin(float commonMargin);
2.2.4 自动分页+生成页码
在使用绝对布局的时候,大部分情况下页面内容是固定大小的,位置也是固定的,分页也需要自己写代码来进行控制。但是如果pdf的内容不是固定的,这时候如果使用绝对布局就不是那么的灵活了,还需要自己计算元素的高度也设置绝对位置,这个时候我们选择使用相对布局则会更合适、简单一些。代码如下: class PageEventHandler implements IEventHandler { private Document document; private PdfFont font; //由于需要统一和汉字的字体,所以加了一个pdffont参数,没有此需求的可以不加 public PageEventHandler(Document document, PdfFont font){ this.document = document; this.font = font; } @Override public void handleEvent(Event event) { PdfDocumentEvent pdfEvent = (PdfDocumentEvent) event; PdfPage page = pdfEvent.getPage(); PdfDocument pdfDoc = pdfEvent.getDocument(); //获取当前页码 int pageNumber = pdfDoc.getPageNumber(page); PdfCanvas pdfCanvas = new PdfCanvas( page.newContentStreamBefore(), page.getResources(), pdfDoc); //在距离上个元素50的距离处写页码 this.document.setTopMargin(50f); pdfCanvas.beginText() .setFontAndSize(this.font, 32) //在页面一般宽的地方写上当前页面,由于字体定位的原点也在左下角,所以这里x轴减掉了页码宽度的一半,确保页码是写在中间的位置 //y轴是距离底部20px .moveText(PageSize.A4.getWidth()/2-32/2, 20f) .showText(String.valueOf(pageNumber)) .endText(); pdfCanvas.release(); } } //使用 pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, new PageEventHandler(document, font));
2.2.5 常用组件
table
//不指定table每一列的宽度,根据列的内容自适应 public Table(int numColumns); //指定每一列的宽度 public Table(float[] pointColumnWidths); //向table里面添加单元格,默认是横着加,达到指定列数后,自动换行 public Table addCell(Cell cell); //设置table或cell的边框,这里需要注意,如果不想显示边框,需要在每一个cell中都设置border为none,只设置table的边框还是会显示边框的 public T setBorder(Border border); //无边框 cell.setBorder(Border.NO_BORDER); //实线边框 cell.setBorder(new SolidBorder(1)); //虚线边框 cell.setBorder(new DashedBorder(1)); //四个都是圆角 cell.setBorderRadius(new BorderRadius(10f)); //圆角-右下角 cell.setBorderBottomRightRadius(new BorderRadius(10f)); //圆角-左下角 cell.setBorderBottomLeftRadius(new BorderRadius(10f)); //圆角-右上角 cell.setBorderTopRightRadius(new BorderRadius(10f)); //圆角-左上角 cell.setBorderTopLeftRadius(new BorderRadius(10f)); //关于圆角这一部分,table中的圆角,似乎设置了并不能生效,不过可以通过将外围的table的border设置为none,内部的cell设置边框实现
用table实现一个进度条
-
思路:外层一个一行一列的table,内部cell再套一个一行一列的table作为实际的进度,根据百分比计算内部table的宽度
//percent这里传的是小数 public void processBar(Document document, float width, float percent){ float processWidth = width * percent; DeviceRgb processColor = new DeviceRgb(11,11,11);//随便写个颜色吧 DeviceRgb processBgColor = new DeviceRgb(101,11,11);//随便写个颜色吧 Table table = new Table(new float[]{width}).setMargin(0).setPadding(0);//把不必要的空隙都扼杀在摇篮里 Table processTable = new Table(new float[]{processWidth}).setMargin(0).setPadding(0); processTable.addCell(new Cell().setBorder(Border.NO_BORDER) .setMargin(0).setPadding(0).setBackgroundColor(processColor)); table.addCell(new Cell().setBorder(Border.NO_BORDER).setBackgroundColor(processBgColor) .setMargin(0).setPadding(0).setBorder(Border.NO_BORDER).add(processTable)); }
Paragraph
paragraph应该是最经常使用的一个类了,pdf中写文字都会用到这个类。
Paragraph para = new Paragraph("我是一个paragraph") .setFontSize(14)//设置字体大小 .setBold()//设置文字为粗体 .setFontColor(new DeviceRgb(0,0,0))//设置字体颜色 .setTextAlignment(TextAlignment.CENTER)//文字水平居中 .setFixedLeading(14);//类似于css中的行高
有的时候会有一些需求是,同一段落的一段文字中有部分文字需要设置成别的颜色或样式样式,这时候可以Text来处理,如下:
//这种处理方式不能处理加粗的问题,如果想要加粗的文字正好在段落的中间,但是如果设置为粗体会导致整段文字都变成粗体(斜体是同理的) para.add(new Text("我想与众不同").setFontSize("20").setFontColor(new DeviceRgb(255,255,255)));
Image
//图片分辨率问题见上面分辨率部分 Image image = new Image(ImageDataFactory.create("/home/test.jpg"));
2.2.6 基本使用
private void pdf1() { path = getExternalFilesDir("").getAbsolutePath() + "/itext_basic.pdf"; PdfWriter writer;//创建一个写入传递的输出流的 PdfWriter。 try { writer = new PdfWriter(new FileOutputStream(path)); PdfFont font = PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf); Text text = new Text("Hello World PDF created using iText") .setFont(font) .setFontSize(15) .setFontColor(ColorConstants.MAGENTA); //Add paragraph to the document document.add(new Paragraph(text)); document.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
2.2.7 字体样式
try { PdfDocument pdf = new PdfDocument(new PdfWriter(path)); PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER); Style style = new Style().setFont(font) .setFontSize(14) .setFontColor(ColorConstants.RED) .setBackgroundColor(ColorConstants.YELLOW); Document document = new Document(pdf); document.add(new Paragraph() .add("In this PDF, ") .add(new Text("Text is styled").addStyle(style)) .add(" using iText ") .add(new Text("Style").addStyle(style)) .add(".")); document.close(); } catch (IOException e) { e.printStackTrace(); }
2.2.8 txt转pdf
private void pdf3(String source, String des) { try { BufferedReader br = new BufferedReader(new FileReader(source)); PdfDocument pdf = new PdfDocument(new PdfWriter(des)); Document document = new Document(pdf); String line; PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER); while ((line = br.readLine()) != null) { document.add(new Paragraph(line).setFont(font)); } br.close(); document.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
2.3 打开pdf
/** * 打开PDF文件 */ private void openPDF() { new Handler().postDelayed(new Runnable() { @Override public void run() { try { FileUtils.openFile(context, new File(path)); } catch (Exception e) { Log.e("Exception", e.toString()); } } }, 1000); }
打开PDF文件
/** * 打开PDF文件 * * @param context * @param url * @throws ActivityNotFoundException * @throws IOException */ public static void openFile(Context context, File url) throws ActivityNotFoundException { if (url.exists()) { Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", url); String urlString = url.toString().toLowerCase(); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); /** * Security */ List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } // 通过比较url和扩展名,检查您要打开的文件类型。 // 当if条件匹配时,插件设置正确的意图(mime)类型 // 所以Android知道用什么程序打开文件 if (urlString.toLowerCase().contains(".doc") || urlString.toLowerCase().contains(".docx")) { // Word document intent.setDataAndType(uri, "application/msword"); } else if (urlString.toLowerCase().contains(".pdf")) { // PDF file intent.setDataAndType(uri, "application/pdf"); } else if (urlString.toLowerCase().contains(".ppt") || urlString.toLowerCase().contains(".pptx")) { // Powerpoint file intent.setDataAndType(uri, "application/vnd.ms-powerpoint"); } else if (urlString.toLowerCase().contains(".xls") || urlString.toLowerCase().contains(".xlsx")) { // Excel file intent.setDataAndType(uri, "application/vnd.ms-excel"); } else if (urlString.toLowerCase().contains(".zip") || urlString.toLowerCase().contains(".rar")) { // ZIP file intent.setDataAndType(uri, "application/trap"); } else if (urlString.toLowerCase().contains(".rtf")) { // RTF file intent.setDataAndType(uri, "application/rtf"); } else if (urlString.toLowerCase().contains(".wav") || urlString.toLowerCase().contains(".mp3")) { // WAV/MP3 audio file intent.setDataAndType(uri, "audio/*"); } else if (urlString.toLowerCase().contains(".gif")) { // GIF file intent.setDataAndType(uri, "image/gif"); } else if (urlString.toLowerCase().contains(".jpg") || urlString.toLowerCase().contains(".jpeg") || urlString.toLowerCase().contains(".png")) { // JPG file intent.setDataAndType(uri, "image/jpeg"); } else if (urlString.toLowerCase().contains(".txt")) { // Text file intent.setDataAndType(uri, "text/plain"); } else if (urlString.toLowerCase().contains(".3gp") || urlString.toLowerCase().contains(".mpg") || urlString.toLowerCase().contains(".mpeg") || urlString.toLowerCase().contains(".mpe") || urlString.toLowerCase().contains(".mp4") || urlString.toLowerCase().contains(".avi")) { // Video files intent.setDataAndType(uri, "video/*"); } else { // 如果你愿意,你也可以为任何其他文件定义意图类型 // 另外,使用下面的else子句来管理其他未知扩展 // 在这种情况下,Android将显示设备上安装的所有应用程序 // 因此您可以选择使用哪个应用程序 intent.setDataAndType(uri, "*/*"); } intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } else { Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show(); } }
manifest.xml
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.zg.pdfdemo.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
provider_paths
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external_files" path="."/> </paths>
2.4 pdfRender的简单使用
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void pdf4() { path = getExternalFilesDir("").getAbsolutePath() + "/itext_basic.pdf"; try { // 1.创建文件 File pdfFile = new File(path); // 2.获取 ParcelFileDescriptor 对象 ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY); // 3.创建PdfRenderer对象 PdfRenderer renderer = new PdfRenderer(parcelFileDescriptor); //渲染page数据到bitmap //1、获取页码数据Page对象 PdfRenderer.Page page = renderer.openPage(0); int pageCount = renderer.getPageCount(); Log.i("zxd", "pdf4: 总数=" + pageCount); //2.创建ARGB_8888 的bitmap Bitmap bitmap = Bitmap.createBitmap(page.getWidth(), page.getHeight(), Bitmap.Config.ARGB_8888); //3.渲染 page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY); //渲染到iv中 binding.iv.setImageBitmap(bitmap); //关闭当前page数据 page.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
总结
优点:本身是Android提供的,比较轻量无需依赖第三方SDK,核心代码都是native实现,执行效率比较高
缺点:只能在Android5.0 或以上版本使用,由于实现方式是native 所以无法自己定制渲染算法,以及方式、使用时还需要自己控制线程安全比较繁琐
参考: