【问题标题】:Animating a Rectangle in JavaJava中的矩形动画
【发布时间】:2012-04-03 02:19:01
【问题描述】:

我一直试图让这个矩形移动我使用 for 循环创建的。这段代码所发生的只是有一个原始矩形,然后在该矩形旁边有一个新矩形。没有动画发生,只有那两个矩形显示在窗口上。有什么方法可以让这个矩形动画?

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

【问题讨论】:

    标签: java swing animation draw


    【解决方案1】:

    在paint 或paintComponent 方法中没有程序逻辑,我的逻辑是指带有“运动”的for 循环,因为那是行不通的。你想

    • 几乎从不在 JComponent 的paint 方法中绘制,而是在它的paintComponent 方法中绘制。
    • 不要忘记调用super.paintComponent(g) 方法,通常作为paintComponent(g) 覆盖中的第一个方法调用。
    • 使用摆动计时器逐步更改 x 和 y 值
    • 更改后在 JComponent 上调用 repaint()

    例如

    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】:

    有多种制作动画的方法。这是另一个例子。注意 repaint() 在后台线程中的位置。这直接在 JFrame 上绘制。在 JPanel 上绘画时使用 paintComponent()。

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

    【讨论】:

    • 虽然repaint() 是线程安全的,但对点的访问是不同步的;请改用EventQueue.invokeLater()javax.swing.Timer
    【解决方案3】:

    更新:好吧,从旧的记忆来看,以前的答案不太好,这是获得一些动画快速智能的最快、最便宜、最脏的方法,您可以按原样复制和编译代码:

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

    这有很多捷径,正如 Eels 气垫船所说,这不是一种“理想”的方式,但它具有基本结构。你有一个画布(我使用过 JFrame,再次不推荐),然后向它添加一个组件。您必须覆盖paintComponent(如果您使用的是swing,我建议您这样做),这将绘制您的组件。
    然后,您需要以某种方式更改组件的位置(建议对执行此操作的对象进行适当的方法调用),并要求画布重新绘制自身。
    我已经包含了等待,所以你可以看到正在发生的事情,但是如果你正在考虑游戏编程,你应该考虑创建一个游戏循环来管理这个,我推荐用 java 进行杀手游戏编程,你可以获得一个免费的电子书版本快速谷歌搜索。

    【讨论】:

    • 不要在 Swing 应用程序中使用 while (true) 循环,因为您有踩到事件线程的风险(这里只避免使用,因为您在 main 方法中执行此操作,尤其是永远不要使用 Graphic通过调用组件的getGraphics() 方法获得的对象。获得的对象不是持久的,用它绘制的任何东西在下一次重绘时都不会持久——这是你无法控制的。
    • 是的,while (true) 是一个肮脏的黑客,所以他可以快速在屏幕上显示一些东西,然后从那里正确地做。我正在使用getContentPane(),这也不应该使用吗?已经有一段时间了
    • getContentPane() 返回的是 Component,而不是 Graphics 对象,您不能在静态设置中使用 this。投票删除此答案。
    猜你喜欢
    • 2012-04-03
    • 2016-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-18
    • 2016-09-29
    • 2015-01-15
    相关资源
    最近更新 更多