【问题标题】:Java Swing : drawLine very slowJava Swing:drawLine非常慢
【发布时间】:2012-10-14 04:15:19
【问题描述】:

我正在使用 swing 编写一个 java 应用程序,我需要在其中绘制一个正方形上方的网格。为此,我使用了Graphics 类提供的drawLine(...) 方法。

除了绘制每条线需要很长时间(50 条线超过 20 秒......)之外,一切都很好。我什至可以看到实时绘制的线条。一件奇怪的事情是水平线的绘制速度比垂直线快(几乎是瞬间)。

我可能做错了什么。这是网格的代码。

public void drawGrid(Graphics g){
    g.setColor(new Color(255, 255, 255, 20));
    int width = getWidth();
    int height = (int) (width * Utils.PLATE_RATIO);
    int step = pixelSize*gridSpacing;
    Color bright = new Color(255, 255, 255, 100);
    Color transparent = new Color(255, 255, 255, 20);
    for(int ix = insets.left + step;                        
            ix < width; ix += step){
        if(((ix - insets.left) / step) % 10 == 0){
            g.setColor(bright);
        }
        else{
            g.setColor(transparent);
        }
        g.drawLine(ix, insets.top, ix, height+insets.top);
    }
    for(int iy = insets.top+step;
            iy < (insets.top + height); iy += step){
        if(((iy - insets.top) / step) % 10 == 0){
            g.setColor(bright);
        }
        else{
            g.setColor(transparent);
        }
        g.drawLine(insets.left, iy, width + insets.left, iy);
    }
}

【问题讨论】:

  • 这是因为你没有使用双缓冲。见docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
  • 当然可以,我怎么这么笨。谢谢!
  • @SureshKumar swing 组件默认是双缓冲的...
  • @SureshKumar 同意 kleopatra 的观点——你不能仅仅通过这部分代码来判断他是否使用双缓冲绘画策略。并且提供的代码完全没问题。
  • 单缓冲永远不会花那么长时间。

标签: java swing graphics fullscreen doublebuffered


【解决方案1】:

您发布的代码很好,没有问题。
这是使用您的方法的组件的工作示例(有点简化):

public static class MyGrid extends JComponent
{
    private int step = 10;

    public MyGrid ()
    {
        super ();
    }

    public Dimension getPreferredSize ()
    {
        return new Dimension ( 500, 500 );
    }

    protected void paintComponent ( Graphics g )
    {
        super.paintComponent ( g );
        drawGrid ( g );
    }

    public void drawGrid ( Graphics g )
    {
        int width = getWidth ();
        int height = getHeight ();
        Color bright = new Color ( 255, 255, 255, 200 );
        Color transparent = new Color ( 255, 255, 255, 100 );

        for ( int ix = step; ix < width; ix += step )
        {
            if ( ( ix / step ) % 10 == 0 )
            {
                g.setColor ( bright );
            }
            else
            {
                g.setColor ( transparent );
            }
            g.drawLine ( ix, 0, ix, height );
        }

        for ( int iy = step; iy < height; iy += step )
        {
            if ( ( iy / step ) % 10 == 0 )
            {
                g.setColor ( bright );
            }
            else
            {
                g.setColor ( transparent );
            }
            g.drawLine ( 0, iy, width, iy );
        }
    }
}

我猜那段代码之外有一些问题。

附:有点跑题了,但是……

我建议您计算绘画区域的可见部分(使用 JComponent 的 getVisibleRect () 方法或 Graphics g.getClip ().getBounds () 方法)并将您的绘画限制在该区域。

如果组件真的很大(例如组件的面积为 10000x10000 像素),那么小的优化可以加快组件的绘制速度。

【讨论】:

  • 我认为代码之外没有任何问题,我做的和你差不多。我现在用getVisibleRect().getSize() 替换了getWidth()。请参阅下面的解决方案。
【解决方案2】:

按照@sureshKumar 的建议,这是我使用双缓冲解决问题的方法。我只是在屏幕外的图像上绘图,并在绘图结束时调用drawImage()。这似乎可以解决问题。

编辑:这似乎只有在您想从paintComponent(...) 方法之外调用您的绘画方法(在我的情况下为drawGrid())时才有用。

代码如下:

private Graphics bufferGraphics;
private Image offScreen;
private Dimension dim;
//other attributes not shown...

public CentralPanel(){
    //Some initialization... (not shown)

    //I added this listener so that the size of my rectangle
    //and of my grid changes with the frame size
    this.addComponentListener(new ComponentListener() {

        @Override
        public void componentResized(ComponentEvent e) {
            dim = getVisibleRect().getSize();
            offScreen = createImage(dim.width, dim.height);
            bufferGraphics = offScreen.getGraphics();
            repaint();
            revalidate();
        }
        //other methods of ComponentListener not shown
    });
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(offScreen, 0, 0, null);
}

public void drawGrid(){
    int width = dim.width - insets.left - insets.right;
    width -= (width % plateSize.getXSize());
    int height = (int) (width*Utils.PLATE_RATIO);
    height -= (height % plateSize.getYSize());
    int step = pixelSize*gridSpacing;
    Color bright = new Color(255, 255, 255, 100);
    Color transparent = new Color(255, 255, 255, 20);
    for(int ix = insets.left + step;                        
            ix < (width+insets.left); ix += step){
        if(((ix - insets.left) / step) % 10 == 0){
            bufferGraphics.setColor(bright);
        }
        else{
            bufferGraphics.setColor(transparent);
        }
        //I am now drawing on bufferGraphics instead 
        //of the component Graphics
        bufferGraphics.drawLine(ix, insets.top, ix, height+insets.top);
    }
    step *= Utils.PLATE_RATIO;
    for(int iy = insets.top+step;
            iy < (insets.top + height); iy += step){
        if(((iy - insets.top) / step) % 10 == 0){
            bufferGraphics.setColor(bright);
        }
        else{
            bufferGraphics.setColor(transparent);
        }
        bufferGraphics.drawLine(insets.left, iy, width + insets.left, iy);
    }
}

P.S.:如果这应该作为我的问题的编辑添加,请告诉我,我会做的。

【讨论】:

  • 天哪,你为什么不直接扩展一个已经有双缓冲策略的 JComponent(实际上每个 Swing 组件都有它并且默认使用它)?这将节省您的大量时间,而且您不需要发明轮子……扩展 JComponent 时您需要做的就是扩展paintComponent 方法,然后……是的,绘画!就这样。甚至无需担心双缓冲。
  • 还有一件事——当我提到使用getVisibleRect () 方法来避免无意义的绘画时,你没有明白我在说什么。我说的是排除在组件上不可见的线条(以及线条的那些部分)。例如,如果您的组件包含在活动滚动中 - 您一次只能看到它的一部分。如果可能,您应该避免在该可见部分之外进行绘画。在您的情况下,您可以轻松排除不可见的行。
  • @MikleGarin 我明白现在出了什么问题。我实际上是从另一个函数调用drawGrid(Graphics g),但我相信只有painComponent(Graphics g) 是自动双重处理的,而不是用JComponent 的图形绘制的所有内容。无论如何,如果我可以从外部paintComponent(...) 调用drawGrid(...) 方法,它会变得更容易,所以我将保留我的实现。感谢您对getVisibleRect() 的评论,我现在明白了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-05
  • 2016-03-26
  • 2015-05-22
  • 1970-01-01
  • 2017-12-12
  • 2015-06-14
  • 2013-05-14
相关资源
最近更新 更多