我正在开发软件,我可以在其中获得带有列标题和一组值的动态列列表。
为了从此表生成报告,我使用 Jasper Reports 库(版本 6.19.1)并使用交叉表。问题是在交叉表中我没有找到动态更改列宽度的方法。它们都具有相同的固定大小 60px,当列的标题或其值很大时,我想更改大小以使用户更满意。
有什么办法可以做到这一点吗?
我尝试在 Java 中计算宽度并发送一个已经包含此动态宽度值作为字段的对象。所以我将它转换为一个变量并尝试将其放入宽度中,但它并不认为此操作有效。我想它只接受恒定宽度值。我得到的是这样的:
net.sf.jasperreports.engine.JRException: org.xml.sax.SAXParseException; lineNumber: 220; columnNumber: 57; cvc-datatype-valid.1.2.1: '$V{cellWidth}' is not a valid value for 'NMTOKEN'.
在将report.jrxml编译为report.jasper之前,我能够使用Java和额外的类解决问题。
就我而言,我需要动态调整和扩展报告的宽度以占据整个可用空间。
但是,这只适用于列的宽度不超过报告的总宽度的情况。
解决这个问题:
*使用 JasperSoft Design 创建报告,包括交叉表、 并将适当的键分配给您想要动态化的这些元素。
*创建自定义类实现以允许设置动态宽度
*设置交叉表内单元格和文本字段的宽度。 计算宽度基于交叉表中的 n 列除以报表的可用大小。 (n 列/可用宽度)= 动态宽度
这不是一个完美的解决方案,但它对我有用!
自定义类实现
public class JRDesignCellContentsCustom extends JRDesignCellContents implements JRCellContents {
public JRDesignCellContentsCustom() {
super();
}
public JRDesignCellContentsCustom(JRDesignCellContents jrDesignCellContents, List<JRDesignElement> listaJRDesignElement) {
super();
//Copy values...
setWidth(jrDesignCellContents.getWidth()); //<- Now you can access the protected method and set the width wathever you want
setBackcolor(jrDesignCellContents.getBackcolor());
setHeight(jrDesignCellContents.getHeight());
setStyle(jrDesignCellContents.getStyle());
setMode(jrDesignCellContents.getModeValue());
setStyleNameReference(jrDesignCellContents.getStyleNameReference());
setOrigin(jrDesignCellContents.getOrigin());
setElementGroup(jrDesignCellContents.getElementGroup());
/*
* This is to add the child elements that are inside your cell, which you obtained using keys.
* JRDesignTextField, JRDesignStaticText... etc
* */
listaJRDesignElement.forEach(this::addElement);
}
// You need to override setWidth to change protected to public and set the width from the custom class
// Wherever you want
@Override
public void setWidth(int width) {
super.setWidth(width);
}
}
然后编译并导出代码:
public ByteArrayOutputStream reportWithDynamicColumnSizeByParameter(Map<String, Object> params, Connection con) throws IOException, JRException {
params.put("nColumns", 4);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
String srcFileName = "detalle_calificacion";
ClassPathResource resource = new ClassPathResource(REPORT_FOLDER + File.separator + srcFileName + JRXML);
InputStream reportStream = resource.getInputStream();
try {
JasperDesign jasperDesign = JRXmlLoader.load(reportStream);
//You need to set the element key on JasperSoft Design to find the crosstab.
//In this case, I set crosstab in the first detail band with the element key "crosstab".
JRElement jrElement = jasperDesign.getDetailSection().getBands()[0].getElementByKey("crosstab");
//Then verify if it's a crosstab
if (jrElement instanceof JRDesignCrosstab jrDesignCrosstab) {
/*===Dynamic Column Group===*/
JRDesignCrosstabColumnGroup columnGroupActividad = (JRDesignCrosstabColumnGroup) jrDesignCrosstab.getColumnGroups()[0];
int nColumns = (int) params.get("nColumns");
params.remove("nColumns"); //Not pass params to report
final int availableWidth = 442; //<-- This is the width of my report, you can change whatever you want
//as long as it doesn't exceed the width of your report
// + 1 is for total column group
final int dynamicCellWidth = availableWidth / (nColumns + 1);
/*
* ====================== SECTION COLUMN GROUP HEADER =============================
* */
System.out.println("=== COLUMN GROUP : " + columnGroupActividad.getName() + " ===");
JRDesignCellContents header = (JRDesignCellContents) columnGroupActividad.getHeader();
JRDesignTextField activityName = (JRDesignTextField) header.getElementByKey("nombre_actividad");
JRDesignTextField percentage = (JRDesignTextField) header.getElementByKey("porcentaje");
activityName.setWidth(dynamicCellWidth);
percentage.setWidth(dynamicCellWidth);
System.out.println("=== EXPECTED CHANGES ===");
System.out.println("ACTIVITY NAME(width: " + activityName.getWidth() + ", height: " + activityName.getHeight() + ")");
System.out.println("PERCENTAGE(width: " + percentage.getWidth() + ", height: " + percentage.getHeight() + ")");
/*
* Unfortunately, you cannot set the width of the crosstab cells directly because it is a protected method,
* and you need to inherit a CustomClass from JRDesignCellContents to access those methods.
*
* @see JRDesignCellContentsCustom for more information.
* */
JRDesignCellContentsCustom customHeader = new JRDesignCellContentsCustom((JRDesignCellContents) header.clone(), List.of(activityName, percentage));
//It is assigned to the ColumnGroup with the new width to work (This must be the same as the JDesignTextField.getWidth())
customHeader.setWidth(dynamicCellWidth);
//Set to the column group the custom header to the crosstab
columnGroupActividad.setHeader(customHeader);
//We obtain the cells of the crosstab, in position 0. There will always be the Measure Cell
List<JRCrosstabCell> measureCells = ((JRDesignCrosstab) jrElement).getCellsList();
((JRDesignCrosstabCell) measureCells.get(0)).setWidth(dynamicCellWidth);
//and the same for the TextField inside the measure cells
JRDesignTextField score = (JRDesignTextField) jrDesignCrosstab.getElementByKey("calificacion");
score.setWidth(dynamicCellWidth);
System.out.println("=== EXPECTED CHANGES ===");
System.out.println("SCORE(width: " + score.getWidth() + ", height: " + score.getHeight() + ")");
/*
* ====================== SECTION COLUMN GROUP TOTAL =============================
* */
JRDesignCellContents totalHeader = (JRDesignCellContents) columnGroupActividad.getTotalHeader();
System.out.println("=== COLUMN GROUP TOTAL: " + columnGroupActividad.getName() + " ===");
//Repeat the process...
JRDesignStaticText labelFinalScore = (JRDesignStaticText) totalHeader.getElementByKey("label_nota_final");
labelFinalScore.setWidth(dynamicCellWidth);
JRDesignCellContentsCustom customTotalHeader = new JRDesignCellContentsCustom((JRDesignCellContents) totalHeader.clone(), List.of(labelFinalScore));
customTotalHeader.setWidth(dynamicCellWidth);
columnGroupActividad.setTotalHeader(customTotalHeader);
//the measure cell in position 1 is total...
((JRDesignCrosstabCell) measureCells.get(1)).setWidth(availableWidth);
JRDesignTextField finalScore = (JRDesignTextField) jrDesignCrosstab.getElementByKey("calificacion_final");
finalScore.setWidth(dynamicCellWidth);
}
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, con);
JasperExportManager.exportReportToPdfStream(jasperPrint, stream);
} finally {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return stream;
}