1. 程式人生 > 其它 >合併行_Java POI Excel移動行和複製行的處理

合併行_Java POI Excel移動行和複製行的處理

技術標籤:合併行

Java POI Excel移動行和複製行的處理

POI操作Excel時,不支援移動行的操作,因此在需要通過複製行+刪除行+建立空白行的組合方式來達成移動行的效果。

坑點:

  • POI操作Excel文件和Excel軟體操作文件的理解是不一樣的,Excel軟體日常操作時不是嚴格區分空白行空行,而POI操作時必須嚴格區分,空白行可以直接操作,而空行則必須先執行建立行操作後才能操作。
  • copyRows 複製行從源位置到目標位置時,不是完全覆蓋模式,因此如果目標區域有內容,會用源區域有內容的部分替換到目標區域的內容,源區域沒有內容的部分仍會使用目標區域的內容。這個和Excel軟體操作中複製區域貼上到指定區域的操作的效果是不同的。另外copyRows
    的會忽略起始區域是空行的行,例如如果想將3-5行開始複製到7-9行,如果3、4行是空行,那麼拷貝後的結果為為7行為5行的內容。
  • removeRow 刪除行時,只會刪除行的資料,對於行的合併單元格等資料不會刪除,因為合併單元格是存放在sheet上。因此如果想同時刪除合併單元格,則需要先手動刪除合併單元格,再刪除行。由於合併單元格資料是存放在一個list上的,而刪除只提供下標刪除方式,因此刪除時需要按照下標大小倒序刪除。
  • copyRows 可以將源行的內容和樣式都拷貝到目標行。
  • 使用複製行+刪除行+建立空白行組合方式達到移動行的效果時,需要注意臨時區域的位置,保證臨時區域不會和最終結果有重疊區域。

實現的程式碼

if(CollectionUtils.isNotEmpty(dataList)) {
            int beginRowNo = data.getBeginRow();
            Row beginRow = sheet.getRow(beginRowNo);
            int addNo = dataList.size();
            int moveBeginRow = beginRowNo+1;
            //如果移動的開始行不存在,則建立一個空行,否則複製的時候會丟掉不存在的行
            if(sheet.getRow(moveBeginRow)==null){
                sheet.createRow(moveBeginRow);
            }
            int lastRowNo = sheet.getLastRowNum();

            //計算移動的行數
            int movedNo = lastRowNo-moveBeginRow+1;
            //臨時區的開始行
            int tempBeginRowNo = lastRowNo+addNo+1;
            //臨時區的結束行
            int tempLastRowNo = tempBeginRowNo+movedNo-1;
            //末尾部分的最終開始行
            int finalBeginRowNo = addNo+beginRowNo;

            //移動開始行以後的行到臨時的開始行
            moveRows(sheet,moveBeginRow,lastRowNo,tempBeginRowNo);
            //在空出位置插入新行並複製第一行的樣式
            for (int i = moveBeginRow; i <finalBeginRowNo; i++){
                Row row = sheet.getRow(i);
                if (row != null){
                    sheet.removeRow(row);
                }
                //建立空白的行
                sheet.createRow(i);
                if(beginRow!=null){
                    //複製第一行的樣式到當前行
                    sheet.copyRows(beginRowNo,beginRowNo, i, DEFAULT_ROW_COPY_POLICY);
                }
            }
            //將移動的行從臨時開始行回移新增好的行的末尾
            /*
              為什麼不直接第一次移動的預期的末尾呢?是因為POX只支援Copy行的操作,而copy操作不是完整的區間覆蓋。
              如果源區間內帶有格式,並且目的區間和源區間有重疊話,會導致無法copy後源區間和目的區間混合在一起,無法清理。
              因此實現移動行區間的操作時,需要先將源行區間複製到一個臨時的區域,然後清理掉源行區間的行和樣式,再插入需要新增的行
              ,最後再將被移動的行區間移動回新的行末尾。
             */
            moveRows(sheet,tempBeginRowNo,tempLastRowNo,finalBeginRowNo);


        }

    /**
     * 移動指定行區間到指定位置,並刪除移動後原地的行
     * @author 徐明龍 XuMingLong 2019-06-27
     * @param sheet
     * @param srcBeginRow
     * @param srcEndRow
     * @param destBeginRow
     * @return void
     */
    private static void moveRows(XSSFSheet sheet,int srcBeginRow,int srcEndRow,int destBeginRow){
        //先複製到目的位置
        sheet.copyRows(srcBeginRow, srcEndRow, destBeginRow, DEFAULT_ROW_COPY_POLICY);
        //清理之前殘留的合併單元格
        List<Integer> removeMergedRegion = new ArrayList<>();
        for(int i=sheet.getMergedRegions().size()-1;i>=0;i--){
            CellRangeAddress address = sheet.getMergedRegions().get(i);
            //如果合併單元格在複製前的位置,則刪除
            if(address.getFirstRow() >= srcBeginRow && address.getFirstRow()<destBeginRow ){
                removeMergedRegion.add(i);
            }
        };
        //執行清理合並的單元格
        for(Integer i:removeMergedRegion){
            sheet.removeMergedRegion(i);
        }
        //刪除移動後原地的行
        for (int i = srcBeginRow; i <= srcEndRow; i++){
            Row row = sheet.getRow(i);
            if (row != null){
                //刪除複製後殘留的行
                sheet.removeRow(row);
            }
        }

    }