笔者接触Poi-tl是为了解决项目中遇到的打印需求,后面项目上遇到了遇到在列表渲染的时候,列表不存在时补充一整行水平居中的“无”,即“无”占据一行。这个需求笔者找了很多文章也没有找到很好的解决方案,官方的文档是实现动态表格重写表格渲染,继承DynamicTableRenderPolicy。
但是重写了DynamicTableRenderPolicy类就意味着需要完全重写表格的渲染逻辑,这不是笔者需要的,笔者需要的是,当列表有数据时执行原有表格的渲染逻辑,没有数据的时候走笔者渲染“无“的逻辑,而不是完全的重写。
从官方文档并没有找到很好的解决方案,笔者突然想起来Java的继承机制,如果我不继承DynamicTableRenderPolicy而是继承LoopRowTableRenderPolicy类,使用过Poi-tl的应该知道,这个类就是官方提供的表格渲染类。我继承这个类,重写渲染(render)方法,有值则使用父类渲染表格的方法,否则使用自定义渲染逻辑,说干就干,经过长久的调试,笔者写下如下代码:
import cn.hutool.core.collection.CollUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.Rows;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.policy.TableRenderPolicy;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.TableTools;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.data.repository.query.ParameterOutOfBoundsException;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
public class MyLoopRowTableRenderPolicy extends LoopRowTableRenderPolicy {
/**
* 创建单元格的数量,因为每个票都不同,创建不同的单元格合并才会合并成一整行
*/
private Integer cellSize;
public MyLoopRowTableRenderPolicy() {
super();
//默认13
this.cellSize = 13;
}
public MyLoopRowTableRenderPolicy(int cellSize) {
super();
if(cellSize <= 1){
throw new RuntimeException("参数错误,只能是大于1的整数");
}
this.cellSize = cellSize;
}
@Override
public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
//数据必须是集合类,否则不应该使用这个渲染器
if (data instanceof Collection && CollUtil.isEmpty((Collection) data)) {
//为空时默认补充一行无
renderEmpty(eleTemplate);
} else {
super.render(eleTemplate, data, template);
}
}
/**
* 渲染空数据,在表格钟默认增加一行无
*
* @param eleTemplate
*/
private void renderEmpty(ElementTemplate eleTemplate) {
RunTemplate runTemplate = (RunTemplate) eleTemplate;
XWPFRun run = runTemplate.getRun();
//消除标签值
run.setText("", 0);
XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
//获取当前表格
XWPFTable table = tagCell.getTableRow().getTable();
//获取模板所在行,即表格标签下一行
int rowIndex = getTemplateRowIndex(tagCell) + 1;
//删除表格标签下一行
table.removeRow(rowIndex);
//插入一行
XWPFTableRow row = table.insertNewTableRow(rowIndex);
Stream.iterate(0, i -> i + 1)
.limit(cellSize).forEach(i -> row.createCell());
TableTools.mergeCellsHorizonal(table, rowIndex, 0, cellSize - 1);
try {
TableRenderPolicy.Helper.renderRow(table.getRow(rowIndex),
Rows.of("无").center().textFontFamily("宋体").textFontSize(11).create());
} catch (Exception e) {
e.printStackTrace();
}
}
private int getTemplateRowIndex(XWPFTableCell tagCell) {
XWPFTableRow tagRow = tagCell.getTableRow();
return this.getRowIndex(tagRow);
}
private int getRowIndex(XWPFTableRow row) {
List<XWPFTableRow> rows = row.getTable().getRows();
return rows.indexOf(row);
}
}
可以看到,这样实现很简单,只需要实现当表格数据不存在时的逻辑即可,事实证明,也是可以真正实现的。其中cellSize的作用非常重要,实例化对象的时候需要指定当前渲染”无“的列数,这个需要自己调试,因为如果使用
TableRenderPolicy.Helper.renderRow(table.getRow(rowIndex),
Rows.of("无").center().textFontFamily("宋体").textFontSize(11).create());
以上代码是没办法实现”无“占据一整行的,需要自己指定插入的列数,当列数足够时,就会充满一行,再合并整行实现整行变成一个单元格,当然,如果您有更好的实现,也可以跟我说一说,这已经是我所能想到最灵活的方式,但不是各位大佬的极限。