POI开发指南2

  |   0 评论   |   0 浏览

图像

图像是图形支持的一部分。要添加图像,只需在绘图首字母上调用createPicture()。在编写时,支持以下类型:

  • PNG
  • JPG
  • DIB

应该注意的是,一旦将一幅图像添加到一张纸上,任何现有的图形都可能被擦除。


//create a new workbook
Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
//add picture data to this workbook.
InputStream is = new FileInputStream("image1.jpeg");
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = wb.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
is.close();
CreationHelper helper = wb.getCreationHelper();
//create sheet
Sheet sheet = wb.createSheet();
// Create the drawing patriarch.  This is the top level container for all shapes. 
Drawing drawing = sheet.createDrawingPatriarch();
//添加图片形状
ClientAnchor anchor = helper.createClientAnchor();
//设置图片的左上角,
//随后调用Picture#resize()将对其进行操作
anchor.setCol1(3);
anchor.setRow1(2);
Picture pict = drawing.createPicture(anchor, pictureIdx);
//相对于图片左上角自动调整图片大小
pict.resize();
//保存工作簿
String file = "picture.xls";
if(wb instanceof XSSFWorkbook) file += "x";
try (OutputStream fileOut = new FileOutputStream(file)) {
    wb.write(fileOut);
}

Warning
Picture.resize()仅适用于JPEG和PNG。其他格式尚不受支持。

从工作簿读取图像:

st lst = workbook.getAllPictures();
r (Iterator it = lst.iterator(); it.hasNext(); ) {
  PictureData pict = (PictureData)it.next();
  String ext = pict.suggestFileExtension();
  byte[] data = pict.getData();
  if (ext.equals("jpeg")){
    try (OutputStream out = new FileOutputStream("pict.jpg")) {
      out.write(data);
    }
  }

命名区域和命名单元格

命名区域是按名称引用一组单元格的方法。命名单元格是命名范围的退化情况,因为“单元格组”只包含一个单元格。您可以按工作簿中单元格的命名区域来创建和引用单元格。使用命名范围时,将使用org.apache.poi.ss.util.CellReference和org.apache.poi.ss.util.AreaReference类。

注意:使用“A1:B1”这样的相对值可能会导致名称在Microsoft Excel中处理工作簿时指向的单元格意外移动,通常使用“$A$1:$B$1”这样的绝对引用可以避免这种情况,请参阅本讨论。

创建命名区域/命名单元格

// 设置代码
String sname = "TestSheet", cname = "TestName", cvalue = "TestVal";
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet(sname);
sheet.createRow(0).createCell(0).setCellValue(cvalue);
// 1. 使用areareference为单个单元格创建命名区域
Name namedCell = wb.createName();
namedCell.setNameName(cname + "1");
String reference = sname+"!$A$1:$A$1"; // area reference
namedCell.setRefersToFormula(reference);
// 2. 使用cellreference为单个单元格创建命名区域
Name namedCel2 = wb.createName();
namedCel2.setNameName(cname + "2");
reference = sname+"!$A$1"; // cell reference
namedCel2.setRefersToFormula(reference);
// 3. 使用AreaReference为区域创建命名范围
Name namedCel3 = wb.createName();
namedCel3.setNameName(cname + "3");
reference = sname+"!$A$1:$C$5"; // area reference
namedCel3.setRefersToFormula(reference);
// 4. 创建命名公式
Name namedCel4 = wb.createName();
namedCel4.setNameName("my_sum");
namedCel4.setRefersToFormula("SUM(" + sname + "!$I$2:$I$6)");

从命名区域/命名单元格读取

// setup code
String cname = "TestName";
Workbook wb = getMyWorkbook(); // retrieve workbook
// 检索命名范围
int namedCellIdx = wb.getNameIndex(cellName);
Name aNamedCell = wb.getNameAt(namedCellIdx);
// 在指定范围检索单元格并测试其内容
AreaReference aref = new AreaReference(aNamedCell.getRefersToFormula());
CellReference[] crefs = aref.getAllReferencedCells();
for (int i=0; i<crefs.length; i++) {
    Sheet s = wb.getSheet(crefs[i].getSheetName());
    Row r = sheet.getRow(crefs[i].getRow());
    Cell c = r.getCell(crefs[i].getCol());
    // 根据单元格类型等提取单元格内容物。
}

从非连续命名区域读取

// Setup code
String cname = "TestName";
Workbook wb = getMyWorkbook(); // retrieve workbook
//检索命名范围
// Will be something like "$C$10,$D$12:$D$14";
int namedCellIdx = wb.getNameIndex(cellName);
Name aNamedCell = wb.getNameAt(namedCellIdx);
// 检索命名范围检索命名范围内的单元格并测试其内容
//将返回一个C10的区域引用,并且
// 另一个用于D12到D14
AreaReference[] arefs = AreaReference.generateContiguous(aNamedCell.getRefersToFormula());
for (int i=0; i<arefs.length; i++) {
    // Only get the corners of the Area
    // (use arefs[i].getAllReferencedCells() to get all cells)
    CellReference[] crefs = arefs[i].getCells();
    for (int j=0; j<crefs.length; j++) {
        // Check it turns into real stuff
        Sheet s = wb.getSheet(crefs[j].getSheetName());
        Row r = s.getRow(crefs[j].getRow());
        Cell c = r.getCell(crefs[j].getCol());
        // Do something with this corner cell
    }
}

注意,删除单元格时,Excel不会删除附加的命名区域。因此,工作簿可以包含指向不再存在的单元格的命名范围。在构造AreaReference之前,应该检查引用的有效性

if(name.isDeleted()){
  //named range points to a deleted cell. 
} else {
  AreaReference ref = new AreaReference(name.getRefersToFormula());
}

单元格注释-HSSF和XSSF

注释是一个富文本注释,附加到单元格并与单元格关联,与其他单元格内容分开。注释内容与单元格分开存储,并显示在与单元格分开但与单元格关联的图形对象(如文本框)中

Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
CreationHelper factory = wb.getCreationHelper();
Sheet sheet = wb.createSheet();
Row row   = sheet.createRow(3);
Cell cell = row.createCell(5);
cell.setCellValue("F4");
Drawing drawing = sheet.createDrawingPatriarch();
//当注释框可见时,将其显示在1x3的空间中
ClientAnchor anchor = factory.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex()+1);
anchor.setRow1(row.getRowNum());
anchor.setRow2(row.getRowNum()+3);
// 创建注释并设置文本+作者
Comment comment = drawing.createCellComment(anchor);
RichTextString str = factory.createRichTextString("Hello, World!");
comment.setString(str);
comment.setAuthor("Apache POI");
// 将注释分配给单元格
cell.setCellComment(comment);
String fname = "comment-xssf.xls";
if(wb instanceof XSSFWorkbook) fname += "x";
try (OutputStream out = new FileOutputStream(fname)) {
    wb.write(out);
}
wb.close();

读取单元格注释

Cell cell = sheet.get(3).getColumn(1);
Comment comment = cell.getCellComment();
if (comment != null) {
  RichTextString str = comment.getString();
  String author = comment.getAuthor();
}
//  或者,可以通过(行、列)检索单元格注释
comment = sheet.getCellComment(3, 1);

要获取工作表上的所有评论:

Map<CellAddress, Comment> comments = sheet.getCellComments();
Comment commentA1 = comments.get(new CellAddress(0, 0));
Comment commentB1 = comments.get(new CellAddress(0, 1));
for (Entry<CellAddress, ? extends Comment> e : comments.entrySet()) {
  CellAddress loc = e.getKey();
  Comment comment = e.getValue();
  System.out.println("Comment at " + loc + ": " +
      "[" + comment.getAuthor() + "] " + comment.getString().getString());
}

调整列宽以适应内容

Sheet sheet = workbook.getSheetAt(0);
sheet.autoSizeColumn(0); //调整第一列的宽度
sheet.autoSizeColumn(1); //调整第二列的宽度

仅限SXSFWorkbooks,由于“随机访问”窗口可能会排除工作表中计算列的最佳宽度所需的大多数行,因此在刷新任何行之前,必须跟踪这些列以自动调整大小。

SXSSFWorkbook workbook = new SXSSFWorkbook();
SXSSFSheet sheet = workbook.createSheet();
sheet.trackColumnForAutoSizing(0);
sheet.trackColumnForAutoSizing(1);
// 如果有列索引的集合,请参见SXSSFSheet#trackColumnForAutoSizing(Collection<Integer>)
// 或者你自己循环遍历for-loop.
// 或者, 使用 SXSSFSheet#trackAllColumnsForAutoSizing()如果将自动调整大小的列没有
//预先知道或正在升级现有代码并试图最小化更改。记住
//跟踪所有列将需要更多的内存和CPU周期,因为计算出了最适合的宽度
//在刷新的每一行的所有跟踪列上。

//创建一些单元格
for (int r=0; r < 10; r++) {
    Row row = sheet.createRow(r);
    for (int c; c < 10; c++) {
        Cell cell = row.createCell(c);
        cell.setCellValue("Cell " + c.getAddress().formatAsString());
    }
}
// 自动调整列大小。
sheet.autoSizeColumn(0);
sheet.autoSizeColumn(1);

注意,Sheet#autoSizeColumn()不计算公式单元格,公式单元格的宽度是根据缓存的公式结果计算的。如果工作簿中有许多公式,则在自动调整大小之前对它们进行评估是一个好主意。

警告
要计算列宽Sheet.autoSizeColumn使用Java2D类,这些类在图形环境不可用时引发异常。如果图形环境不可用,则必须告诉Java您正在headless mode下运行,并设置以下系统属性:Java.awt.headless=true。您还应该确保您在工作簿中使用的字体对Java可用。

如何读取超链接

Sheet sheet = workbook.getSheetAt(0);
Cell cell = sheet.getRow(0).getCell(0);
Hyperlink link = cell.getHyperlink();
if(link != null){
    System.out.println(link.getAddress());
}

如何创建超链接

Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
CreationHelper createHelper = wb.getCreationHelper();
//cell style for hyperlinks
//by default hyperlinks are blue and underlined
CellStyle hlink_style = wb.createCellStyle();
Font hlink_font = wb.createFont();
hlink_font.setUnderline(Font.U_SINGLE);
hlink_font.setColor(IndexedColors.BLUE.getIndex());
hlink_style.setFont(hlink_font);
Cell cell;
Sheet sheet = wb.createSheet("Hyperlinks");
//URL
cell = sheet.createRow(0).createCell(0);
cell.setCellValue("URL Link");
Hyperlink link = createHelper.createHyperlink(Hyperlink.LINK_URL);
link.setAddress("http://poi.apache.org/");
cell.setHyperlink(link);
cell.setCellStyle(hlink_style);
//link to a file in the current directory
cell = sheet.createRow(1).createCell(0);
cell.setCellValue("File Link");
link = createHelper.createHyperlink(Hyperlink.LINK_FILE);
link.setAddress("link1.xls");
cell.setHyperlink(link);
cell.setCellStyle(hlink_style);
//e-mail link
cell = sheet.createRow(2).createCell(0);
cell.setCellValue("Email Link");
link = createHelper.createHyperlink(Hyperlink.LINK_EMAIL);
//note, if subject contains white spaces, make sure they are url-encoded
link.setAddress("mailto:poi@apache.org?subject=Hyperlinks");
cell.setHyperlink(link);
cell.setCellStyle(hlink_style);
//link to a place in this workbook
//create a target sheet and cell
Sheet sheet2 = wb.createSheet("Target Sheet");
sheet2.createRow(0).createCell(0).setCellValue("Target Cell");
cell = sheet.createRow(3).createCell(0);
cell.setCellValue("Worksheet Link");
Hyperlink link2 = createHelper.createHyperlink(Hyperlink.LINK_DOCUMENT);
link2.setAddress("'Target Sheet'!A1");
cell.setHyperlink(link2);
cell.setCellStyle(hlink_style);
try (OutputStream out = new FileOutputStream("hyperinks.xlsx")) {
    wb.write(out);
}
wb.close();

数据验证

从3.8版开始,POI使用稍微不同的语法处理.xls和.xlsx格式的数据验证。

hssf.usermodel(binary.xls格式)

根据一个或多个预定义值检查用户输入单元格的值。

下面的代码将用户可以在单元格A1中输入的值限制为三个整数值(10、20或30)中的一个。

HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Data Validation");
CellRangeAddressList addressList = new CellRangeAddressList(
  0, 0, 0, 0);
DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(
  new String[]{"10", "20", "30"});
DataValidation dataValidation = new HSSFDataValidation
  (addressList, dvConstraint);
dataValidation.setSuppressDropDownArrow(true);
sheet.addValidationData(dataValidation);

下拉列表:

此代码将执行相同的操作,但会为用户提供一个下拉列表以从中选择值。

HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Data Validation");
CellRangeAddressList addressList = new CellRangeAddressList(
  0, 0, 0, 0);
DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(
  new String[]{"10", "20", "30"});
DataValidation dataValidation = new HSSFDataValidation
  (addressList, dvConstraint);
dataValidation.setSuppressDropDownArrow(false);
sheet.addValidationData(dataValidation);

出错时的消息:

创建一个消息框,如果用户输入的值无效,该消息框将显示给用户。

dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
dataValidation.createErrorBox("Box Title", "Message Text");

将“框标题”替换为要在消息框标题栏中显示的文本,将“消息文本”替换为错误消息的文本。

提示:

创建当包含数据验证的单元格收到焦点时用户将看到的提示

dataValidation.createPromptBox("Title", "Message Text");
dataValidation.setShowPromptBox(true);

传递给createPromptBox()方法的第一个参数中封装的文本将更大胆,并显示为提示的标题,而第二个参数将显示为消息的文本。可以传递createExplicitListConstraint()方法和包含整数、浮点、日期或文本值的字符串数组。

进一步的数据验证:

要获取检查输入值的验证,例如10到100之间的整数,请使用DVConstraint.createNumericConstraint(int,int,String,String)工厂方法。

dvConstraint = DVConstraint.createNumericConstraint(
  DVConstraint.ValidationType.INTEGER,
  DVConstraint.OperatorType.BETWEEN, "10", "100");

查看javadoc中的其他验证和运算符类型;还要注意,并不是此方法支持所有验证类型。传递给两个字符串参数的值可以是公式;“=”符号用于表示公式

dvConstraint = DVConstraint.createNumericConstraint(
  DVConstraint.ValidationType.INTEGER,
  DVConstraint.OperatorType.BETWEEN, "=SUM(A1:A3)", "100");

如果调用createNumericConstraint()方法,则无法创建下拉列表,只会忽略setUppressDropDownArrow(false)方法调用。

可以通过调用createDateConstraint(int,String,String,String)或createTimeConstraint(int,String,String)来创建日期和时间约束。两者都与上述非常相似,并在javadoc中进行了解释。

从电子表格单元格创建数据验证。

特定单元格的内容可用于提供数据验证的值,而DVConstraint.createFormulaListConstraint(String)方法支持此功能。要指定值来自连续单元格区域,请执行以下任一操作:

dvConstraint = DVConstraint.createFormulaListConstraint("$A$1:$A$3");

或者

Name namedRange = workbook.createName();
namedRange.setNameName("list1");
namedRange.setRefersToFormula("$A$1:$A$3");
dvConstraint = DVConstraint.createFormulaListConstraint("list1");

在这两种情况下,用户都可以从包含单元格A1、A2和A3的值的下拉列表中进行选择。

数据不必作为数据验证。但是,若要从其他工作表中选择数据,必须在创建工作表时为该工作表指定一个名称,并且应在公式中使用该名称。假设存在一张名为“数据表”的表格,这将起作用:

Name namedRange = workbook.createName();
namedRange.setNameName("list1");
namedRange.setRefersToFormula("'Data Sheet'!$A$1:$A$3");
dvConstraint = DVConstraint.createFormulaListConstraint("list1");

这也将:

dvConstraint = DVConstraint.createFormulaListConstraint("'Data Sheet'!$A$1:$A$3");

但这不会:

Name namedRange = workbook.createName();
namedRange.setNameName("list1");
namedRange.setRefersToFormula("'Sheet1'!$A$1:$A$3");
dvConstraint = DVConstraint.createFormulaListConstraint("list1");

这也不会:

dvConstraint = DVConstraint.createFormulaListConstraint("'Sheet1'!$A$1:$A$3");

标题:POI开发指南2
作者:MingGH
地址:https://runnable.run/articles/2020/01/14/1578969996361.html