EasyExcel export custom merge cell strategy personal understanding

catalogue

EasyExcel version

POI merge cell -- CellRangeAddress

Custom policy

Custom cell style

CellStyle:

Custom cell merge Handler

1. Merge according to the specified column and merge cells with the same content upward

2. Merge according to the specified column and merge cells with the same content upward

​3. Automatically merge cells. Rows are merged primarily and columns are merged secondarily (merge if the contents are the same)

In short

EasyExcel is an open source project of simple, memory saving read-write Excel based on Java. Support reading and writing 100M excel while saving memory as much as possible. Excel export has also been done in previous development, but it is exported through tool class calls that have been encapsulated by predecessors of the company (Apache poi used by the company). Add # EasyExcel to your project for practice in case of need.

EasyExcel version

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.0.5</version>
        </dependency>

POI merge cell -- CellRangeAddress

CellRangeAddress has four parameters: start line number, end line number, start column number and end column number

CellRangeAddress(2, 3, 4, 5);  Represents the combined regional range E3, F4
 Starting from line 2, ending at line 3, starting at column 4 and ending at column 5. 

be careful:

1. The starting line number cannot be greater than the ending line number (error: new CellRangeAddress(1, 0, 0, 0)).

2. The starting column number cannot be greater than the ending column number (error: new CellRangeAddress(0, 0, 1, 0)).

3. The row and column of EXECL start from 0, not 1.

The cell merge display style of a sheet page is controlled by the CellRangeAddress collection in a sheet page.

EasyExcel user defined merging cells is to operate the CellRangeAddress collection in each sheet page!

Custom policy

The following is the strategy of merging cells (the code is for reference and understanding only, and the specific strategy needs to be customized according to its own actual business!)

Custom cell style

CellStyle:

import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;


public class CellStyle {

    public static  HorizontalCellStyleStrategy getCellCenterStyle(){
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //Set background color
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        //Set header font
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short)13);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        //Set header Center
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //Content strategy
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //Set horizontal center
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //Set vertical center
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        return horizontalCellStyleStrategy;
    }
}

Custom cell merge Handler

1. Merge according to the specified column and merge cells with the same content upward

ExcelFillCellLineMergeHandler:
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @author Inori
 * @version 1.0.0-SNAPSHOT
 * @Description : Custom merge cell policy Handler column merge merges the same cells up
 * @create 2022-01-27 9:52
 */
public class ExcelFillCellLineMergeHandler implements CellWriteHandler {
    private int[] mergeColumnIndex; //Column example of user-defined merged cells: int[] mergeColumeIndex = {0, 1, 11};
    private int mergeRowIndex; //Generally speaking, the row height of the filling header of a user-defined merged cell is 3. For example, if the high order of the row is 3, int mergeRowIndex = 3;

    public ExcelFillCellLineMergeHandler() {
    }

    public ExcelFillCellLineMergeHandler(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }
    //Operation before cell creation
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }
    //Operation after cell creation
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }
    //The afterCellDispose method is an opportunity after the cell is created and before it is destroyed. At this time, we can change the cell content.
    //mergeColumnIndex: for example, int [] mergeColumnIndex = {0, 1, 11};
    //mergeRowIndex defines the rows of merged cells. / / generally, the height of the filling header row is higher. For example, if the high order of the header row is 3, int mergeRowIndex = 3;

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
        int curRowIndex = cell.getRowIndex(); //Number of rows in the current cell
        int curColIndex = cell.getColumnIndex(); // Number of columns in the current cell
        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < mergeColumnIndex.length; i++) {
                if (curColIndex == mergeColumnIndex[i]) {
                    //Cell data processing
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }
    /**
     * @description Merge current cell up
     * cell             Current cell
     * curRowIndex      Current row
     * curColIndex      Current column
     * @author Inori
     * @date 2022/1/26 15:50
     * @params [writeSheetHolder, cell, curRowIndex, curColIndex]
     * @return void
     * @throws
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //Data in current cell
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        //Gets the previous cell of the current cell
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        //Gets the data in the previous cell of the current cell
        Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
        // Compares the current cell data with the previous cell data
        if (preData.equals(curData)) {
            //Get the current sheet page
            Sheet sheet = writeSheetHolder.getSheet();
            //Get all merged cells
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            //Merge
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                //CellRangeAddress POI merge cells
                //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                //Example: CellRangeAddress (2, 6000, 3, 3);
                //From line 2, line 6000 ends, column 3 starts and column 3 ends.
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // cellRangeAddr.isInRange(int rowInd, int colInd) determines whether a given coordinate is within the range of this range.
                // If the previous cell has been merged, move out the original merging cell first, and then add the merging cell again
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // If the previous cell has not been merged, a new merged cell will be added
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

Code call (the data is just fun, and I really can't make it up!)
//The same header of Excel will be merged automatically. The content data of Excel table will be merged in the specified column, starting from the specified row! Merge with the same column content!
    @PostMapping("/tableDataLineMerge")
    public void tableDataLineMerge(HttpServletResponse response) throws IOException {
        //Excel header data
        List<List<String>> headers=new ArrayList<>();
        //Basic information
        //Serial number
        List<String> baseSerialHead=new ArrayList<>();
        baseSerialHead.add("Basic information");
        baseSerialHead.add("Basic information");
        baseSerialHead.add("Serial number");
        //business
        List<String> baseShopHead=new ArrayList<>();
        baseShopHead.add("Basic information");
        baseShopHead.add("Basic information");
        baseShopHead.add("business");
        //particular year
        List<String> baseYearHead=new ArrayList<>();
        baseYearHead.add("Basic information");
        baseYearHead.add("Basic information");
        baseYearHead.add("particular year");

        //Product list (drinks)
        //mineral water
        //Wahaha
        List<String> DrinkWahahaHead=new ArrayList<>();
        DrinkWahahaHead.add("drink");
        DrinkWahahaHead.add("mineral water");
        DrinkWahahaHead.add("Wahaha");
        //NongFu Spring
        List<String> DrinkNongfuHead=new ArrayList<>();
        DrinkNongfuHead.add("drink");
        DrinkNongfuHead.add("mineral water");
        DrinkNongfuHead.add("NongFu Spring");

        //Drinks
        //arctic ocean
        List<String> DrinkArcticHead=new ArrayList<>();
        DrinkArcticHead.add("drink");
        DrinkArcticHead.add("Drinks");
        DrinkArcticHead.add("arctic ocean");

        //Hongbaolai
        List<String> DrinkHBLHead=new ArrayList<>();
        DrinkHBLHead.add("drink");
        DrinkHBLHead.add("Drinks");
        DrinkHBLHead.add("Hongbaolai");

        //Excel header data filling
        headers.add(baseSerialHead);
        headers.add(baseShopHead);
        headers.add(baseYearHead);
        headers.add(DrinkWahahaHead);
        headers.add(DrinkNongfuHead);
        headers.add(DrinkArcticHead);
        headers.add(DrinkHBLHead);

        //Excel table content data
        List<List<String>> datas=new ArrayList<>();
        //First row of data
        List<String> dataFirst=new ArrayList<>();
        dataFirst.add("1");
        dataFirst.add("A");
        dataFirst.add("2010");
        dataFirst.add("1.0");
        dataFirst.add("1.5");
        dataFirst.add("5.0");
        dataFirst.add("1.5");
        //Second row data
        List<String> dataSecond=new ArrayList<>();
        dataSecond.add("2");
        dataSecond.add("A");
        dataSecond.add("2020");
        dataSecond.add("2.0");
        dataSecond.add("2.5");
        dataSecond.add("8.0");
        dataSecond.add("2.0");
        //Third row data
        List<String> dataThird=new ArrayList<>();
        dataThird.add("3");
        dataThird.add("B");
        dataThird.add("2010");
        dataThird.add("0.9");
        dataThird.add("1.4");
        dataThird.add("4.5");
        dataThird.add("2.0");
        //Fourth row data
        List<String> dataFourth=new ArrayList<>();
        dataFourth.add("4");
        dataFourth.add("B");
        dataFourth.add("2020");
        dataFourth.add("2.0");
        dataFourth.add("2.5");
        dataFourth.add("8.0");
        dataFourth.add("2.0");
        //Excel table content data filling
        datas.add(dataFirst);
        datas.add(dataSecond);
        datas.add(dataThird);
        datas.add(dataFourth);
        //excel generation

        // Note that some students have reported that using swagger will lead to various problems. Please use the browser or postman directly
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // Here is urlencoder Encode can prevent Chinese garbled code. Of course, it has nothing to do with easyexcel
        String fileName = URLEncoder.encode("Specify column merge", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        //Customize the columns of merged cells (specify the merged columns according to their own data): for example, we want to merge businesses
        int[] mergeColumeIndex = {1};
        //Customize rows of merged cells
        int mergeRowIndex = 3;//It is generally set to Excel header data high
        //All configurations are inherited, and the configuration of Workbook will be inherited by sheet, so when setting parameters with EasyExcel, in EasyExcel Before the sheet () method, the scope is the whole sheet, and then for a single sheet
        EasyExcel.write(response.getOutputStream()).registerWriteHandler(new ExcelFillCellLineMergeHandler(mergeRowIndex, mergeColumeIndex)).head(headers).sheet("sheet1").doWrite(datas);

    }

Call presentation:

Before consolidation:

After merger

2. Merge according to the specified column and merge cells with the same content upward

ExcelFillCellRowMergeHandler:
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.wty.model.CellLineRange;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @author Inori
 * @version 1.0.0-SNAPSHOT
 * @Description : Custom merge cell policy Handler row merge merges the same cell to the left
 * @create 2022-01-27 9:52
 */
public class ExcelFillCellRowMergeHandler implements CellWriteHandler {
    private List<CellLineRange> cellLineRangeList; //If you want to merge the 4th and 5th, 6th and 7th columns of custom merged cells: [CellLineRange(firstCol=3, lastCol=4), CellLineRange(firstCol=5, lastCol=6)]

    private int mergeRowIndex; //The row at the beginning of the user-defined merged cell. Generally speaking, the filling header row height of - 1 means that the merging starts from each column under the header: if the high order of the header row is 3, int mergeRowIndex = 2;


    public ExcelFillCellRowMergeHandler() {
    }

    public ExcelFillCellRowMergeHandler(List<CellLineRange> cellLineRangeList, int mergeRowIndex) {
        this.cellLineRangeList=cellLineRangeList;
        this.mergeRowIndex=mergeRowIndex;
    }
    //Operation before cell creation
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }
    //Operation after cell creation
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }
    //The afterCellDispose method is an opportunity after the cell is created and before it is destroyed. At this time, we can change the cell content.
    //cellLineRangeList customize the columns of merged cells. If you want to merge columns 4 and 5, 6 and 7: [CellLineRange(firstCol=3, lastCol=4), CellLineRange(firstCol=5, lastCol=6)]
    //mergeRowIndex the rows of custom merged cells. Generally speaking, the height of the filling header row - 1 means merging from each column under the header: if the high order of the header row is 3, int mergeRowIndex = 2;
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
        int curRowIndex = cell.getRowIndex(); //Number of rows in the current cell
        int curColIndex = cell.getColumnIndex(); // Number of columns in the current cell
        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < cellLineRangeList.size(); i++) {
                if (curColIndex > cellLineRangeList.get(i).getFirstCol()&&curColIndex<=cellLineRangeList.get(i).getLastCol()) {
                    //Cell data processing
                    mergeWithLeftLine(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }
    /**
     * @description Merge the current cell to the left
     * @author Inori
     * @date 2022/1/27 15:39
     * @params [writeSheetHolder, cell, curRowIndex, curColIndex]
     * @return void
     * @throws
     */
    private void mergeWithLeftLine(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //Data in current cell
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        //Gets the left cell of the current cell
        Cell leftCell = cell.getSheet().getRow(curRowIndex).getCell(curColIndex - 1);
        //Gets the data in the left cell of the current cell
        Object leftData = leftCell.getCellType() == CellType.STRING ? leftCell.getStringCellValue() : leftCell.getNumericCellValue();

        // Compare the current cell data with the one on the left
        if (leftData.equals(curData)) {
            //Get the current sheet page
            Sheet sheet = writeSheetHolder.getSheet();
            //Get all merged cells
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            //Merge
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                //CellRangeAddress POI merge cells
                //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                //Example: CellRangeAddress (2, 6000, 3, 3);
                //From line 2, line 6000 ends, column 3 starts and column 3 ends.
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // cellRangeAddr.isInRange(int rowInd, int colInd) determines whether a given coordinate is within the range of this range.
                // If a cell on the left has been merged, move out the original merging cell first, and then add the merging cell again
                if (cellRangeAddr.isInRange(curRowIndex, curColIndex - 1)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastColumn(curColIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // If a cell on the left is not merged, a new merged cell will be added
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex , curRowIndex, curColIndex- 1, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }

    }

}

CellLineRange:

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @author Inori
 * @version 1.0.0-SNAPSHOT
 * @Description :
 * @create 2022-01-27 14:07
 */
@Data
@AllArgsConstructor
public class CellLineRange {
    private int firstCol; //Start column
    private int lastCol;  //End column
}

Code call (the data is just fun, and I really can't make it up!)

//The same Excel header will be merged automatically. The Excel table content data will be merged within the specified column range, starting after the specified row! Merge lines with the same content!
    @PostMapping("/tableDataRowMerge")
    public void tableDataRowMerge(HttpServletResponse response) throws IOException {
        //Excel header data
        List<List<String>> headers=new ArrayList<>();
        //Basic information
        //Serial number
        List<String> baseSerialHead=new ArrayList<>();
        baseSerialHead.add("Basic information");
        baseSerialHead.add("Basic information");
        baseSerialHead.add("Serial number");
        //business
        List<String> baseShopHead=new ArrayList<>();
        baseShopHead.add("Basic information");
        baseShopHead.add("Basic information");
        baseShopHead.add("business");
        //particular year
        List<String> baseYearHead=new ArrayList<>();
        baseYearHead.add("Basic information");
        baseYearHead.add("Basic information");
        baseYearHead.add("particular year");

        //Product list (drinks)
        //mineral water
        //Wahaha
        List<String> DrinkWahahaHead=new ArrayList<>();
        DrinkWahahaHead.add("drink");
        DrinkWahahaHead.add("mineral water");
        DrinkWahahaHead.add("Wahaha");
        //NongFu Spring
        List<String> DrinkNongfuHead=new ArrayList<>();
        DrinkNongfuHead.add("drink");
        DrinkNongfuHead.add("mineral water");
        DrinkNongfuHead.add("NongFu Spring");

        //Drinks
        //arctic ocean
        List<String> DrinkArcticHead=new ArrayList<>();
        DrinkArcticHead.add("drink");
        DrinkArcticHead.add("Drinks");
        DrinkArcticHead.add("arctic ocean");

        //Hongbaolai
        List<String> DrinkHBLHead=new ArrayList<>();
        DrinkHBLHead.add("drink");
        DrinkHBLHead.add("Drinks");
        DrinkHBLHead.add("Hongbaolai");

        //Excel header data filling
        headers.add(baseSerialHead);
        headers.add(baseShopHead);
        headers.add(baseYearHead);
        headers.add(DrinkWahahaHead);
        headers.add(DrinkNongfuHead);
        headers.add(DrinkArcticHead);
        headers.add(DrinkHBLHead);

        //Excel table content data
        List<List<String>> datas=new ArrayList<>();
        //First row of data
        List<String> dataFirst=new ArrayList<>();
        dataFirst.add("1");
        dataFirst.add("A");
        dataFirst.add("2010");
        dataFirst.add("1.5");
        dataFirst.add("1.5");
        dataFirst.add("5.0");
        dataFirst.add("1.5");
        //Second row data
        List<String> dataSecond=new ArrayList<>();
        dataSecond.add("2");
        dataSecond.add("A");
        dataSecond.add("2020");
        dataSecond.add("2.0");
        dataSecond.add("2.0");
        dataSecond.add("2.0");
        dataSecond.add("2.0");
        //Third row data
        List<String> dataThird=new ArrayList<>();
        dataThird.add("3");
        dataThird.add("B");
        dataThird.add("2010");
        dataThird.add("0.9");
        dataThird.add("1.4");
        dataThird.add("4.5");
        dataThird.add("2.0");
        //Fourth row data
        List<String> dataFourth=new ArrayList<>();
        dataFourth.add("4");
        dataFourth.add("B");
        dataFourth.add("2020");
        dataFourth.add("2.0");
        dataFourth.add("2.5");
        dataFourth.add("8.0");
        dataFourth.add("8.0");
        //Excel table content data filling
        datas.add(dataFirst);
        datas.add(dataSecond);
        datas.add(dataThird);
        datas.add(dataFourth);
        //excel generation

        // Note that some students have reported that using swagger will lead to various problems. Please use the browser or postman directly
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // Here is urlencoder Encode can prevent Chinese garbled code. Of course, it has nothing to do with easyexcel
        String fileName = URLEncoder.encode("Specify column merge", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        //Customize the columns of merged cells
        ArrayList<CellLineRange> cellLineRanges=new ArrayList<>();
        cellLineRanges.add(new CellLineRange(3,4));
        cellLineRanges.add(new CellLineRange(5,6));
        //Customize the row that starts merging cells
        int mergeRowIndex = 2;//Generally, if it is set to the high-order 3 of Excel header data, it will be 2, for example: 0,1,2
        //All configurations are inherited, and the configuration of Workbook will be inherited by sheet, so when setting parameters with EasyExcel, in EasyExcel Before the sheet () method, the scope is the whole sheet, and then for a single sheet
        EasyExcel.write(response.getOutputStream()).registerWriteHandler(getCellCenterStyle()).registerWriteHandler(new ExcelFillCellRowMergeHandler(cellLineRanges,mergeRowIndex)).head(headers).sheet("sheet1").doWrite(datas);

    }

Call presentation:

Before consolidation:

After consolidation:

3. Automatically merge cells. Rows are merged primarily and columns are merged secondarily (merge if the contents are the same)

(I just merge casually without controlling the range of cell merging! If you want to use it later, you can define the effective range of cell!)

ExcelFillCellMergeHandler:

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @author Inori
 * @version 1.0.0-SNAPSHOT
 * @Description : Automatically merge cells. Rows are merged primarily and columns are merged secondarily
 * @create 2022-01-27 15:28
 */
public class ExcelFillCellMergeHandler implements CellWriteHandler {
    private int mergeColumnIndex; // Customize the columns of merged cells, such as starting from the first column, int mergeColumnIndex=0;
    private int mergeRowIndex; //The default filling height of the row of user-defined merged cells. For example, if the high order of the row is 3, int mergeRowIndex = 2;

    public  ExcelFillCellMergeHandler(){

    }
    public ExcelFillCellMergeHandler(int mergeColumnIndex,int mergeRowIndex){

        this.mergeColumnIndex=mergeColumnIndex;
        this.mergeRowIndex=mergeRowIndex;

    }
    //Operation before cell creation
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    //Operation after cell creation
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    //The afterCellDispose method is an opportunity after the cell is created and before it is destroyed. At this time, we can change the cell content.
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
        int curRowIndex = cell.getRowIndex(); //Number of rows in the current cell
        int curColIndex = cell.getColumnIndex(); // Number of columns in the current cell
        if (curRowIndex>mergeRowIndex&&curColIndex>mergeColumnIndex){
            mergeCell(writeSheetHolder, cell, curRowIndex, curColIndex);
        }

    }

    /**
     * @return void
     * @throws
     * @description Merge current cell up
     * cell             Current cell
     * curRowIndex      Current row
     * curColIndex      Current column
     * @author Inori
     * @date 2022/1/26 15:50
     * @params [writeSheetHolder, cell, curRowIndex, curColIndex]
     */
    private void mergeCell(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //Data in current cell
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        //Gets the previous cell of the current cell
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        //Gets the data in the previous cell of the current cell
        Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();

        //Gets the left cell of the current cell
        Cell leftCell = cell.getSheet().getRow(curRowIndex).getCell(curColIndex - 1);
        //Gets the data in the left cell of the current cell
        Object leftData = leftCell.getCellType() == CellType.STRING ? leftCell.getStringCellValue() : leftCell.getNumericCellValue();
        //Is the data of a cell on the left of the current cell the same as the current cell
        boolean left = leftData.equals(curData);
        //Is the data of the previous cell of the current cell the same as that of the current cell
        boolean pre = preData.equals(curData);

        if (left) {
            //Get the current sheet page
            Sheet sheet = writeSheetHolder.getSheet();
            //Get all merged cells
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            //Judge whether the left cell participates in the consolidation. true participates in the consolidation, false does not participate
            boolean leftIsMerged = false;
            //Merge cells on the left
            CellRangeAddress cellRangeLeftAddr = null;

            for (int i = 0; i < mergeRegions.size() && !leftIsMerged; i++) {
                //CellRangeAddress Description: POI merge cells
                //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                //Example: CellRangeAddress (2, 6000, 3, 3);
                //From line 2, line 6000 ends, column 3 starts and column 3 ends.

                //Merge cells on the left
                cellRangeLeftAddr = mergeRegions.get(i);
                // cellRangeAddr.isInRange(int rowInd, int colInd) determines whether a given coordinate is within the range of this range.
                // If the left cell has been merged, move out the original merged cell first, and then conduct data processing
                if (cellRangeLeftAddr.isInRange(curRowIndex, curColIndex - 1)) {
                    sheet.removeMergedRegion(i);
//                    cellRangeLeftAddr.setLastColumn(curColIndex);
                    leftIsMerged = true;
                }
            }
            //Whether the cell on the left participates in the consolidation. true participates in the consolidation. false does not participate
            if (leftIsMerged) {

                if (pre) {
                    //Can I merge with the above cells? true yes false no
                    boolean preIsMerged = false;
                    for (int i = 0; i < mergeRegions.size() && !preIsMerged; i++) {
                        //CellRangeAddress POI merge cells
                        //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                        //Example: CellRangeAddress (2, 6000, 3, 3);
                        //From line 2, line 6000 ends, column 3 starts and column 3 ends.
                        //Merge cells above
                        CellRangeAddress cellRangePreAddr = mergeRegions.get(i);
                        // cellRangeAddr.isInRange(int rowInd, int colInd) determines whether a given coordinate is within the range of this range.

                        // If the previous cell has been merged
                        if (cellRangePreAddr.isInRange(curRowIndex - 1, curColIndex)) {
                            //Judge whether to merge with the above merged cells
                            //Judge whether the starting column of the merged area is the same as the starting column of the merged cell on the left, and whether the last column is the same as the current cell column!
                            if (cellRangePreAddr.getFirstColumn() == cellRangeLeftAddr.getFirstColumn()
                                    &&
                                    cellRangePreAddr.getLastColumn() == curColIndex
                            ) {
                                //Merge the current cell with the merged cells above
                                sheet.removeMergedRegion(i);
                                cellRangePreAddr.setLastRow(curRowIndex);
                                sheet.addMergedRegion(cellRangePreAddr);
                                preIsMerged = true;
                            }
                            //Jump out of current loop
                            break;
                        }
                    }
                    if (!preIsMerged) {
                        //Merge the current cell row
                        cellRangeLeftAddr.setLastColumn(curColIndex);
                        sheet.addMergedRegion(cellRangeLeftAddr);
                    }

                } else {
                    //If the starting row of the left merged cell is equal to the current row!
                    if (cellRangeLeftAddr.getFirstRow()==curRowIndex){
                        //Merge the current cell row
                        cellRangeLeftAddr.setLastColumn(curColIndex);
                        sheet.addMergedRegion(cellRangeLeftAddr);
                    }else {
                        //Subtract one row from the left merged cell
                        cellRangeLeftAddr.setLastRow(curRowIndex-1);
                        //Generate the current cell row merge from New
                        CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex , curRowIndex, cellRangeLeftAddr.getFirstColumn(), curColIndex);

                        sheet.addMergedRegion(cellRangeLeftAddr);
                        sheet.addMergedRegion(cellRangeAddress);
                    }

                }

            } else {
                //Generate new row and merge cells!
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, curColIndex - 1, curColIndex);
                if (pre) {
                    //Can I merge with the above cells? true yes false no
                    boolean preIsMerged = false;
                    for (int i = 0; i < mergeRegions.size() && !preIsMerged; i++) {
                        //CellRangeAddress POI merge cells
                        //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                        //Example: CellRangeAddress (2, 6000, 3, 3);
                        //From line 2, line 6000 ends, column 3 starts and column 3 ends.
                        //Merge cells above
                        CellRangeAddress cellRangePreAddr = mergeRegions.get(i);
                        // cellRangeAddr.isInRange(int rowInd, int colInd) determines whether a given coordinate is within the range of this range.
                        // If the previous cell has been merged
                        if (cellRangePreAddr.isInRange(curRowIndex - 1, curColIndex)) {
                            //Judge whether to merge with the above merged cells
                            //Judge whether the starting column of the merged range is the same as the starting column of the new merged cell, and whether the last column is the same as the current cell column!
                            if (cellRangePreAddr.getFirstColumn() == cellRangeAddress.getFirstColumn()
                                    &&
                                    cellRangePreAddr.getLastColumn() == cellRangeAddress.getLastColumn()
                            ) {
                                sheet.removeMergedRegion(i);
                                cellRangePreAddr.setLastRow(curRowIndex);
                                sheet.addMergedRegion(cellRangePreAddr);
                                preIsMerged = true;
                            }
                            //Jump out of current loop
                            break;
                        }
                    }
                    if (!preIsMerged) {
                        //Add merged cell
                        sheet.addMergedRegion(cellRangeAddress);
                    }

                }else {
                    //Add merged cell
                    sheet.addMergedRegion(cellRangeAddress);
                }

            }

        } else {
            if (pre) {
                //Get the current sheet page
                Sheet sheet = writeSheetHolder.getSheet();
                //Get all merged cells
                List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
                //Judge whether to merge with the above merged cells. false
                boolean preIsMerged = false;
                for (int i = 0; i < mergeRegions.size() && !preIsMerged; i++) {
                    //CellRangeAddress POI merge cells
                    //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                    //Example: CellRangeAddress (2, 6000, 3, 3);
                    //From line 2, line 6000 ends, column 3 starts and column 3 ends.
                    CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                    // cellRangeAddr.isInRange(int rowInd, int colInd) determines whether a given coordinate is within the range of this range.
                    // If the previous cell has been merged, move out the original merging cell first, and then add the merging cell again
                    if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                        //Whether to merge the current column. If yes, merge
                        if (cellRangeAddr.getFirstColumn()==curColIndex&&cellRangeAddr.getLastColumn()==curColIndex){
                            sheet.removeMergedRegion(i);
                            cellRangeAddr.setLastRow(curRowIndex);
                            sheet.addMergedRegion(cellRangeAddr);
                        }
                        preIsMerged = true;
                        break;
                    }
                }
                // If the previous cell has not been merged, a new merged cell will be added
                if (!preIsMerged) {
                    CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                    sheet.addMergedRegion(cellRangeAddress);
                }
            }

        }
    }
}

Code call (the data is just fun, and I really can't make it up!)

//Automatically merge cells. Rows are merged primarily and columns are merged secondarily (merge if the contents are the same)
    @PostMapping("/tableDataMerge")
    public void tableDataMerge(HttpServletResponse response) throws IOException {
        //Excel header data
        List<List<String>> headers=new ArrayList<>();
        //Basic information
        //Serial number
        List<String> baseSerialHead=new ArrayList<>();
        baseSerialHead.add("Basic information");
        baseSerialHead.add("Basic information");
        baseSerialHead.add("Serial number");
        //business
        List<String> baseShopHead=new ArrayList<>();
        baseShopHead.add("Basic information");
        baseShopHead.add("Basic information");
        baseShopHead.add("business");
        //particular year
        List<String> baseYearHead=new ArrayList<>();
        baseYearHead.add("Basic information");
        baseYearHead.add("Basic information");
        baseYearHead.add("particular year");

        //Product list (drinks)
        //mineral water
        //Wahaha
        List<String> DrinkWahahaHead=new ArrayList<>();
        DrinkWahahaHead.add("drink");
        DrinkWahahaHead.add("mineral water");
        DrinkWahahaHead.add("Wahaha");
        //NongFu Spring
        List<String> DrinkNongfuHead=new ArrayList<>();
        DrinkNongfuHead.add("drink");
        DrinkNongfuHead.add("mineral water");
        DrinkNongfuHead.add("NongFu Spring");

        //Drinks
        //arctic ocean
        List<String> DrinkArcticHead=new ArrayList<>();
        DrinkArcticHead.add("drink");
        DrinkArcticHead.add("Drinks");
        DrinkArcticHead.add("arctic ocean");

        //Hongbaolai
        List<String> DrinkHBLHead=new ArrayList<>();
        DrinkHBLHead.add("drink");
        DrinkHBLHead.add("Drinks");
        DrinkHBLHead.add("Hongbaolai");

        //Wine
        //Tsingtao Beer
        List<String> DrinkQDHead=new ArrayList<>();
        DrinkQDHead.add("drink");
        DrinkQDHead.add("Wine");
        DrinkQDHead.add("Tsingtao Beer");

        //Excel header data filling
        headers.add(baseSerialHead);
        headers.add(baseShopHead);
        headers.add(baseYearHead);
        headers.add(DrinkWahahaHead);
        headers.add(DrinkNongfuHead);
        headers.add(DrinkArcticHead);
        headers.add(DrinkHBLHead);
        headers.add(DrinkQDHead);

        //Excel table content data
        List<List<String>> datas=new ArrayList<>();
        //First row of data
        List<String> dataFirst=new ArrayList<>();
        dataFirst.add("1");
        dataFirst.add("A");
        dataFirst.add("2010");
        dataFirst.add("1.5");
        dataFirst.add("1.5");
        dataFirst.add("5.0");
        dataFirst.add("1.5");
        dataFirst.add("6.0");
        //Second row data
        List<String> dataSecond=new ArrayList<>();
        dataSecond.add("2");
        dataSecond.add("A");
        dataSecond.add("2020");
        dataSecond.add("2.0");
        dataSecond.add("2.0");
        dataSecond.add("2.0");
        dataSecond.add("2.0");
        dataSecond.add("8.0");
        //Third row data
        List<String> dataThird=new ArrayList<>();
        dataThird.add("3");
        dataThird.add("B");
        dataThird.add("2010");
        dataThird.add("0.9");
        dataThird.add("2.5");
        dataThird.add("8.0");
        dataThird.add("8.0");
        dataThird.add("8.0");
        //Fourth row data
        List<String> dataFourth=new ArrayList<>();
        dataFourth.add("4");
        dataFourth.add("B");
        dataFourth.add("2020");
        dataFourth.add("2.0");
        dataFourth.add("2.5");
        dataFourth.add("8.0");
        dataFourth.add("8.0");
        dataFourth.add("8.1");
        //The fifth row of data
        List<String> dataFifth=new ArrayList<>();
        dataFifth.add("5");
        dataFifth.add("c");
        dataFifth.add("2010");
        dataFifth.add("2.0");
        dataFifth.add("2.5");
        dataFifth.add("8.0");
        dataFifth.add("8.0");
        dataFifth.add("8.1");
        //The sixth row of data
        List<String> dataSixth=new ArrayList<>();
        dataSixth.add("6");
        dataSixth.add("c");
        dataSixth.add("2020");
        dataSixth.add("2.0");
        dataSixth.add("2.5");
        dataSixth.add("7.0");
        dataSixth.add("8.0");
        dataSixth.add("8.0");



        //Excel table content data filling
        datas.add(dataFirst);
        datas.add(dataSecond);
        datas.add(dataThird);
        datas.add(dataFourth);
        datas.add(dataFifth);
        datas.add(dataSixth);
        //excel generation

        // Note that some students have reported that using swagger will lead to various problems. Please use the browser or postman directly
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // Here is urlencoder Encode can prevent Chinese garbled code. Of course, it has nothing to do with easyexcel
        String fileName = URLEncoder.encode("Rows and columns are merged automatically, with row merging as the primary and column merging as the secondary", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        int mergeColumnIndex =0;
        int mergeRowIndex = 2;//Generally, if it is set to the high-order 3 of Excel header data, it will be 2, for example: 0,1,2
        //All configurations are inherited, and the configuration of Workbook will be inherited by sheet, so when setting parameters with EasyExcel, in EasyExcel Before the sheet () method, the scope is the whole sheet, and then for a single sheet
        EasyExcel.write(response.getOutputStream()).registerWriteHandler(getCellCenterStyle()).registerWriteHandler(new ExcelFillCellMergeHandler(mergeColumnIndex,mergeRowIndex)).head(headers).sheet("sheet1").doWrite(datas);

    }

Call presentation:

Before consolidation:

After merger

Keywords: Java

Added by debigmac on Sun, 30 Jan 2022 04:30:15 +0200