交叉表中的动态列宽大小

问题描述 投票:0回答:1

我正在开发软件,我可以在其中获得带有列标题和一组值的动态列列表。

为了从此表生成报告,我使用 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'.

jasper-reports pivot-table
1个回答
0
投票

在将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;
    }
© www.soinside.com 2019 - 2024. All rights reserved.