Animating a Rectangle in Java

2019-01-20 16:35发布

问题:

I've been trying to get this rectangle to move that I've created using a for loop. All that's happening with this code is that there is an original rectangle and then a new one next to that rectangle. No animation happens, only those two rectangles show on the window. What are some methods to get this rectangle to animate?

import java.awt.*;
import javax.swing.*;

public class Gunman extends JComponent {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public int x = 10;
    public int y = 10;
    public int width = 8;
    public int height = 10;
    public void paint(Graphics g) {
        g.setColor(Color.red);
        g.drawRect (x, y, width, height);  
        g.fillRect (x, y, width, height);
        for(int i = 0; i<=1024; i++){
            g.setColor(Color.red);
            g.drawRect(x++, y, width, height);
            g.fillRect(x++, y, width, height);
        }
    }
}

回答1:

Don't have program logic in a paint or paintComponent method, and by logic, I mean the for loop with "motion" as that just won't work. You want to

  • Almost never draw in a JComponent's paint method but rather in its paintComponent method.
  • Don't forget to call the super.paintComponent(g) method too, often as the first method call in the paintComponent(g) override.
  • Use a Swing Timer to step wise change the x and y values
  • call repaint() on the JComponent after the changes are made

For example

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

public class Gunman extends JComponent {

   private static final long serialVersionUID = 1L;
   private static final int PREF_W = 900;
   private static final int PREF_H = 700;
   private static final int TIMER_DELAY = 30;
   public int rectX = 10;
   public int rectY = 10;
   public int width = 8;
   public int height = 10;

   public Gunman() {
      new Timer(TIMER_DELAY, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent actEvt) {
            if (rectX < PREF_W && rectY < PREF_H) {
               rectX++;
               rectY++;
               repaint();
            } else {
               ((Timer)actEvt.getSource()).stop();
            }
         }
      }).start();
   }


   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.red);
      g.drawRect(rectX, rectY, width, height);
      g.fillRect(rectX, rectY, width, height);
   }

   public int getRectX() {
      return rectX;
   }

   public void setRectX(int rectX) {
      this.rectX = rectX;
   }

   public int getRectY() {
      return rectY;
   }

   public void setRectY(int rectY) {
      this.rectY = rectY;
   }

   private static void createAndShowGui() {
      Gunman mainPanel = new Gunman();

      JFrame frame = new JFrame("Gunman");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

}


回答2:

There are numerous ways to animate. Here is another example. Notice the location of repaint() inside a background thread. This paints directly on a JFrame. Use paintComponent() when painting on JPanels.

public static void main(String args[]) throws Exception {
new JFrame("Draw a red box") {
  Point pointStart = new Point(50,50);
  Point pointEnd   = new Point(200,200);
  public void paint(Graphics g) {
    super.paint(g);
    if (pointStart != null) {
      g.setColor(Color.RED);
      g.drawRect(pointStart.x, pointStart.y, pointEnd.x, pointEnd.y);
    }}{
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setSize(300, 300);
    setLocation(300, 300);
    setVisible(true);
    Thread t = new Thread(new Runnable() {
      public void run() {
        while (pointEnd.x > 0 && pointEnd.y > 0) {
          pointEnd = new Point(--pointEnd.x, --pointEnd.y);
          repaint();
          try {
            Thread.sleep(22);
          } catch (InterruptedException e) {
            e.printStackTrace();
        }}
        pointStart = null;
        pointEnd = null;
    }});
    t.setDaemon(true);
    t.start();
  }};}


回答3:

UPDATE: Ok previous answer was not so good from the old memory, here is the quickest, cheapest, most dirty way to get some animation quicksmart, you can copy and compile the code as is:

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;

public class Test extends JFrame {

    public Gunman g = new Gunman();

    public static void main( String[] args ) {
        Test t = new Test();
        t.setSize( 800, 600 );
        t.setVisible( true );
        t.getContentPane().add( t.g );

        while ( true ) {
            t.g.x = t.g.x + 1;
            t.g.y = t.g.y + 1;
            t.repaint();
            try {
                Thread.sleep( 100 );
            } catch ( InterruptedException e ) {
            }
        }
    }

    public void paintComponent( Graphics g ) {
        g.clearRect( 0, 0, 800, 600 );
    }
}


class Gunman extends JComponent {

    private static final long serialVersionUID = 1L;
    public int x = 10;
    public int y = 10;
    public int width = 8;
    public int height = 10;

    public void paintComponent( Graphics g ) {
        g.setColor( Color.red );
        g.fillRect( x, y, width, height );
    }
}

There are ALOT of shortcuts in this, as Hovercraft of Eels has said, this is not an 'ideal' way to do it, but it has the basic structure. You have a canvas (I have used the JFrame, again not really recommended), and you add a component to it. You must override paintComponent (if you are using swing, which I do recommend you do), and this will draw your component.
You then need to alter your component's position in some way (recommend a proper method call on the object that does this), and ask the canvas to repaint itself.
I have included the wait so you can see what's happening, but if you are thinking of game programming, you should look into creating a game loop to manage this, I recommend Killer game programming in java, you can get a free ebook version with a quick google search.