我试图阻止EDT重绘动画。我做的第一件事是将实际的绘图任务排除在另一个线程中,写入一个VolatileImage,它在我对应的JPanel的paintComponent方法中由EDT重绘。
如果我将重绘排除到另一个线程,这可以正常工作。不过,我确实在我的动画上方放置了几个其他面板。 结果,调用了我的画家(动画)面板的重绘方法,导致其他人也重新绘制而没有闪烁。
因此,重绘其他面板,在画家上调用重绘会导致闪烁。重新绘制单个面板会导致不透明的重绘,很少闪烁。
有人知道,如何将自己的jpanel重绘同步到我已经可用的缓冲图像中。我说重新启动触发EDT导致闪烁,因为它没有同步。
我重新调用动画
@Override
public void KeyframeChanged(Keyframe frame) {
if (painter.isVisible()) {
map.getMainMap().doPaintComponent(painter.getBuffer().getGraphics());
painter.renderAnimation();
painter.updateScreen();
}
}
画家方法:
public void updateScreen() {
Graphics g = this.getGraphics();
if (g != null) // component already visible?
{
// is there a backBuffer to draw?
if (backBuffer != null) {
g.drawImage(backBuffer, 0, 0, null);
} else {
// if not, create one and render on it
createBackBuffer();
renderAnimation();
}
}
}
public void renderAnimation() {
// Do drawing stuff here
}
@Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
}// end of paint
由于
感谢您的回答和链接。我仍然需要阅读其中的一些内容。然而,为了说明当前的行为,这个小型SSCCE应该有所帮助。
package repaintexample;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.VolatileImage;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
class Painter extends JPanel {
private VolatileImage backBuffer;
private Graphics2D g2d;
public Painter() {
setDoubleBuffered(false);
setOpaque(false);
}
@Override
public boolean isOptimizedDrawingEnabled() {
return false;
}
private void createBackBuffer() {
backBuffer = this.getGraphicsConfiguration().createCompatibleVolatileImage(1920, 1200);
}
public void adjustBackBufferSize() {
if (backBuffer != null) {
if (getWidth() > backBuffer.getWidth() || getHeight() > backBuffer.getHeight()) {
createBackBuffer();
}
}
}
public void updateScreen(Graphics g) {
if (g != null) // component already visible?
{
// is there a backBuffer to draw?
if (backBuffer != null) {
g.drawImage(backBuffer, 0, 0, null);
} else {
// if not, create one and render on it
createBackBuffer();
}
}
}
public void renderAnimation(int i, int j) {
if (backBuffer == null) {
createBackBuffer();
}
do {
if (backBuffer.validate(getGraphicsConfiguration()) == VolatileImage.IMAGE_INCOMPATIBLE) {
createBackBuffer();
}
g2d = (Graphics2D) backBuffer.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.red);
g2d.fillOval(i, j, 50, 50);
} while (backBuffer.contentsLost());
}
@Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
updateScreen(g);
}// end of paint
public VolatileImage getBuffer() {
return backBuffer;
}
}
class ContainerFrame extends JFrame {
private Painter mapPainter;
private JPanel informationPanel; // covers a lot of metainformation - Actually own JTable instance updating the same objects for repainting in each circle
private JPanel controller; // Maptools
private JPanel tabbedPabe; // Change redraw content
public ContainerFrame() {
this.setSize(1600, 1024);
this.setVisible(true);
initComponents();
initPositions();
Thread animation = new Thread(new Runnable() {
@Override
public void run() {
// My application is a mapping application, in which i first draw the tiles, before goin on with the "real" animated stuff
// clearing backbuffer content with g.fillRect(0, 0, getWidth(), getHeight());
while (true) {
for (int i = 0; i < mapPainter.getWidth(); i += 100) {
for (int j = 0; j < mapPainter.getHeight(); j += 5) {
mapPainter.renderAnimation(i, j);
int repaintCase = 2;
switch (repaintCase) {
case 0:
// Default case redrawing via EDT, triggering the others in proper order
mapPainter.repaint();
break;
case 1:
// case repainting by current Thread - necessity of repainting above positioned panels
// results in flickering, since not synchronized
mapPainter.updateScreen(mapPainter.getGraphics());
informationPanel.repaint();
controller.repaint();
tabbedPabe.repaint();
break;
case 2:
// painting components on buffer
// Results in rarely flickering and opague repaint
// is there any common way, to manually repaint onto anything - like image
informationPanel.paintAll(mapPainter.getBuffer().getGraphics());
controller.paintAll(mapPainter.getBuffer().getGraphics());
tabbedPabe.paintAll(mapPainter.getBuffer().getGraphics());
mapPainter.updateScreen(mapPainter.getGraphics());
break;
}
}
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
animation.start();
}
private void initComponents() {
mapPainter = new Painter();
mapPainter.setSize(this.getSize());
informationPanel = new JPanel();
informationPanel.setSize(new Dimension(360, 800));
controller = new JPanel();
controller.setSize(new Dimension(500, 250));
tabbedPabe = new JPanel();
tabbedPabe.setSize(new Dimension(300, 300));
this.getLayeredPane().add(mapPainter, JLayeredPane.DEFAULT_LAYER);
this.getLayeredPane().add(controller, JLayeredPane.MODAL_LAYER);
this.getLayeredPane().add(tabbedPabe, JLayeredPane.MODAL_LAYER);
this.getLayeredPane().add(informationPanel, JLayeredPane.MODAL_LAYER);
}
private void initPositions() {
controller.setLocation(mapPainter.getWidth() - controller.getWidth(), mapPainter.getHeight() - controller.getHeight());
tabbedPabe.setLocation(this.getWidth() - tabbedPabe.getWidth(), mapPainter.getHeight() - controller.getHeight() - tabbedPabe.getHeight() - 400);
informationPanel.setLocation(10, mapPainter.getHeight() - informationPanel.getHeight() - 200);
}
}
public class RepaintExample {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ContainerFrame f = new ContainerFrame();
}
}
我现在使用案例0确实看到有大或非常差的fps - 30或大约6.我不确定,这怎么可能,我可以找到某事。在已发布的链接中。我认为确保最多可以减轻EDT,可以成为一个合适的解决方案。
此外,我所示的3个面板的内容不需要以与动画相同的频率重新绘制。不幸的是,我还没有找到一种防止重绘的正确方法。我已经使用了很长一段时间的唯一方法是在一个叫做“动画”的那些区域的invokelater调用中的痛苦。常见的重绘(Rectangle rec)一直没有用,因为单个调用已经汇总为一个大的,覆盖的像素比我传入的更多。
public void drawCachedSprite(Graphics2D g, CachedSprites sprites, int zoom, double cog, double x, double y, double w, double h) {
try{
pos_x = x;
pos_y = y;
RenderingUtil.getRenderQuality();
transform.setToIdentity();
// Compute the corner, the drawing needs to start with
transform.translate(x - (w / 2.0), y - (h / 2.0));
g.drawImage(sprites.getSprite(DefaultResources.getType(), spriteColor, zoom, cog), transform, null);
} catch (IllegalArgumentException e) {
e.printStackTrace();
System.out.println("width or height not set properly");
}
}