我在 JScrollPane 中放置了一个 JPanel 对象,并且滚动按预期工作。通过覆盖 paintComponent() 我试图在 JPanel 对象中进行自定义绘画。但是,当 JPanel 对象放置在 JScrollPane 中时,JPanel 不再正确绘制(而是仅显示其背景颜色)。
因为我的应用程序要求 JPanel 不断更新,所以构建了一个单独的线程以在特定时间间隔重新绘制 JPanel。
以下代码摘录显示了我当前的项目:
a) 来 self 的 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),帧渲染正确但出现严重闪烁,它不断地在绘制背景颜色和绘制粉红色框之间交替。我已经尝试添加和删除布局管理器,禁用重绘管理器以及启用和禁用两个组件的“双缓冲”标志,所有这些都会导致上述两种行为之一(仅呈现背景或闪烁)。
谁能帮我解决这个问题?
注意:我很清楚 Java 的变量命名约定,因为这是一个私有(private)项目,我选择以大写字母开头的变量名,因为我认为它看起来更好,请不要对此发表评论。
最佳答案
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( )
但在您的情况下,您甚至不应该创建 Graphic
的上下文,因为它已经创建并传递给 paintComponent 方法。 (参见 this 类似问题)
2) repaint()
不需要 SwingUtilities.invokeXXX
block ,因为它是线程安全的。但特别是不需要 SwingUtilities.invokeAndWait
(因为这是一个阻塞调用并等待所有待处理的 AWT 事件得到处理并且 run() 方法完成)这不是很好并且也可能被添加到屏幕上您看到的视觉人工制品。
3) 我已经尝试添加和删除布局管理器,禁用重绘管理器以及启用和禁用两个组件的“双缓冲”标志,所有这些都会导致上述两种行为之一(仅渲染背景或闪烁)。 撤消所有操作,因为我看不出这对绘画有何影响。
如果我有 SSCCE 会更有帮助这说明了不需要的行为。因为我可以尝试重现您的错误,但我很可能无法(由于适用于您的应用的特定条件可能会导致这些视觉伪像)
关于java - JPanel inside JScrollPane 绘画问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17304401/