Android 基于POI库,根据模板导出word文档

前言

由于项目需求,需要根据用户提供的word模板,填充动态内容生成新的word,为了记录自己的踩坑日记,记录一下。


一、POI是什么?

Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对文档读和写的功能。

这里给出官网链接-POI官网,同时下载版本也在官网链接中,可进行对应版本下载。

同时在了解POI库的过程中,还了解到poi-ti库,也就是word模板引擎,基于Apache POI,其实也就是在POI库的基础上做了一层API的封装,对应jar包中是包含了poi的,这里给出中文文档有兴趣的可以了解下-poi-ti API文档,源码github链接-poi-ti源码。这个开源库对java做了很好的适配,后台开发如果涉及到操作word,excel等文档读写完全可以使用,使用也很简单,只需要根据中文文档集成即可。

唯一遗憾的是poi-ti该库的作者只对java库做了适配,确不适用Android,我在研究过程中引入jar包确一直无法编译通过。如果大家还想使用poi-ti 大家可以移步另一位博主写的博客,这里给出链接-Android使用poi_ti生成word,这位博主使用低版本的poi-ti在Android 端完成了编译通过,我也有测试使用,但是不符合我项目要求,果断放弃。

本文主要使用poi来完成对word的读写以及excel文档的读写。

二、使用步骤

1.下载对应库并引入

有2种方式,一种直接下载对应版本的jar包,链接这-jar下载链接,另外一种是gradle依赖方式。

注意在这里有一个坑,word分为doc和docx,excel也分为.xls和.xlsx,针对这个poi也做了区分,如下表:

类型 API 对应jar
doc/xls

HWPF

(HWPFDocument-doc格式文件对象

/HSSFWorkbook-xls格式文件对象)

poi-scratchpad
docx/xlsx

XWPF

(XWPFDocument- docx格式文件对象

/XSSFWorkbook-.xlsx格式文件对象)

poi-ooxml

我使用的是docx/xlsx,对应gradle中的依赖如下所示:

Android 基于POI库,根据模板导出word文档_第1张图片

2.word的读写(docx)

读写word docx文件通过xwpf模块来进行的,核心API为XWPFDocument。一个XWPFDocument代表一个docx文档,其可以用来读docx写文档。本文用到XWPFDocument中有下面这几种对象:

XWPFParagraph 一个段落
XWPFRun 具有相同属性的一段文本
XWPFTable 一个表格
XWPFTableRow 表格的一行
XWPFTableCell 表格对应的一个单元格

介绍完对应API后我们开始通过模板来动态填充模板中的内容以及表格,从而生成新的模板。文中给出我们项目中的word模板,如下:

Android 基于POI库,根据模板导出word文档_第2张图片

可以看出基本信息一栏是固定的,检测数据下面一栏是动态列表。针对固定的我们可以采取通过poi进行读模板,读取到后将对应标记的{{a}}等等字符串给替换成我们真正需要的值。那针对动态列表的渲染,我们可以通过循环的方式,一直复制当前行,给下面循环插入一行来完成。代码如下:

Android 基于POI库,根据模板导出word文档_第3张图片

DocxTool类中export封装的方法主要方法是下面这个:由于隐私原因数据就不做展示,我都是从本地Android sqlite中取得,可以根据实际业务来做调整。
HashMap map = new HashMap<>();为文档中标记的{{a}}等等字符串,用来做替换,
List labors = new ArrayList<>();为封装的动态渲染表格数据。
XWPFTableRow twoRow = table.getRow(copyRow)以测试模板中第7行固定作为动态表格复制,
XWPFTableRow row = table.insertNewTableRow(goodsStartRow++);然后向下面循环插入,插入后在对每个单元格进行数据赋值。

Android 基于POI库,根据模板导出word文档_第4张图片

DocxTool类中searchAndReplace封装的方法则是替换并导出,主要方法如下:
    public static void searchAndReplace(Activity activity, String Name, XWPFDocument document, HashMap map) {

        try {
            /**
             * 替换表格中的指定文字
             */
            //获取文档中所有的表格,每个表格是一个元素
            Iterator itTable = document.getTablesIterator();
            while (itTable.hasNext()) {
                XWPFTable table = (XWPFTable) itTable.next();   //获取表格内容
                int count = table.getNumberOfRows();    //表格的行数
                //遍历表格行的对象
                for (int i = 0; i < count; i++) {
                    XWPFTableRow row = table.getRow(i);    //表格每行的内容
                    List cells = row.getTableCells();   //每个单元格的内容
                    //遍历表格的每行单元格对象
                    for (int j = 0; j < cells.size(); j++) {
                        XWPFTableCell cell = cells.get(j);    //获取每个单元格的内容
                        List paragraphs = cell.getParagraphs();      //获取单元格里所有的段落
                        for (XWPFParagraph paragraph : paragraphs) {
                            //获取段落的内容
                            List run = paragraph.getRuns();
                            // 遍历段落文字对象
                            for (int o = 0; o < run.size(); o++) {
                                // 获取段落对象
                                if (run.get(o) == null || run.get(o).equals("")) {
                                    continue;
                                }
                                String sectionItem = run.get(o).getText(run.get(o).getTextPosition());    //获取段落内容
                                if (sectionItem == null || sectionItem.equals("")) {    //段落为空跳过
                                    continue;
                                }
                                //遍历自定义表单关键字,替换Word文档中表格单元格的内容
                                for (String key : map.keySet()) {
                                    // 替换内容
                                    sectionItem = sectionItem.replace(key, String.valueOf(map.get(key)));
                                    run.get(o).setText(sectionItem, 0);
                                }
                            }
                        }
                    }
                }
            }
            //创建生成的文件
            String ps = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
            File path = new File(ps, "docx-export");
            if (!path.exists()) {
                path.mkdir();
            }
            String date = Utils.getCurTimeString().replace("-", "").replace(" ", "").replace(":", "");
            String dirName = date + ".docx";
            File File = new File(path, dirName);
            if (File.exists()) {
                File.delete();
            }

            FileOutputStream outputStream = new FileOutputStream(File);
            document.write(outputStream);
            document.close();
            outputStream.flush();
            outputStream.close();


            new Handler(Looper.getMainLooper()).post(() -> {

                ExprotWordDialog wordDialog = new ExprotWordDialog(activity, s -> {
                    Uri photoURI = null;
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    try {
                        if (Build.VERSION.SDK_INT >= 24) {
                            photoURI = FileProvider.getUriForFile(activity,
                                    "自己的包名" + ".file.provider",
                                    new File(File.getPath()));
                            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);//给目标文件临时授权
                        } else {
                            photoURI = Uri.fromFile(new File(File.getPath()));
                        }
                        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        intent.setDataAndType(photoURI, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                        activity.startActivity(intent);
                    } catch (Exception e) {
                        Log.e("=", e.toString());
                        Toast.makeText(activity, "查看文档出错", Toast.LENGTH_SHORT).show();
                    }


                });
                wordDialog.show();
                wordDialog.setFilename("文件名:" + dirName);
                wordDialog.setPath("本地存储路径:" + path.getPath());


            });

        } catch (Exception e) {
            Log.e("==", e.toString() + "替换docx出错");
            e.printStackTrace();
        }
    }

到此,模板数据填充以及动态列表渲染就结束了。

3.excel的读写(xlxs)

arraylist为封装得动态数据

Android 基于POI库,根据模板导出word文档_第5张图片


总结

记录下来,也是为了记录自己的踩坑日记。

你可能感兴趣的:(word,android,apache)