如何在Java中使用POI实现XWPFTable单元格水平合并?
Hey there! Let's tackle that horizontal cell merging issue in XWPFTable together. I know it can be frustrating when vertical merging works but horizontal doesn't—POI's handling of table merges has some gotchas, especially around how Word renders the final result. Here's a reliable, tested approach that should get you the exact outcome you're aiming for:
Core Fix: Combine VMerge with Width Adjustments
The most common reason horizontal merges fail is that many older solutions only set the vMerge property but skip adjusting cell widths and cleaning up redundant cells. Word relies on both the merge flags and proper width allocation to render merged cells correctly.
Step-by-Step Code Implementation
Here's a reusable utility method to handle horizontal merges, plus a full example:
import org.apache.poi.xwpf.usermodel.*; import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigInteger; public class XWPFHorizontalMergeFix { public static void main(String[] args) throws IOException { // Create a sample document and table XWPFDocument doc = new XWPFDocument(); XWPFTable table = doc.createTable(3, 4); // 3 rows, 4 columns // Populate initial content table.getRow(0).getCell(0).setText("Unmerged Cell"); table.getRow(0).getCell(1).setText("Merge"); table.getRow(0).getCell(2).setText("These"); table.getRow(0).getCell(3).setText("Cells"); // Merge columns 1-3 in the first row (0-indexed) mergeHorizontalCells(table, 0, 1, 3); // Save the document try (FileOutputStream out = new FileOutputStream("horizontal_merge_example.docx")) { doc.write(out); } doc.close(); } /** * Merges cells horizontally in a specific row of an XWPFTable * @param table Target table * @param rowIndex Index of the row to modify (0-indexed) * @param startCol Starting column index (0-indexed) * @param endCol Ending column index (0-indexed, must be > startCol) */ public static void mergeHorizontalCells(XWPFTable table, int rowIndex, int startCol, int endCol) { // Validate input parameters if (rowIndex < 0 || rowIndex >= table.getNumberOfRows()) return; XWPFRow targetRow = table.getRow(rowIndex); if (startCol < 0 || endCol >= targetRow.getTableCells().size() || startCol >= endCol) return; CTTblGrid tableGrid = table.getCTTbl().getTblGrid(); int totalMergedWidth = 0; // Calculate total width of columns to merge for (int col = startCol; col <= endCol; col++) { totalMergedWidth += tableGrid.getGridColArray(col).getW().intValue(); } // Configure the first cell in the merged range XWPFCell startCell = targetRow.getCell(startCol); CTCell ctStartCell = startCell.getCTCell(); CTTcPr cellProps = ctStartCell.getTcPr() != null ? ctStartCell.getTcPr() : ctStartCell.addNewTcPr(); // Mark as the start of the merge CTVMerge mergeStart = cellProps.addNewVMerge(); mergeStart.setVal(STMerge.RESTART); // Set width to total merged column width CTTcW cellWidth = cellProps.getTcW() != null ? cellProps.getTcW() : cellProps.addNewTcW(); cellWidth.setW(BigInteger.valueOf(totalMergedWidth)); // Configure remaining cells in the merged range for (int col = startCol + 1; col <= endCol; col++) { XWPFCell currentCell = targetRow.getCell(col); if (currentCell == null) continue; CTCell ctCurrentCell = currentCell.getCTCell(); CTTcPr currentProps = ctCurrentCell.getTcPr() != null ? ctCurrentCell.getTcPr() : ctCurrentCell.addNewTcPr(); // Mark as continuation of the merge CTVMerge mergeContinue = currentProps.addNewVMerge(); mergeContinue.setVal(STMerge.CONTINUE); // Set width to 0 and clear content to avoid rendering issues CTTcW zeroWidth = currentProps.getTcW() != null ? currentProps.getTcW() : currentProps.addNewTcW(); zeroWidth.setW(BigInteger.valueOf(0)); currentCell.removeParagraph(0); // Clear any existing text } } }
Key Notes to Avoid Failure
- VMerge Flags: Always set the first cell to
STMerge.RESTARTand subsequent cells toSTMerge.CONTINUE—this tells POI and Word where the merge starts and ends. - Width Allocation: The starting cell must take up the total width of all merged columns, while merged cells get a width of 0. Without this, Word will still display separate narrow cells even if the merge flags are set.
- Clear Redundant Content: Empty out the cells being merged into the start cell to prevent leftover text from breaking the visual merge.
- Validate Cell Existence: Ensure all cells in the merge range exist (no
nullcells) before attempting to merge—create missing cells if needed.
Troubleshooting Checklist
If it's still not working:
- Open the generated
.docxfile, go to File > Info > Inspect Document to check for table corruption. - Unzip the
.docxfile and look atword/document.xml—verify that the merged cells have the correct<w:vMerge w:val="restart"/>and<w:vMerge w:val="continue"/>tags, plus proper<w:tcW>width settings. - Double-check that you're using a recent version of Apache POI (5.2.0+ recommended)—older versions have bugs in table merge handling.
内容的提问来源于stack exchange,提问作者Joel George




