有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使JSVGScrollPane内的JSVGCanvas与SVGDocument大小匹配

我在图表应用程序中显示SVG图形时遇到问题。画布采用其父组件的大小,而不是它包含的文档。大于此大小的文档不会完全呈现

案例

例如,我有一个621x621像素的图形

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" contentScriptType="text/ecmascript" zoomAndPan="magnify" contentStyleType="text/css" preserveAspectRatio="xMaxYMax slice" version="1.0">
  <rect x="50" y="50" fill="white" width="20" height="20" stroke="black" stroke-width="1"/>
  <rect x="600" y="600" fill="blue" width="20" height="20" stroke="black" stroke-width="1"/>
</svg>

它没有viewBox,因为它是动态创建的。当我创建文档时,我没有大小

我有一个JSVGCanvas在一个JSVGScrollPane内,滚动窗格的父级比文档小,比如说它的500x500像素

预期行为

启动应用程序时,可以看到适合500x500的图形部分。底层文档更大,所以有滚动条,可以向左和向下滚动121px

当增加框架的大小时,图形中越来越多的部分变得可见。当您将其增加到大于620x620px时,滚动条可能会完全消失

如果将面板缩小,然后再次显示文档,滚动条将重新出现

遇到的行为

启动应用程序时,可以看到适合500x500的图形部分。没有可见的滚动条

当面板变大时,画布会增加(其背景色),但图形不会延伸到初始500x500px之外的部分

当你把面板缩小到400x400时,滚动条确实会出现,但只允许我滚动100px。因此画布仍然是最初的500x500,而不是期望的621x621

示例

遇到的行为可以通过Batik存储库上的^{}和我在上面添加的SVG文件进行复制。该示例使用固定大小的500x500px

如果在画布和滚动窗格中添加背景色,则整个框架为蓝色。因此,虽然画布的大小确实在增加,但它不会绘制超出其初始大小的任何内容

canvas.setBackground(Color.blue);
scroller.setBackground(Color.red);

我可以在调整面板大小后重置文档。这使得它可以重新绘制图形的可见部分,但滚动仍不能按预期工作,因为画布仍然只是可见部分的大小,而不是文档的大小。您可以通过将此侦听器添加到框架(或者我认为的任何其他面板)来测试这一点

class ResizeListener implements ComponentListener {

    JSVGCanvas canvas;
    URI url;

    public ResizeListener(JSVGCanvas c, URI u) {
        canvas = c;
        url = u;
    }

    @Override
    public void componentResized(ComponentEvent arg0) {
        canvas.setURI(url.toString());
        // Calling invalidate on canvas or scroller does not work
    }

    // ...
};

我已经读到我应该有一个viewBox,但是我的文档经常被重新创建(基于modelchange事件),当我创建文档时,我不知道如何获取viewBoxSVGDocument.getRootElement().getBBox();通常给我null。在没有提供viewBox的情况下,Batik代码中应该有回退,所以我希望这是可选的。另外,当我被平移到某个地方时,如果我更改文档,它应该保持这种平移

我希望我把问题说清楚了。当我期望JSVG类提供我想要的开箱即用的行为时,我是否对它们期望过高,或者我在这里遗漏了什么?有人能帮我找到解决办法吗


共 (1) 个答案

  1. # 1 楼答案

    我想我可能已经找到了解决办法。我需要设置文档的高度和宽度,而不是搞乱viewBox。设置了大小后,不需要查看框,滚动条可以按预期工作。如果我设置了viewbox,画布会不断缩放图像以使其适合

    现在,为了更新大小,每当我更改可能会影响大小的dom时,我都会使用它

    static BridgeContext ctx = new BridgeContext(new UserAgentAdapter());
    static GVTBuilder builder = new GVTBuilder();
    
    private static void calculateSize(SVGDocument doc) {
        GraphicsNode gvtRoot = builder.build(ctx, doc);
    
        Rectangle2D rect = gvtRoot.getSensitiveBounds();
    
        doc.getRootElement().setAttributeNS(null,
                SVGConstants.SVG_WIDTH_ATTRIBUTE, rect.getMaxX() + "");
        doc.getRootElement().setAttributeNS(null,
                SVGConstants.SVG_HEIGHT_ATTRIBUTE, rect.getMaxY() + "");
    }
    

    我在UpdateManager线程的之外执行此操作,在dom修改之后使用JSVGCanvas.setSVGDocument()。当我试图通过UpdateManager进行此操作时,它只更新了第一个更改。updatequeue的正确初始化可能可以解决这个问题,但由于我更改了很多文档,所以每次都可以重新开始

    我只剩下一个小问题。每当我设置新文档时,滚动位置都会重置。我尝试在操作之前进行转换,然后再重新应用,但这似乎不起作用

    编辑+: 我通过替换文档的根节点,而不是替换整个文档,成功地解决了这个小问题。画布设置为documentstate ALWAYS_DYNAMIC。文档中的更改会立即应用,包括根节点的新大小(如上所述设置)。但由于文档没有完全替换,所以滚动状态保持不变

    画布只有在完成文档呈现后才可以修改dom。我用SVGLoad监听器检测到了这一点

    class Canvas extends JSVGCanvas implements SVGLoadEventDispatcherListener {
    
        public Canvas() {
            super();
            setDocumentState(ALWAYS_DYNAMIC);
            addSVGLoadEventDispatcherListener(this);
        }
    
        /**
         * Indicatates whether the canvas has finished its first render, the
         * canvas is now ready for modification of the dom
         */
        private boolean isReadyForModification = false;
    
        /**
         * Renew the document by replacing the root node with the one of the new
         * document
         * 
         * @param doc The new document
         */
        public void renewDocument(final SVGDocument doc) {
    
            if (isReadyForModification) {
                getUpdateManager().getUpdateRunnableQueue().invokeLater(
                        new Runnable() {
                            @Override
                            public void run() {
                                // Get the root tags of the documents
                                Node oldRoot = getSVGDocument().getFirstChild();
                                Node newRoot = doc.getFirstChild();
    
                                // Make the new node suitable for the old
                                // document
                                newRoot = getSVGDocument().importNode(newRoot,
                                        true);
    
                                // Replace the nodes
                                getSVGDocument().replaceChild(newRoot, oldRoot);
                            }
                        });
            } else {
                setSVGDocument(doc);
            }
    
        }
    
        @Override
        public void svgLoadEventDispatchCompleted(SVGLoadEventDispatcherEvent e) {
            isReadyForModification = true;
        }
    
        // ...
    }