【问题标题】:JPanel inside JScrollPane Painting IssueJScrollPane 中的 JPanel 绘画问题
【发布时间】:2013-06-22 16:00:53
【问题描述】:

我在 JScrollPane 中放置了一个 JPanel 对象,并且滚动按预期工作。通过覆盖paintComponent(),我尝试在JPanel 对象中进行自定义绘画。但是,当 JPanel 对象放置在 JScrollPane 中时,JPanel 不再正确绘制(而是仅显示其背景颜色)。

因为我的应用程序需要不断更新 JPanel,所以构造了一个单独的线程以在特定时间间隔重新绘制 JPanel。

以下代码摘录显示了我当前的项目:

a) 我的 JPanel 中的paintComponent()(这个方法已经被缩减为只有绘画,实际的绘画将是一个不断更新的从另一个线程提供的 BufferedImage,而不是这个粉红色的大静态框):

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);

    //Render Frame
    // 'RXDisplayCanvas' is the JPanel.
    Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();

    G2D.setColor(Color.PINK);
    //800 and 600 are arbitrary values for this example, real values are calculated at runtime. The value calculation code is verified to work (as its used elsewhere in a similar scenario)
    G2D.fillRect(0, 0, 800, 600);
    G2D.dispose();
}

b) 定期重绘帧的“更新程序”线程:

@Override
public void run() {
    long MaxFrameTime;
    long Time;

    while(isVisible()){
        // 'FPSLimit' is a integer value (default to 30)
        MaxFrameTime = Math.round(1000000000.0 / FPSLimit);
        Time = System.nanoTime();

        try{
            SwingUtilities.invokeAndWait(new Runnable(){
                @Override
                public void run() {
                    // 'RXDisplayCanvas' is the JPanel.
                    RXDisplayCanvas.repaint(); //When using this, the JPanel does not display correctly.

                    //RXDisplayCanvas.paintImmediately(0, 0, RXDisplayCanvas.getWidth(), RXDisplayCanvas.getHeight()); When using this, the JPanel renders correctly but flickers.
                }
            });
        }catch(InterruptedException | InvocationTargetException e){}

        Time = System.nanoTime() - Time;
        if(Time < MaxFrameTime){
            try{
                Thread.sleep(Math.round((MaxFrameTime - Time)/1000000.0));
            }catch(InterruptedException ex){}
        }
    }
}

我已经考虑到 repaint() 不会导致立即重新绘制屏幕,​​但问题在于屏幕的不正确渲染。当程序被单独放置时,它仅呈现 JPanel 的背景颜色,直到 JScrollPane 滚动,它在下一次 repaint() 调用绘制不正确的显示之前正确呈现一帧。

当将 repaint() 切换为 paintImmediately() 时(在摘录 b 中),帧渲染正确,但出现严重闪烁,它在绘制背景颜色和绘制粉红色框之间不断交替。我已经尝试添加和删除布局管理器,禁用重绘管理器以及启用和禁用两个组件的“双缓冲”标志,所有这些都导致上述两种行为之一(仅渲染背景或闪烁)。

谁能帮助我解决这个问题?

N.B:我非常了解 Java 的变量命名约定,因为这是一个私人项目,我选择以大写字母开头的变量名,因为我认为它看起来更好,请不要发布有关此的 cmets。

【问题讨论】:

  • 如何同步对共享数据的访问?
  • 不再相关,但这是通过另一个线程将 BufferedImage 的光栅复制到活动显示缓冲区来完成的。显示器不断显示当前的 BufferedImage,同时另一个线程对其进行更新(全部使用同步块完成)。

标签: java swing jpanel jscrollpane paintcomponent


【解决方案1】:

1) 我不确定:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    // 'RXDisplayCanvas' is the JPanel.
    Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();
    ..
    G2D.dispose();
}

我建议这样做:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D G2D = (Graphics2D)g;
    G2D.setColor(Color.PINK);
    G2D.fillRect(0, 0, 800, 600);
}

注意我如何省略了getGraphics,并使用paintComponent 的图形上下文中传递的电流。

还请注意,我不调用g2d.dipose(),因为这会导致问题,它应该只在您创建Component.getGraphics()Graphics 上完成,但在您的情况下,您甚至不应该将Graphics 上下文创建为它已经被创建并传递给 paintComponent 方法。 (见this类似问题)

2) repaint() 不需要 SwingUtilities.invokeXXX 块,因为它是线程安全的。但尤其不需要SwingUtilities.invokeAndWait(因为这是一个阻塞调用,并等待所有待处理的 AWT 事件得到处理并 run() 方法完成),这并不好,也可能会添加到您看到的屏幕视觉伪影中。

3) 我尝试添加和删除布局管理器,禁用重绘管理器以及启用和禁用两个组件的“双缓冲”标志,其中所有组件都导致上述两种行为之一(仅呈现背景或闪烁)。撤消所有这些,因为我看不出这对绘画有何影响。

如果我有一个 SSCCE 来说明不受欢迎的行为,那将会更有帮助。因为我可以尝试重现您的错误,但我很可能无法重现(由于适用于您的应用的特定条件可能会导致这些视觉伪影)

【讨论】:

  • 天哪……我怎么会错过呢? Graphics2D G2D = (Graphics2D)g;而不是这个: (Graphics2D)RXDisplayCanvas.getGraphics();这确实解决了我的问题,非常感谢你注意到这个我 3 天没注意到的小错误......
  • @CPUTerminator 没问题,有时我们只需要第二双眼睛 :)
猜你喜欢
  • 2014-08-03
  • 2016-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
  • 2012-08-05
  • 1970-01-01
相关资源
最近更新 更多