3

I have a custom, abstract class 'Panel' which extends JPanel. There aren't many differences with the two when painting. I have a Panel and I'm simulating an animation by updating the x value of an image. I have two animations right now, one that properly repaints and another than does not. This is for the one that does not. The one that works will be labelled A, the one that doesn't will be B.

A and B follow the same format. Update some variable on the Panel, calls update (a method in Panel which calls PaintComponent) and then calls repaint. It calls repaint after because this issue was with A before and was solved that way.

A: Updates an image variable. B: Updates the x variable of an image.

The Problem: The repaint doesn't clear the old image location and so it's a choppy mess across the screen.

What I've tried:

  • I've seen the super.PaintComponent(g) mentioned a lot, but this hasn't solved the problem.
  • I've tried changing the order for when the repaint/update methods are called.
  • Repaint does not update the Panel at all. (Probably because the painting is done in PaintComponent)

Any help would be appreciated.

Code:

Panel:

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());
}

Animation Methods (mg is the panel in question):

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();
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
aoi
  • 167
  • 1
  • 2
  • 11
  • Maybe double buffering can help - check out [Painting in AWT and Swing](http://java.sun.com/products/jfc/tsc/articles/painting/) – Nate W. Sep 12 '11 at 17:00
  • 3
    Why are you calling paintComponent() directly? I've never seen this done in Swing graphics program. Your whole design seems,... unusual. Are you following a tutorial of some kind? – Hovercraft Full Of Eels Sep 12 '11 at 17:02
  • And why are you creating your off_screen image from within paintComponent? that defeats the purpose of this being off screen as it slows down your painting needlessly? – Hovercraft Full Of Eels Sep 12 '11 at 17:03
  • Also, are you taking care to do your animations in a background thread off of the EDT? – Hovercraft Full Of Eels Sep 12 '11 at 17:09
  • @Hovercraft: I am not following a tutorial of any kind. Also, all of the drawing is being put onto the buffer in the drawoff method. (buffer.drawImage()). – aoi Sep 12 '11 at 17:15
  • @Shakedown: I am using doublebuffering. – aoi Sep 12 '11 at 17:15
  • @aoi: I suggest that you start reading the Swing Graphics tutorials. Again, your whole design smells bad, and it will likely need a full re-write. – Hovercraft Full Of Eels Sep 12 '11 at 17:18
  • see this link and also check stack oerflow question.... http://www.coderanch.com/t/345317/GUI/java/clear-JPanel-before-repainting http://stackoverflow.com/questions/5271495/java-graphics-repaint-problem http://stackoverflow.com/questions/5660704/java-swing-clearing-custom-painting-from-a-jpanel-overlayed-with-other-jpanels-i http://stackoverflow.com/questions/6902771/jpanel-graphics-clearing-and-repainting – Samir Mangroliya Sep 12 '11 at 17:09

1 Answers1

3

I think your design is way off and that is why things are not working. I'm not quite sure how your non-abstract JPanels work, but consider making your parent JPanel something more along these lines:

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();
   }
}

Also and again,

  • Do all long processing methods off of the EDT (Event Dispatch Thread).
  • Never call Thread.sleep(...) on the EDT.
  • Consider using Swing Timers instead of using Thread.sleep for the animations.
  • It's OK to call repaint on your JPanel off of the EDT, but for the most part that's about it.
  • All other Swing methods should be called on the EDT.
  • Read, re-read, and study the 2D and Swing graphics tutorials.
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • +1 i miss there using `javax.swing.Timer` istead of `Thread.sleep(int)` two craziest examples here http://stackoverflow.com/questions/7206122/swing-rate-limiting – mKorbel Sep 12 '11 at 18:55
  • 1
    I used Swing Timers and that fixed the problem. It was because the repaint method wasn't being called/doing what it was supposed to. Probably because of that EDT you mentioned. But thank you very much. :) – aoi Sep 12 '11 at 19:24