EasyExcel之SheetWriteHandler:解鎖Excel寫入的高階玩法

引言

在 EasyExcel 強大的功能體系中,SheetWriteHandler 接口是一個關鍵的組成部分。它允許開發者在寫入 Excel 的 Sheet 時進行自定義處理,為實現各種復雜的業務需求提供了強大的支持。通過深入了解和運用 SheetWriteHandler 接口,我們能夠更加靈活地控制 Excel 的寫入過程,實現諸如設置復雜樣式、動態合并單元格、添加數據驗證等功能,從而滿足多樣化的業務場景。接下來,讓我們一起深入探索 EasyExcel 中 SheetWriteHandler 接口的常用使用業務場景吧。

一、SheetWriteHandler 接口簡介

1.1 接口定義與作用

在 EasyExcel 框架中,SheetWriteHandler 接口是實現對 Excel 寫入過程中 Sheet 級別的自定義操作的關鍵接口。它位于整個 EasyExcel 體系的核心位置,是開發者進行復雜 Excel 文件生成和處理的重要工具。該接口允許開發者在創建 Sheet、寫入 Sheet 前后等關鍵節點介入,對 Sheet 的各種屬性和內容進行自定義設置,從而滿足多樣化的業務需求。

在實際開發中,很多場景下默認的 Excel 生成方式無法滿足復雜的業務需求。如在財務報表導出時,需要對 Sheet 進行特定的樣式設置,包括字體、字號、顏色、背景色、邊框等,以突出顯示重要數據和區分不同的數據區域;在數據統計報表中,可能需要根據數據的不同對某些單元格或區域進行合并,使報表結構更加清晰直觀。這些復雜的需求都可以通過 SheetWriteHandler 接口來實現。通過實現該接口,開發者能夠更加靈活地控制 Excel 文件的生成過程,提高數據展示的質量和用戶體驗。

1.2 核心方法剖析

SheetWriteHandler 接口主要包含四個核心方法,它們在 Excel 寫入過程的不同階段發揮著重要作用。

  • beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder):這個方法在 Sheet 創建之前被調用。在該方法中,開發者可以進行一些初始化的操作,如設置一些全局的 Sheet 屬性,雖然這種情況相對較少。假設我們要在創建 Sheet 前,根據業務需求決定是否啟用某個特定的 Sheet 功能,就可以在這個方法中進行判斷和設置。例如,如果業務要求某些特殊用戶導出的 Excel 中,Sheet 要支持特定的打印設置,我們可以在這個方法中根據用戶標識來進行相應的打印設置初始化 。
  • afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder):在 Sheet 創建完成后,此方法被觸發。這是一個非常常用的方法,很多與 Sheet 初始化相關的操作都可以在此完成。比如設置 Sheet 的默認列寬、行高,添加自定義的表頭樣式,或者進行單元格的合并操作等。在一個學生成績管理系統中,導出學生成績報表時,我們可以在這個方法中合并第一行的單元格,用于顯示報表的總標題,使報表更加美觀和規范。
  • beforeSheetWrite(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder):該方法在開始寫入 Sheet 數據之前執行。此時,我們可以對即將寫入的數據進行一些預處理操作,或者設置一些與寫入數據相關的屬性。在一個電商訂單數據導出場景中,我們可以在這個方法中根據訂單的狀態對數據進行篩選,只寫入符合特定狀態(如已完成訂單)的數據,同時設置寫入數據的格式,確保數據以正確的格式展示在 Excel 中。
  • afterSheetWrite(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder):當 Sheet 數據寫入完成后,這個方法被調用。在這個階段,我們可以進行一些收尾工作,如添加數據統計信息、插入圖表或者對整個 Sheet 進行最后的格式調整等。比如在一個銷售數據報表導出后,我們可以在這個方法中計算銷售總額、平均值等統計數據,并將其添加到 Sheet 的指定位置,同時對整個 Sheet 進行格式優化,使其更符合閱讀習慣 。

二、常用業務場景解析

2.1 數據驗證與下拉框設置

2.1.1 簡單固定下拉框實現

在許多數據錄入場景中,為了確保數據的準確性和一致性,常常需要使用下拉框來限制用戶的輸入選項。利用 SheetWriteHandler 接口,我們可以輕松實現簡單固定下拉框的設置。

以員工性別錄入為例,假設我們在導出員工信息表格時,希望 “性別” 列只能選擇 “男” 或 “女”。首先,我們需要創建一個實現 SheetWriteHandler 接口的類,如GenderDropDownHandler。在afterSheetCreate方法中,我們進行下拉框的設置操作。

import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddressList;public class GenderDropDownHandler implements SheetWriteHandler {@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 創建數據驗證輔助工具DataValidationHelper helper = sheet.getDataValidationHelper();// 設置下拉框數據String[] genderOptions = {"男","女"};// 創建數據驗證約束,這里使用顯式列表約束,將下拉框選項作為約束條件DataValidationConstraint constraint = helper.createExplicitListConstraint(genderOptions);// 設置下拉框的范圍,這里從第二行(索引為1)開始,到第100行,列索引為1(假設性別列在第二列)CellRangeAddressList addressList = new CellRangeAddressList(1, 100, 1, 1);// 創建數據驗證對象,將約束和范圍關聯起來DataValidation dataValidation = helper.createValidation(constraint, addressList);// 將數據驗證添加到Sheet中,這樣就完成了下拉框的設置sheet.addValidationData(dataValidation);}}

在上述代碼中,afterSheetCreate方法在 Sheet 創建完成后被調用。首先獲取工作簿和 Sheet 對象,然后通過DataValidationHelper創建數據驗證約束,將固定的性別選項作為約束條件。接著定義下拉框的作用范圍,即從第二行到第 100 行的第二列。最后創建數據驗證對象并添加到 Sheet 中,從而實現了 “性別” 列的簡單固定下拉框設置。

在導出 Excel 時,注冊這個處理器即可:

String fileName = "employee_info.xlsx";List < Employee > employeeList = getEmployeeList(); //獲取員工信息列表EasyExcel.write(fileName, Employee.class).registerWriteHandler(new GenderDropDownHandler()).sheet("員工信息").doWrite(employeeList);

通過這種方式,生成的 Excel 文件中 “性別” 列就會顯示為下拉框,用戶只能從 “男”“女” 兩個選項中選擇,有效避免了性別錄入錯誤。

2.1.2 動態下拉框實現

在實際業務中,下拉框的選項往往不是固定不變的,而是需要根據業務需求從數據庫或其他動態源獲取。比如在一個電商系統中,導出商品信息表格時,“商品分類” 列的下拉框選項需要實時從數據庫中查詢獲取。

假設我們有一個CategoryService用于從數據庫中獲取商品分類信息,實現動態下拉框的步驟如下:

首先,創建一個實現 SheetWriteHandler 接口的類,如DynamicCategoryDropDownHandler。在afterSheetCreate方法中,獲取動態的下拉框數據,并進行相應設置。

import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddressList;public class DynamicCategoryDropDownHandler implements SheetWriteHandler {private CategoryService categoryService;public DynamicCategoryDropDownHandler(CategoryService categoryService) {this.categoryService = categoryService;}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();DataValidationHelper helper = sheet.getDataValidationHelper();// 從數據庫獲取商品分類數據List < String > categoryList = categoryService.getCategories();String[] categoryArray = categoryList.toArray(new String[0]);DataValidationConstraint constraint = helper.createExplicitListConstraint(categoryArray);// 設置下拉框范圍,假設商品分類列在第三列,從第二行開始到第200行CellRangeAddressList addressList = new CellRangeAddressList(1, 200, 2, 2);DataValidation dataValidation = helper.createValidation(constraint, addressList);sheet.addValidationData(dataValidation);}}

在上述代碼中,DynamicCategoryDropDownHandler類的構造函數接收一個CategoryService實例,用于獲取動態的商品分類數據。在afterSheetCreate方法中,首先從CategoryService獲取商品分類列表,將其轉換為數組后創建數據驗證約束。然后定義下拉框的作用范圍,最后創建數據驗證對象并添加到 Sheet 中。

在導出 Excel 時,同樣注冊這個處理器:

String fileName = "product_info.xlsx";List < Product > productList = getProductList(); //獲取商品信息列表CategoryService categoryService = new CategoryService(); //假設這是獲取商品分類服務的實例化EasyExcel.write(fileName, Product.class).registerWriteHandler(new DynamicCategoryDropDownHandler(categoryService)).sheet("商品信息").doWrite(productList);

通過這種方式,生成的 Excel 文件中 “商品分類” 列的下拉框選項會根據數據庫中的實時數據動態變化,滿足了業務的動態需求。

2.1.3 級聯下拉框實現

級聯下拉框在復雜的數據錄入場景中非常常見,比如省市級聯選擇。下面以導出地區信息表格時實現省市級聯下拉框為例,給出完整實現代碼和詳細解釋。

首先,我們需要準備省級和市級的數據,并創建一個隱藏的 Sheet 來存儲它們之間的映射關系。假設我們有兩個列表provinceList和cityList分別存儲省份和城市信息,且cityList中每個城市對象包含所屬省份的標識。

import com.alibaba.excel.EasyExcel;import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddressList;import java.util.*;public class ProvinceCityCascadeHandler implements SheetWriteHandler {private List < String > provinceList;private List < City > cityList;public ProvinceCityCascadeHandler(List < String > provinceList, List < City > cityList) {this.provinceList = provinceList;this.cityList = cityList;}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet mainSheet = writeSheetHolder.getSheet();// 創建隱藏Sheet存儲省-市映射關系Sheet hiddenSheet = workbook.createSheet("hidden_province_city");workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);// 在隱藏Sheet中寫入省份數據,從第一行第一列開始writeDataToSheet(hiddenSheet, provinceList, 0, 0);// 構建省-市映射關系并寫入隱藏Sheet,從第一行第二列開始Map < String, List < String >> provinceCityMap = buildProvinceCityMap();int rowIndex = 0;for (String province: provinceList) {List < String > cities = provinceCityMap.get(province);writeDataToSheet(hiddenSheet, cities, rowIndex, 1);rowIndex += cities.size();}// 設置省份下拉框DataValidationHelper mainHelper = mainSheet.getDataValidationHelper();DataValidationConstraint provinceConstraint = mainHelper.createExplicitListConstraint(provinceList.toArray(new String[0]));CellRangeAddressList provinceAddressList = new CellRangeAddressList(1, 100, 0, 0);DataValidation provinceValidation = mainHelper.createValidation(provinceConstraint, provinceAddressList);mainSheet.addValidationData(provinceValidation);// 設置市級下拉框for (int i = 1; i <= 100; i++) {CellRangeAddressList cityAddressList = new CellRangeAddressList(i, i, 1, 1);String formula = "INDIRECT(\"hidden_province_city!$B$" + (findProvinceRowIndex(provinceList, mainSheet.getRow(i).getCell(0).getStringCellValue()) + 1) + ":$B$" + (findProvinceRowIndex(provinceList, mainSheet.getRow(i).getCell(0).getStringCellValue()) + getCityCount(provinceCityMap, mainSheet.getRow(i).getCell(0).getStringCellValue())) + "\")";DataValidationConstraint cityConstraint = mainHelper.createFormulaListConstraint(formula);DataValidation cityValidation = mainHelper.createValidation(cityConstraint, cityAddressList);mainSheet.addValidationData(cityValidation);}}// 將數據寫入指定Sheet的指定位置private void writeDataToSheet(Sheet sheet, List < String > dataList, int startRow, int startCol) {for (int i = 0; i < dataList.size(); i++) {Row row = sheet.getRow(startRow + i);if (row == null) {row = sheet.createRow(startRow + i);}Cell cell = row.createCell(startCol);cell.setCellValue(dataList.get(i));}}// 構建省-市映射關系private Map < String, List < String >> buildProvinceCityMap() {Map < String, List < String >> provinceCityMap = new HashMap < > ();for (City city: cityList) {if (!provinceCityMap.containsKey(city.getProvince())) {provinceCityMap.put(city.getProvince(), new ArrayList < > ());}provinceCityMap.get(city.getProvince()).add(city.getName());}return provinceCityMap;}// 根據省份名稱查找在隱藏Sheet中的行索引private int findProvinceRowIndex(List < String > provinceList, String province) {return provinceList.indexOf(province);}// 獲取某個省份對應的城市數量private int getCityCount(Map < String, List < String >> provinceCityMap, String province) {return provinceCityMap.getOrDefault(province, Collections.emptyList()).size();}// 城市類,包含省份和城市名稱public static class City {private String province;private String name;public City(String province, String name) {this.province = province;this.name = name;}public String getProvince() {return province;}public String getName() {return name;}}}

在上述代碼中,ProvinceCityCascadeHandler類實現了 SheetWriteHandler 接口。在afterSheetCreate方法中,首先創建一個隱藏的 Sheet 用于存儲省 - 市映射關系,并將省份和對應的城市數據寫入該 Sheet。然后設置省份下拉框,將所有省份作為固定選項。對于市級下拉框,通過INDIRECT函數根據選擇的省份動態獲取對應的城市列表作為下拉選項。writeDataToSheet方法用于將數據寫入指定 Sheet 的指定位置,buildProvinceCityMap方法構建省 - 市映射關系,findProvinceRowIndex方法查找省份在隱藏 Sheet 中的行索引,getCityCount方法獲取某個省份對應的城市數量。

在導出 Excel 時,注冊這個處理器:

String fileName = "area_info.xlsx";List < Area > areaList = getAreaList(); //獲取地區信息列表List < String > provinceList = getProvinceList(); //獲取省份列表List < ProvinceCityCascadeHandler.City > cityList = getCityList(); //獲取城市列表EasyExcel.write(fileName, Area.class).registerWriteHandler(new ProvinceCityCascadeHandler(provinceList, cityList)).sheet("地區信息").doWrite(areaList);

通過以上實現,生成的 Excel 文件中 “省份” 和 “城市” 列形成了級聯下拉框,用戶選擇省份后,城市下拉框會動態顯示該省份對應的城市選項,大大提高了數據錄入的準確性和便捷性。

2.2 樣式與格式設置

2.2.1 表頭樣式定制

在導出 Excel 報表時,為了使報表更加美觀和易讀,常常需要對表頭進行樣式定制。利用 SheetWriteHandler 接口,我們可以在 Sheet 創建前后輕松設置表頭的字體、背景色、對齊方式等樣式。

以一個學生成績報表為例,假設我們希望表頭字體為黑體、加粗,背景色為淺藍色,水平和垂直居中對齊。創建一個實現 SheetWriteHandler 接口的類,如HeaderStyleHandler,在afterSheetCreate方法中進行表頭樣式設置。

import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import org.apache.poi.ss.usermodel.*;public class HeaderStyleHandler implements SheetWriteHandler {@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 創建表頭樣式對象CellStyle headerStyle = workbook.createCellStyle();// 設置水平居中對齊headerStyle.setAlignment(HorizontalAlignment.CENTER);// 設置垂直居中對齊headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 設置背景色為淺藍色headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 創建字體對象并設置為黑體、加粗Font headerFont = workbook.createFont();headerFont.setFontName("黑體");headerFont.setBold(true);headerStyle.setFont(headerFont);// 獲取表頭行,假設表頭在第一行Row headerRow = sheet.getRow(0);if (headerRow == null) {headerRow = sheet.createRow(0);}// 遍歷表頭行的單元格,應用表頭樣式for (int i = 0; i < headerRow.getLastCellNum(); i++) {Cell cell = headerRow.getCell(i);if (cell == null) {cell = headerRow.createCell(i);}cell.setCellStyle(headerStyle);}}}

在上述代碼中,afterSheetCreate方法在 Sheet 創建完成后被調用。首先獲取工作簿和 Sheet 對象,然后創建表頭樣式對象headerStyle,設置其對齊方式、背景色和字體等屬性。接著獲取表頭行,如果表頭行不存在則創建。最后遍歷表頭行的單元格,為每個單元格應用表頭樣式。

在導出 Excel 時,注冊這個處理器:

String fileName = "student_score.xlsx";List < StudentScore > studentScoreList = getStudentScoreList(); //獲取學生成績列表EasyExcel.write(fileName, StudentScore.class).registerWriteHandler(new HeaderStyleHandler()).sheet("學生成績").doWrite(studentScoreList);

通過這種方式,生成的 Excel 文件表頭將呈現出我們定制的樣式,使報表更加清晰美觀,提升了用戶體驗。

2.2.2 數據行樣式設置

除了表頭樣式,根據數據內容動態設置數據行樣式也是常見的業務需求。比如在一個銷售報表中,我們希望根據銷售額的高低為數據行設置不同的背景色,以突出顯示重要數據。

假設我們有一個Sale類表示銷售數據,包含amount(銷售額)字段。創建一個實現 SheetWriteHandler 接口的類,如DataRowStyleHandler,在beforeSheetWrite方法中根據數據內容設置數據行樣式。

import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import com.alibaba.excel.write.metadata.holder.WriteTableHolder;import org.apache.poi.ss.usermodel.*;public class DataRowStyleHandler implements SheetWriteHandler {@Overridepublic void beforeSheetWrite(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 創建高銷售額行樣式對象CellStyle highAmountStyle = workbook.createCellStyle();highAmountStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());highAmountStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

// 創建低銷售額行樣式對象

## 三、實際案例應用

### 3.1 電商訂單數據導出

在電商系統中,訂單數據的導出是一項常見且重要的任務。假設我們有一個電商訂單數據導出的場景,需要將訂單數據導出到Excel表格中,并且希望在導出的表格中設置一些特殊的功能。具體需求如下:

- 在“訂單狀態”列設置下拉框,下拉選項為“待付款”“待發貨”“已發貨”“已完成”“已取消”,方便用戶查看和篩選不同狀態的訂單。

- 將“訂單金額”列的數據格式設置為貨幣格式,保留兩位小數,以符合財務數據的展示規范。

- 對于“訂單備注”列中包含特定關鍵詞(如“加急”)的訂單,添加特殊批注,提醒相關人員注意。

首先,創建一個實現SheetWriteHandler接口的類,如`OrderExportHandler`。在這個類中,我們實現上述三個需求。

import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import com.alibaba.excel.write.metadata.holder.WriteTableHolder;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddressList;public class OrderExportHandler implements SheetWriteHandler {@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 設置訂單狀態下拉框DataValidationHelper helper = sheet.getDataValidationHelper();String[] statusOptions = {"待付款","待發貨","已發貨","已完成","已取消"};DataValidationConstraint statusConstraint = helper.createExplicitListConstraint(statusOptions);CellRangeAddressList statusAddressList = new CellRangeAddressList(1, 1000, 2, 2); // 假設訂單狀態列在第三列,從第二行開始到第1000行DataValidation statusValidation = helper.createValidation(statusConstraint, statusAddressList);sheet.addValidationData(statusValidation);// 設置訂單金額列格式為貨幣格式CellStyle amountStyle = workbook.createCellStyle();CreationHelper creationHelper = workbook.getCreationHelper();amountStyle.setDataFormat(creationHelper.createDataFormat().getFormat("¥#,##0.00"));for (int i = 1; i <= 1000; i++) {Row row = sheet.getRow(i);if (row == null) {row = sheet.createRow(i);}Cell amountCell = row.getCell(3); // 假設訂單金額列在第四列if (amountCell == null) {amountCell = row.createCell(3);}amountCell.setCellStyle(amountStyle);}}@Overridepublic void afterSheetWrite(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 為包含“加急”關鍵詞的訂單備注添加批注for (int i = 1; i <= 1000; i++) {Row row = sheet.getRow(i);if (row == null) {continue;}Cell remarkCell = row.getCell(4); // 假設訂單備注列在第五列if (remarkCell != null && remarkCell.getCellType() == CellType.STRING && remarkCell.getStringCellValue().contains("加急")) {Drawing < ? > drawing = sheet.createDrawingPatriarch();ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, (short) 4, i, (short) 6, i + 1);Comment comment = drawing.createCellComment(anchor);comment.setString(new XSSFRichTextString("此訂單為加急訂單,請優先處理"));remarkCell.setCellComment(comment);}}}}

在上述代碼中,afterSheetCreate方法在 Sheet 創建完成后被調用,用于設置訂單狀態下拉框和訂單金額列的格式。afterSheetWrite方法在 Sheet 數據寫入完成后被調用,用于為包含 “加急” 關鍵詞的訂單備注添加批注。

在導出 Excel 時,注冊這個處理器:

String fileName = "order_export.xlsx";List < Order > orderList = getOrderList(); //獲取訂單信息列表EasyExcel.write(fileName, Order.class).registerWriteHandler(new OrderExportHandler()).sheet("訂單數據").doWrite(orderList);

通過上述實現,生成的 Excel 文件中,“訂單狀態” 列會顯示為下拉框,方便用戶篩選訂單;“訂單金額” 列會以貨幣格式展示,更直觀地呈現金額數據;對于包含 “加急” 關鍵詞的訂單備注,會添加特殊批注,提醒相關人員注意。這樣的導出表格能夠更好地滿足電商業務中對訂單數據處理和分析的需求。

3.2 員工信息管理系統

在員工信息管理系統中,導出員工信息表格也是一個常見的功能。假設我們需要實現以下功能:

  • 在 “部門” 列設置下拉框,下拉選項從數據庫中動態獲取,方便用戶選擇和查看不同部門的員工信息。
  • 將 “員工編號”“姓名”“部門”“職位” 等關鍵信息列設置為加粗字體,突出顯示這些重要信息。
  • 對于 “員工備注” 列中的內容,添加相應的批注,詳細說明備注的具體含義。

首先,創建一個實現 SheetWriteHandler 接口的類,如EmployeeExportHandler。假設我們有一個DepartmentService用于從數據庫中獲取部門信息。

import com.alibaba.excel.write.handler.SheetWriteHandler;import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;import com.alibaba.excel.write.metadata.holder.WriteTableHolder;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.CellRangeAddressList;public class EmployeeExportHandler implements SheetWriteHandler {private DepartmentService departmentService;public EmployeeExportHandler(DepartmentService departmentService) {this.departmentService = departmentService;}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 設置部門下拉框DataValidationHelper helper = sheet.getDataValidationHelper();List < String > departmentList = departmentService.getDepartments();String[] departmentArray = departmentList.toArray(new String[0]);DataValidationConstraint departmentConstraint = helper.createExplicitListConstraint(departmentArray);CellRangeAddressList departmentAddressList = new CellRangeAddressList(1, 500, 2, 2); // 假設部門列在第三列,從第二行開始到第500行DataValidation departmentValidation = helper.createValidation(departmentConstraint, departmentAddressList);sheet.addValidationData(departmentValidation);// 設置關鍵信息列字體為加粗CellStyle keyInfoStyle = workbook.createCellStyle();Font boldFont = workbook.createFont();boldFont.setBold(true);keyInfoStyle.setFont(boldFont);int[] keyColumns = {0,1,2,3}; // 假設員工編號、姓名、部門、職位分別在第1、2、3、4列for (int col: keyColumns) {for (int i = 0; i <= 500; i++) {Row row = sheet.getRow(i);if (row == null) {row = sheet.createRow(i);}Cell cell = row.getCell(col);if (cell == null) {cell = row.createCell(col);}cell.setCellStyle(keyInfoStyle);}}}@Overridepublic void afterSheetWrite(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 為員工備注添加批注for (int i = 1; i <= 500; i++) {Row row = sheet.getRow(i);if (row == null) {continue;}Cell remarkCell = row.getCell(5); // 假設員工備注列在第六列if (remarkCell != null && remarkCell.getCellType() == CellType.STRING) {Drawing < ? > drawing = sheet.createDrawingPatriarch();ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, (short) 5, i, (short) 7, i + 1);Comment comment = drawing.createCellComment(anchor);comment.setString(new XSSFRichTextString("備注內容:" + remarkCell.getStringCellValue()));remarkCell.setCellComment(comment);}}}}

在上述代碼中,afterSheetCreate方法用于設置部門下拉框和關鍵信息列的加粗字體樣式。afterSheetWrite方法用于為員工備注添加批注,詳細說明備注內容。

在導出 Excel 時,注冊這個處理器:

String fileName = "employee_export.xlsx";List < Employee > employeeList = getEmployeeList(); //獲取員工信息列表DepartmentService departmentService = new DepartmentService(); //假設這是獲取部門信息服務的實例化EasyExcel.write(fileName, Employee.class).registerWriteHandler(new EmployeeExportHandler(departmentService)).sheet("員工信息").doWrite(employeeList);

通過以上實現,生成的 Excel 文件中,“部門” 列會顯示為動態下拉框,方便用戶選擇和查看不同部門的員工信息;關鍵信息列會以加粗字體突出顯示,便于用戶快速識別重要信息;“員工備注” 列會添加詳細的批注,幫助用戶更好地理解備注內容。這樣的導出表格能夠提高員工信息管理系統的使用效率和數據展示效果。

四、使用注意事項與優化建議

4.1 性能優化

在使用 SheetWriteHandler 進行大量數據導出時,性能問題不容忽視。頻繁創建對象會占用較多的內存資源,影響系統的整體性能。在設置樣式時,如果每次都創建新的CellStyle和Font對象,當數據量較大時,會導致內存消耗急劇增加。為了減少不必要的對象創建,可以在類的成員變量中創建并復用這些對象。

文件 I/O 操作也是影響性能的關鍵因素。頻繁的磁盤寫入操作會降低導出速度,特別是在處理大數據量時。可以采用分批寫入的方式,將數據分批次寫入 Excel 文件,而不是一次性寫入。這樣可以減少 I/O 操作的次數,提高導出效率。在導出電商訂單數據時,如果訂單數據量很大,可以每次讀取 1000 條訂單數據寫入 Excel,完成一批后再寫入下一批 。

4.2 兼容性問題

不同版本的 EasyExcel 中,SheetWriteHandler 接口及相關方法可能存在兼容性差異。在某些版本中,afterSheetCreate方法的參數類型或行為可能發生了變化,如果不注意版本更新說明,直接在新版本中使用舊版本的代碼,可能會導致編譯錯誤或運行時異常。因此,在升級 EasyExcel 版本時,一定要仔細閱讀版本更新說明,了解接口和方法的變化情況。如果遇到兼容性問題,可以參考官方文檔或社區論壇,尋找解決方案。有時候可能需要對代碼進行相應的調整,以適應新版本的要求 。

4.3 常見錯誤與解決方法

在使用 SheetWriteHandler 過程中,可能會遇到一些常見錯誤。當下拉框數據不顯示時,可能是因為數據驗證的設置不正確,如下拉框的范圍設置錯誤、數據驗證約束的創建方式有誤等。此時,需要仔細檢查數據驗證的相關代碼,確保范圍和約束設置正確。如果樣式設置無效,可能是因為樣式對象沒有正確應用到單元格上,或者樣式設置的順序有誤。在設置樣式時,要確保先創建樣式對象,然后將其應用到相應的單元格上,并且注意樣式設置的先后順序,避免后面的設置覆蓋了前面的設置 。

五、總結與展望

5.1 回顧 SheetWriteHandler 的強大功能

通過對 SheetWriteHandler 接口在數據驗證與下拉框設置、樣式與格式設置以及實際案例中的應用探討,我們充分領略了其在處理復雜 Excel 寫入需求時的強大能力。從簡單固定下拉框到動態下拉框和級聯下拉框的實現,為數據錄入提供了準確且便捷的方式;表頭和數據行樣式的定制,使 Excel 報表更加美觀易讀;在電商訂單數據導出和員工信息管理系統等實際案例中,進一步驗證了其在滿足多樣化業務需求方面的有效性,能夠幫助我們生成符合各種業務場景需求的高質量 Excel 文件。

5.2 對未來應用拓展的思考

隨著業務的不斷發展和數據處理需求的日益復雜,SheetWriteHandler 接口在未來有著更廣闊的應用拓展空間。在大數據分析領域,結合海量數據的處理需求,我們可以進一步優化其性能,實現更高效的數據寫入和復雜報表生成。在多語言支持方面,根據不同地區和用戶的語言習慣,動態設置 Excel 的表頭、批注等內容為相應語言,提升國際化應用水平。鼓勵讀者在實際開發中,不斷探索和嘗試新的應用場景,挖掘 SheetWriteHandler 接口的更多潛力,為解決復雜的業務問題提供創新的思路和方法 。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87859.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87859.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87859.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python單例模式魔法方法or屬性

1.單例模式概念定義:單例模式(Singleton Pattern)是一種創建型設計模式&#xff0c;它確保一個類只能有一個實例&#xff0c;并提供一個全局訪問點來獲取該實例。這種模式在需要控制資源訪問、配置管理或協調系統操作時特別有用。核心特點:私有構造函數&#xff1a;防止外部通過…

【Kubernetes系列】Kubernetes 資源請求(Requests)

博客目錄 引言一、資源請求的基本概念1.1 什么是資源請求1.2 請求與限制的區別 二、CPU 請求的深入解析2.1 CPU 請求的單位與含義2.2 CPU 請求的調度影響2.3 CPU 請求與限制的關系 三、內存請求的深入解析3.1 內存請求的單位與含義3.2 內存請求的調度影響3.3 內存請求的特殊性 …

大型語言模型中的自動化思維鏈提示

摘要 大型語言模型&#xff08;LLMs&#xff09;能夠通過生成中間推理步驟來執行復雜的推理任務。為提示演示提供這些步驟的過程被稱為思維鏈&#xff08;CoT&#xff09;提示。CoT提示有兩種主要范式。一種使用簡單的提示語&#xff0c;如“讓我們一步一步思考”&#xff0c;…

Private Set Generation with Discriminative Information(2211.04446v1)

1. 遇到什么問題&#xff0c;解決了什么遇到的問題現有差分隱私生成模型受限于高維數據分布建模的復雜性&#xff0c;合成樣本實用性不足。深度生成模型訓練依賴大量數據&#xff0c;加入隱私約束后更難優化&#xff0c;且不保證下游任務&#xff08;如分類&#xff09;的最優解…

C++編程語言入門指南

一、C語言概述 C是由丹麥計算機科學家Bjarne Stroustrup于1979年在貝爾實驗室開發的一種靜態類型、編譯式、通用型編程語言。最初被稱為"C with Classes"(帶類的C)&#xff0c;1983年更名為C。它既具有高級語言的抽象特性&#xff0c;又保留了底層硬件操作能力&…

ZED相機與Foxglove集成:加速機器人視覺調試效率的實用方案

隨著機器人技術的發展&#xff0c;實時視覺數據流的高效傳輸和可視化成為提升系統性能的重要因素。通過ZED相機&#xff08;包括ZED 2i和ZED X&#xff09;與Foxglove Studio平臺的結合&#xff0c;開發者能夠輕松訪問高質量的2D圖像、深度圖和點云數據&#xff0c;從而顯著提高…

目標檢測新紀元:DETR到Mamba實戰解析

&#x1f680;【實戰分享】目標檢測的“后 DE?”時代&#xff1a;DETR/DINO/RT-DETR及新型骨干網絡探索&#xff08;含示例代碼&#xff09; 目標檢測從 YOLO、Faster R-CNN 到 Transformer 結構的 DETR&#xff0c;再到 DINO、RT-DETR&#xff0c;近兩年出現了許多新趨勢&am…

【IOS】XCode創建firstapp并運行(成為IOS開發者)

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 這篇文章主要介紹XCode創建firstapp并運行 學其所用&#xff0c;用其所學。——梁啟超 歡迎來到我的博客&#xff0c;一起學習&#xff0c;共同進步。 喜歡的朋友可以關注一下&#xff0c;下次更新不迷路…

class類和style內聯樣式的綁定 + 事件處理 + uniapp創建自定義頁面模板

目錄 一.class類的綁定 1.靜態編寫 2.動態編寫 二.style內聯樣式的綁定 三.事件處理 1.案例1 2.案例2 四.uniapp創建自定義頁面模板 1.為什么要這么做&#xff1f; 2.步驟 ①打開新建頁面的界面 ②在彈出的目錄下&#xff0c;新建模板文件 ③用HBuilderX打開該模板…

android 卡頓和丟幀區別

Android 卡頓&#xff08;Jank&#xff09;與丟幀&#xff08;Frame Drop&#xff09;的核心區別在于問題本質與用戶感知&#xff0c;以下是分層解析&#xff1a; ? 一、本質差異 維度卡頓&#xff08;Jank&#xff09;丟幀&#xff08;Frame Drop&#xff09;定義用戶可感知…

【python實用小腳本-125】基于 Python 的 Gmail 郵件發送工具:實現高效郵件自動化

引言 在現代辦公和開發環境中&#xff0c;郵件通信是一種重要的溝通方式。自動化發送郵件可以大大提高工作效率&#xff0c;例如發送通知、報告或文件。本文將介紹一個基于 Python 的 Gmail 郵件發送工具&#xff0c;它能夠通過 Gmail 的 SMTP 服務器發送郵件&#xff0c;并支持…

gateway斷言配置詳解

一、Predicate - 斷? 1、簡單用法 spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- After2017-01-20T17:42:47.789-07:00[America/Denver] 2、自定義斷言 新建類VipRoutePredicateFactory&#xff0c;注意VipRoutePredicateFactory名字…

基于大模型的尿毒癥全流程預測與診療方案研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與方法 1.3 國內外研究現狀 二、尿毒癥相關理論基礎 2.1 尿毒癥的定義、病因與發病機制 2.2 尿毒癥的癥狀與診斷標準 2.3 尿毒癥的治療方法概述 三、大模型技術原理與應用 3.1 大模型的基本概念與發展歷程 3.2 大模型…

裸金屬服務器租用平臺-青蛙云

企業對服務器性能與靈活性的要求與日俱增。青蛙云M-啟強裸金屬服務器租用平臺應運而生&#xff0c;為企業提供了一種兼具物理機性能和云計算彈性的解決方案。裸金屬服務器租用平臺的優勢?(一)高配性能&#xff0c;無虛擬化開銷?裸金屬服務器直接運行在物理硬件之上&#xff0…

[Terence Tao訪談] AlphaProof系統 | AI嗅覺 | 研究生學習 | 龐加萊猜想(高維) | 復雜問題簡單化

玩這些有趣的東西。通常情況下什么也得不到&#xff0c;你必須學會說&#xff1a;“好吧&#xff0c;再試一次&#xff0c;什么都沒發生&#xff0c;我會繼續前進。” DeepMind的AlphaProof系統 Q&#xff1a;DeepMind的AlphaProof系統是通過強化學習訓練的&#xff0c;使用的…

Aseprite工具入門教程4之動畫導入Unity

1、時間軸功能 &#xff08;1&#xff09;眼睛圖標 顯示/隱藏圖層圖層隱藏時無法繪制 &#xff08;2&#xff09;鎖定圖標 鎖定后無法移動或編輯圖層防止意外在錯誤圖層上繪制 &#xff08;3&#xff09;單元格圖標 兩個點代表幀分開&#xff0c;一個橢圓代表幀統一。分開就…

移動硬盤頻繁提示格式化?解決異常故障的正確方法

移動硬盤作為數據存儲的重要工具&#xff0c;不少人都習慣將照片、文檔、項目資料甚至整臺電腦的備份都放在里面。但有時&#xff0c;一件令人頭疼的事悄然發生&#xff1a; 插上硬盤&#xff0c;系統卻突然提示&#xff1a;“使用驅動器中的光盤之前需要將其格式化。是否要將…

Java泛型筆記

1 為什么需要泛型 Java5之前&#xff0c;是沒有泛型的。通過兩段代碼我們就可以知道為何我們需要泛型 public int addInt(int a, int b) {return a b; }public double addDouble(double a, double b) {return a b; } 實際開發中&#xff0c;經常有數值類型求和的需求&…

mysql 圖形化界面工具 DataGrip 安裝與配置

安裝地址&#xff1a; Download DataGrip: Cross-Platform IDE for Databases & SQLhttps://www.jetbrains.com/datagrip/download/?sectionwindows 添加數據源&#xff1a; 下載驅動文件&#xff1a;直接點擊下載即可 點擊測試連接&#xff1a;成功后點擊確定 顯示所有數…

linux下進程之間socket通信c程序例程

以下是一個基于 Linux 的 C 程序示例&#xff0c;展示了如何使用 Unix 域套接字&#xff08;Unix domain socket&#xff09;在不同進程之間互傳 JSON 消息。我們將實現一個簡單的客戶端 - 服務器模型&#xff0c;服務器監聽連接&#xff0c;客戶端連接到服務器并發送 JSON 消息…