JPanel重绘不清除问题

3
我有一个自定义的抽象类“Panel”,它继承了JPanel。在绘画方面两者并没有太多的区别。我有一个Panel,并通过更新图像的x值来模拟动画。目前我有两个动画,一个可以正常重绘,另一个不能。下面是关于那个不能的问题。
A和B都遵循同样的格式。更新Panel上的某些变量,调用update(Panel中的一个方法,该方法调用PaintComponent),然后调用repaint。之所以在更新后调用repaint,是因为这个问题之前也是在A上出现过,并且是通过这种方式解决的。
A:更新一个图像变量。 B:更新图像的x变量。
问题:重绘不清除旧图像位置,导致屏幕上出现了一堆混乱的东西。
我尝试过:
1.我经常看到提到super.PaintComponent(g),但这没有解决问题。
2.我尝试改变调用repaint/update方法的顺序。
3.repaint根本不更新Panel。(可能是因为绘制是在PaintComponent中完成的)
如果能获得任何帮助,将不胜感激。
代码:
public Panel (boolean visible){
    super();
    this.setLayout(new BorderLayout(640, 416));//sets the Layout type of the panel
    this.setOpaque(false);//Makes it so that the panel underneath can be seen where images aren't drawn
    this.setVisible(visible);
    ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    gs = ge.getDefaultScreenDevice();
    gc = gs.getDefaultConfiguration();
}

public void paintComponent (Graphics g){
    setUp();
    drawOff();
    setDown(g);
}

private void setUp(){
    off_screen = gc.createCompatibleImage(getSize().width, getSize().height, Transparency.TRANSLUCENT);
    buffer = off_screen.createGraphics();
}

protected abstract void drawOff();

private void setDown(Graphics g){
    g.drawImage(off_screen,0,0,this);
    off_screen.flush(); 
}

public void update(){
    paintComponent(this.getGraphics());
}

动画方法(mg是相关面板):

private void battleStart(User user) {
    for (int i = 0; i < user.battle.length; i++) {
        mg.battleStart(user.battleStart(i));
        mg.update();
        try {
            Thread.sleep(150);
        } catch (Exception e) {

        }
        mg.repaint();
    }
}

private void animateStart(User user){
    for (int i = 0; i < 10; i++){
        mg.x = mg.x + 10;
        mg.update();
        try {
            Thread.sleep(100);
        } catch (Exception e) {

        }
        mg.repaint();
    }
}

也许双缓冲可以帮助 - 可以查看AWT和Swing中的绘图 - Nate W.
3
你为什么直接调用paintComponent()方法?在Swing图形程序中,我从来没有看到过这样做。你的整个设计似乎有些不寻常。你是在跟随某种教程吗? - Hovercraft Full Of Eels
你为什么要在paintComponent中创建你的off_screen图像?这违背了它作为离屏幕的目的,会不必要地减慢你的绘画速度。 - Hovercraft Full Of Eels
@Hovercraft:我没有跟随任何教程。此外,所有的绘图都是在drawoff方法中放入缓冲区的(buffer.drawImage())。 - aoi
@aoi:我建议你开始阅读Swing Graphics教程。再次强调,你的整个设计都有问题,很可能需要完全重写。 - Hovercraft Full Of Eels
显示剩余3条评论
1个回答

3

我认为你的设计偏差太大,这就是为什么事情不能正常工作的原因。虽然我不确定你的非抽象JPanels是如何工作的,但建议将父JPanel更改为以下内容:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class MyPanel extends JPanel {
   private GraphicsEnvironment ge;
   private GraphicsDevice gs;
   private GraphicsConfiguration gc;
   private BufferedImage offScreen;

   public MyPanel(boolean visible) {
      super();
      this.setLayout(new BorderLayout(640, 416)); // strange constants for this layout.
      this.setOpaque(false);
      this.setVisible(visible);
      ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      gs = ge.getDefaultScreenDevice();
      gc = gs.getDefaultConfiguration();

      addComponentListener(new ComponentAdapter() {
         @Override
         public void componentResized(ComponentEvent e) {
            setUp();
         }
      });
   }

   @Override
   // don't make this public. Keep it protected like the super's
   // just draw in this method. Don't call other methods that create buffers
   // or draw to buffers.
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (offScreen != null) {
         g.drawImage(offScreen, 0, 0, null);
      }
   }

   private void setUp() {
      offScreen = gc.createCompatibleImage(getSize().width, getSize().height,
            Transparency.TRANSLUCENT);
   }

   // draw to the buffer outside of the paintComponent
   // and then call repaint() when done
   public void upDateOffScreen() {
      // ?? offScreen.flush(); // I've never used this before, 
                  // so am not sure if you need this here
      Graphics2D osGraphics = offScreen.createGraphics();
      // TODO: do drawing with osGraphics object here
      osGraphics.dispose();
      repaint();
   }
}

同样,且需要再次强调:

  • 所有长时间处理的方法都应该在EDT (Event Dispatch Thread)之外执行。
  • 不要在EDT上调用Thread.sleep(...)方法。
  • 考虑使用Swing定时器来代替使用Thread.sleep来进行动画操作。
  • 在JPanel上调用repaint方法可以不在EDT上执行,但大部分情况下只有这点例外。
  • 所有其他的Swing方法都应该在EDT上执行。
  • 阅读、反复阅读、学习2D和Swing图形教程。

+1 我想念在那里使用 javax.swing.Timer 而不是 Thread.sleep(int),这里有两个最疯狂的例子:https://dev59.com/G2Lbs4cB2Jgan1zn9O3a - mKorbel
1
我使用了Swing计时器,问题得到了修复。这是因为repaint方法没有被调用/没有执行它该有的功能。很可能是你提到的EDT导致的。但非常感谢您。 :) - aoi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接