好的!下面给你一份基于 poi-tl(Java POI模板引擎)的详细教程,重点讲解如何实现动态表格填充与单元格合并功能,方便你快速用poi-tl处理Word模板生成复杂文档。
poi-tl 详细教程 — 动态表格 & 单元格合并
1. 简介
- poi-tl 是基于 Apache POI 的模板引擎,支持Word模板(.docx)中占位符渲染。
- 支持动态表格渲染、图片、单元格合并、多级嵌套等高级功能。
2. 引入依赖(Maven)
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.13.1</version> <!-- 最新版本请到官网确认 -->
</dependency>
3. 基础示例:动态表格渲染
3.1 准备Word模板
- 在Word中创建表格,表格内填写占位符,格式例如:
序号 | 姓名 | 年龄 |
---|---|---|
{{#employees}} | {{index}} | {{name}} |
{{#employees}}...{{/employees}}
是循环开始和结束标记。{{index}}
是循环中自动生成的索引(从1开始)。
3.2 Java代码实现动态填充表格
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.*;
import java.util.*;
public class DynamicTableExample {
public static void main(String[] args) throws Exception {
// 模板路径
String template = "template.docx";
// 输出路径
String out = "out.docx";
// 构建数据列表
List<Map<String, Object>> employees = new ArrayList<>();
employees.add(new HashMap<String, Object>() {{
put("index", 1);
put("name", "张三");
put("age", 28);
}});
employees.add(new HashMap<String, Object>() {{
put("index", 2);
put("name", "李四");
put("age", 30);
}});
// 或使用 RenderData 和 MiniTableRenderData 更高级表格渲染(后面介绍)
// 构建渲染数据
Map<String, Object> data = new HashMap<>();
data.put("employees", employees);
// 渲染模板
XWPFTemplate templateDoc = XWPFTemplate.compile(template).render(data);
templateDoc.writeToFile(out);
templateDoc.close();
}
}
4. 动态表格高级渲染(使用 MiniTableRenderData)
4.1 准备Word模板
- 在Word模板中写占位符:
{{table}}
4.2 Java代码示例
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.*;
import com.deepoove.poi.policy.MiniTableRenderPolicy;
import java.util.*;
public class MiniTableExample {
public static void main(String[] args) throws Exception {
String template = "template.docx";
String out = "out_mini_table.docx";
// 表头
RowRenderData header = RowRenderData.build("序号", "姓名", "年龄");
// 构建数据行
List<RowRenderData> rows = new ArrayList<>();
rows.add(RowRenderData.build("1", "张三", "28"));
rows.add(RowRenderData.build("2", "李四", "30"));
// 构建表格数据
MiniTableRenderData tableData = new MiniTableRenderData(header, rows);
// 渲染数据
Map<String, Object> data = new HashMap<>();
data.put("table", tableData);
XWPFTemplate templateDoc = XWPFTemplate.compile(template).render(data);
templateDoc.writeToFile(out);
templateDoc.close();
}
}
5. 单元格合并
5.1 Word中模板准备
- 合并的单元格在模板中先合并好,但如果需要动态控制,需通过代码处理。
5.2 使用 poi-tl 动态合并单元格
poi-tl 本身没有直接的合并API,但你可以在表格渲染完成后,使用 Apache POI 的API对目标表格单元格进行合并:
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.FileOutputStream;
import java.util.List;
public class MergeCellExample {
public static void main(String[] args) throws Exception {
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(null);
XWPFDocument doc = template.getXWPFDocument();
// 获取第一个表格
XWPFTable table = doc.getTables().get(0);
// 合并第一行第1列到第2列(水平合并)
mergeCellsHorizontal(table, 0, 0, 1);
// 合并第1列第2行到第3行(垂直合并)
mergeCellsVertically(table, 0, 1, 2);
// 输出文档
FileOutputStream out = new FileOutputStream("merged_output.docx");
doc.write(out);
out.close();
template.close();
}
// 水平合并单元格
public static void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
if (cellIndex == fromCell) {
cell.getCTTc().addNewTcPr().addNewHMerge().setVal("restart");
} else {
cell.getCTTc().addNewTcPr().addNewHMerge().setVal("continue");
}
}
}
// 垂直合并单元格
public static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
if (rowIndex == fromRow) {
cell.getCTTc().addNewTcPr().addNewVMerge().setVal("restart");
} else {
cell.getCTTc().addNewTcPr().addNewVMerge().setVal("continue");
}
}
}
}
6. 总结
功能 | 说明 |
---|---|
动态表格渲染 | 使用循环占位符 或 MiniTableRenderData 实现 |
单元格合并 | 先渲染模板,再用Apache POI API对表格合并 |
推荐做法 | 模板设计时尽量预留合并单元格位置,代码灵活合并 |
明白!我帮你系统整理了一份 poi-tl全方位教程,包含:
- 动态表格渲染(基础循环+MiniTableRenderData)
- 单元格合并(水平合并+垂直合并)
- 复杂多级表格嵌套渲染示例
- poi-tl图片、条码、二维码渲染
- 性能优化与模板设计技巧
1. 动态表格渲染
1.1 基础循环渲染
模板示例:
| 序号 | 姓名 | 年龄 |
|------|------|------|
| {{#employees}} | {{index}} | {{name}} | {{age}} | {{/employees}} |
Java示例:
List<Map<String, Object>> employees = new ArrayList<>();
employees.add(new HashMap<String, Object>() {{ put("index", 1); put("name", "张三"); put("age", 28); }});
employees.add(new HashMap<String, Object>() {{ put("index", 2); put("name", "李四"); put("age", 30); }});
Map<String, Object> data = new HashMap<>();
data.put("employees", employees);
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(data);
template.writeToFile("output.docx");
template.close();
1.2 使用 MiniTableRenderData 渲染更复杂表格
RowRenderData header = RowRenderData.build("序号", "姓名", "年龄");
List<RowRenderData> rows = Arrays.asList(
RowRenderData.build("1", "张三", "28"),
RowRenderData.build("2", "李四", "30")
);
MiniTableRenderData table = new MiniTableRenderData(header, rows);
Map<String, Object> data = new HashMap<>();
data.put("table", table);
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(data);
template.writeToFile("output_mini_table.docx");
template.close();
2. 单元格合并
// 水平合并第0行第0到1单元格
mergeCellsHorizontal(table, 0, 0, 1);
// 垂直合并第0列第1到2行单元格
mergeCellsVertically(table, 0, 1, 2);
public static void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
for (int i = fromCell; i <= toCell; i++) {
XWPFTableCell cell = table.getRow(row).getCell(i);
if (i == fromCell) cell.getCTTc().addNewTcPr().addNewHMerge().setVal("restart");
else cell.getCTTc().addNewTcPr().addNewHMerge().setVal("continue");
}
}
public static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
for (int i = fromRow; i <= toRow; i++) {
XWPFTableCell cell = table.getRow(i).getCell(col);
if (i == fromRow) cell.getCTTc().addNewTcPr().addNewVMerge().setVal("restart");
else cell.getCTTc().addNewTcPr().addNewVMerge().setVal("continue");
}
}
3. 复杂多级表格嵌套渲染示例
- 可以用 Map 嵌套 List 的方式,结合
{{#xxx}}...{{/xxx}}
多层循环
模板示例:
| 序号 | 姓名 | 项目列表 |
|------|------|----------|
| {{#employees}} | {{index}} | {{name}} |
{{#projects}}
- {{projectName}} ({{duration}}月)
{{/projects}}
{{/employees}} |
Java示例:
List<Map<String, Object>> employees = new ArrayList<>();
Map<String, Object> emp1 = new HashMap<>();
emp1.put("index", 1);
emp1.put("name", "张三");
emp1.put("projects", Arrays.asList(
Map.of("projectName", "项目A", "duration", 6),
Map.of("projectName", "项目B", "duration", 3)
));
Map<String, Object> emp2 = new HashMap<>();
emp2.put("index", 2);
emp2.put("name", "李四");
emp2.put("projects", Arrays.asList(
Map.of("projectName", "项目C", "duration", 12)
));
employees.add(emp1);
employees.add(emp2);
Map<String, Object> data = new HashMap<>();
data.put("employees", employees);
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(data);
template.writeToFile("output_nested.docx");
template.close();
4. 图片、条码、二维码渲染
4.1 图片
PictureRenderData picture = new PictureRenderData(60, 60, "path/to/image.jpg");
Map<String, Object> data = Map.of("picture", picture);
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(data);
template.writeToFile("output_picture.docx");
template.close();
模板中使用 {{picture}}
占位符。
4.2 条码与二维码
poi-tl支持条码,示例:
BarCodeRenderData barcode = new BarCodeRenderData(BarCodeRenderData.BarCodeType.CODE128, "1234567890");
QrCodeRenderData qrCode = new QrCodeRenderData("https://example.com", 100, 100);
Map<String, Object> data = new HashMap<>();
data.put("barcode", barcode);
data.put("qrcode", qrCode);
XWPFTemplate template = XWPFTemplate.compile("template.docx").render(data);
template.writeToFile("output_code.docx");
template.close();
5. 性能优化与模板设计技巧
- 模板设计:
- 尽量减少大表格单元格嵌套,简化循环层次。
- 合理使用 MiniTableRenderData 分离复杂表格。
- 数据准备:
- 预处理数据成适合模板结构的格式,避免运行时复杂转换。
- 缓存模板:
- 如果多次生成相同模板,可缓存XWPFTemplate对象。
- 避免重复IO:
- 批量生成时,减少频繁读写文件。
- 占位符规范:
- 遵守poi-tl占位符规则,不使用保留字符。
发表回复