系列文章:
Java 操作 Office:POI 之 word 生成
Java 操作 Office:POI 之 word 图片处理
Java 操作 Office:POI word 之网络图片处理
一 概述
小结失败。因为之前生成的 word 文档,表格格式不够美观,所以最近又花时间做了一些研究和调整。在这里分享一点不算经验的经验。
二 原来的效果
旧版效果如下图所示:
其实已经做了一点格式调整,就是单元格合并(横纵方向),一级宽度设置。但在编写代码时,经常发现一些基础宽度不生效,或者被文字挤掉之后,宽度显示异常的情况。所以最近抽时间又思考了一下,怎样合理的调整格式。
三 调整过程
3.1 预期效果
我们预期的效果,主要是试图固定前面几行的列宽,让最下面的"公司章程" 和 明细 这行保持相对合理的宽度。
3.2 基础设置
我们再看看表格的基础设置方法,首先还是定义一个 3 行 4 列的表格。为什么没有直接定义 7 行 4 列?这是因为开始的时候,曾经试图拆分成多个独立表格来实现效果,不过感觉是失败了。。 poi 有自动合并表格的操作。但出于方便调试的考虑,这里还是保留了拆分子表的设置。
XWPFTable table = doc.createTable(3, 4);
TableTools.widthTable(table, MiniTableRenderData.WIDTH_A4_FULL, 4);
复制代码
先设置前 3 行,这 3 行的列宽样式是一致的。考虑设定第 1 列的宽度为文档宽度的 1/8,第 2 列的宽度为 3/8,第 3 列的宽度为文档宽度的 1/8,第 4 列的宽度为 3/8。
3.2.1 获取表格宽度
直接使用 table.getWidth()即可。
3.2.2 设置列宽度
XWPFTableCell 的 setWidth()方法,但需要注意的是,参数不是整数 或 浮点数,而是字符串。从下面代码中可以看出,最终是通过 CTTblWidth 对向设置列(表格)的宽度。
public void setWidth(String widthValue) {
XWPFTable.setWidthValue(widthValue, this.getTcWidth());
}
复制代码
protected static void setWidthValue(String widthValue, CTTblWidth ctWidth) {
if (!widthValue.matches("auto|[0-9]+|[0-9]+(\\.[0-9]+)?%")) {
throw new RuntimeException("Table width value \"" + widthValue + "\" must match regular expression \"" + "auto|[0-9]+|[0-9]+(\\.[0-9]+)?%" + "\".");
} else {
if (widthValue.matches("auto")) {
ctWidth.setType(STTblWidth.AUTO);
ctWidth.setW(BigInteger.ZERO);
} else if (widthValue.matches("[0-9]+(\\.[0-9]+)?%")) {
setWidthPercentage(ctWidth, widthValue);
} else {
ctWidth.setW(new BigInteger(widthValue));
ctWidth.setType(STTblWidth.DXA);
}
}
}
复制代码
3.2.3 设置列宽
下面我们执行设置如下:
int colWidth = table.getWidth() / 8;
List<XWPFTableCell> row0 = table.getRow(0).getTableCells();
row0.get(0).setText("xxxx");
row0.get(0).setWidth(String.valueOf(colWidth));
row0.get(1).setText("1111");
row0.get(1).setWidth(String.valueOf(colWidth * 3));
row0.get(2).setText("22222");
row0.get(2).setWidth(String.valueOf(colWidth));
row0.get(3).setText(nAppName);
row0.get(3).setWidth(String.valueOf(colWidth * 3));
复制代码
3.3 明细子表设置
进入这里最复杂的地方,也就是怎样设置子表中各列(单元格)的宽度。我曾经试过按照上述 setWidth 的方式,但当这个子表也是采用 4 列的结构时,总会直接影响上面的 3 行,导致格式出错。想要把序号这列设置的窄一些,把剩余的空间分配给右侧两列,好让文字展示得更完整,分布更合理一些。在单独设置宽度无效的背景下,就想到能否通过把子表拆成更多列,然后通过部分单元格合并 和 重新设置宽度的方式,来避免对前 3 行产生影响。
基于这种想法,我们再次开始尝试。希望把序号列宽度降为之前的 1/2,"明细"列与前 3 行宽度相同。这样计算的话,列数设置为之前的 2 倍,也就是 8 个单元格:第 1 列 2 个,第 2 列 1 个,然后再结合宽度设置来处理剩余两列的宽度。几次调整后代码如下:
int size = 8;
XWPFTable detailTable = doc.createTable(details.size(), size);
TableTools.widthTable(detailTable, MiniTableRenderData.WIDTH_A4_FULL, size);
TableTools.mergeCellsVertically(detailTable, 0, 0, details.size() - 1);
for (int i = 0; i < details.size(); i++){
TableTools.mergeCellsHorizonal(detailTable, i, 0, 1);
Detail item = details.get(i);
List<XWPFTableCell> detailTableRow = detailTable.getRow(i).getTableCells();
detailTableRow.get(0).setText("明细");
detailTableRow.get(0).setWidth(String.valueOf(colWidth));
detailTableRow.get(1).setText("斤斤计较斤斤计较斤斤计较斤斤计较急急急急急急");
detailTableRow.get(1).setWidth(String.valueOf(colWidth / 2));
TableTools.mergeCellsHorizonal(detailTable, i, 2, 5);
detailTableRow.get(2).setText("111111111111111");
detailTableRow.get(2).setWidth(String.valueOf(colWidth * 3));
detailTableRow.get(3).setText("xxxxxhhhhhhhhhhhhh哈哈哈哈哈哈哈");
detailTableRow.get(3).setWidth(String.valueOf(colWidth * 3));
}
复制代码
3.4 调整后效果
调整之后,看起来比之前好了很多。但实际上前 3 行还是受到了影响。只是样式稍好了一些。由于项目时间的原因,来不及再做优化了,就暂时保留了这种样式。
四 总结
大家可以看到,手写 word 表格格式还是很麻烦,最理想的方式还是通过模板,代码中执行内容替换的方式来生成,这样在样式的控制上会好很多。但真遇到比较麻烦的项目需求时,就只能硬上了。这时也需要进行多次尝试,来获取较好的效果。
评论