我已经在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)定期重画框架的'updater'线程:

@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的变量命名约定,因为这是一个私人项目,所以我选择以大写字母开头的变量名,因为我认为它看起来更好,请不要对此发表评论。

最佳答案

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(),因为这会导致问题,它应仅在创建GraphicComponent.getGraphics()上完成,但在您的情况下,甚至不应该创建Graphic上下文,因为它已经具有被创建并传递给paintComponent方法。 (请参阅this类似问题)

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

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

如果我有一个SSCCE来说明不需要的行为,那将更有帮助。由于我可以尝试重现您的错误,但我极有可能无法做到(由于适用于您的应用程序的特定条件可能会导致这些视觉假象)

09-10 08:42
查看更多