【问题标题】:I am trying to move a ball in applet using thread but its not moving我正在尝试使用线程在小程序中移动一个球,但它没有移动
【发布时间】:2013-01-05 12:52:55
【问题描述】:

我正在尝试使用线程在小程序中移动一个球,但它没有移动。任何人都可以帮助我作为小程序的新手并继续进行游戏开发..这里是我的代码供参考

public class ballGame extends JApplet implements Runnable
{
    int x_pos=50;
    int y_pos=100;
    int rad=10;
    Thread t;

    public void start() 
    {
        super.start();
        t=new Thread("t");
        t.start();
    }

    public void paint(Graphics g) 
    {
        super.paint(g);
        g.setColor(Color.red);
        setBackground(Color.BLACK);
        g.drawOval(x_pos,y_pos,2*rad,2*rad); 

        while(true)
        {
            x_pos++;

            //validate();
            repaint();

            try
            {
                Thread.sleep(100);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }

        }//end of while
    }//end of paint()
}

【问题讨论】:

  • 这个answer,可能是给定方向的一个很好的学习步骤。

标签: java multithreading swing awt event-dispatch-thread


【解决方案1】:

Swing 是一个单线程环境。也就是说,所有更新和交互都在单个线程中执行。 Swing 也是线程安全的。这意味着 UI 的所有更新必须在该线程(事件调度线程或 ETD)的上下文中执行。

任何阻止 EDT 的代码都会阻止它(除其他外)重新绘制 UI 并响应用户的输入。

您的绘制代码永远不会更新屏幕,实际上它会使您的应用程序看起来“挂起”,因为不允许完成 paint 方法并阻止 ETD。

paint方法被调用后会快速返回并可能快速连续重复调用,这是一个例外。

一般来说,Thread 可能有点过头了,在这种情况下,javax.swing.Timer 之类的东西会更合适。

public class AnimatedBoat {

    public static void main(String[] args) {
        new AnimatedBoat();
    }

    public AnimatedBoat() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new AnimationPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class AnimationPane extends JPanel {

        private BufferedImage boat;
        private int xPos = 0;
        private int direction = 1;

        public AnimationPane() {
            try {
                boat = ImageIO.read(new File("boat.png"));
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        xPos += direction;
                        if (xPos + boat.getWidth() > getWidth()) {
                            xPos = getWidth() - boat.getWidth();
                            direction *= -1;
                        } else if (xPos < 0) {
                            xPos = 0;
                            direction *= -1;
                        }
                        repaint();
                    }

                });
                timer.setRepeats(true);
                timer.setCoalesce(true);
                timer.start();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            int y = getHeight() - boat.getHeight();
            g.drawImage(boat, xPos, y, this);

        }

    }

}

作为旁注。您应该很少需要覆盖顶级容器的paint 方法,例如JAppletJFrame,虽然这样做有很多充分的理由,但您最感兴趣的是事实它们不是双缓冲的,这意味着您可能会在屏幕更新时看到闪烁。

最好使用JPanel 之类的东西并覆盖它的paintComponent 方法。

看看

更多信息

nb 虽然我在示例中使用了JFrame,但将动画面板放入JApplet 是一件简单的事情,这是您不需要/不想从顶层扩展的另一个原因容器;)

【讨论】:

    【解决方案2】:

    paint 中有一个无限循环意味着该方法的单次传递都无法完成。

    你也不应该在paint 方法中调用Thread.sleep(100)。这会阻止EDT 并降低性能。

    改为使用Swing Timer 来进行更新和重绘工作。此外,我会将JComponent 子类化并覆盖paintComponent

    【讨论】:

    • 好的 Reimeus...我会尝试使用摇摆计时器谢谢
    【解决方案3】:

    您不能在paint() 中调用repaint() 方法。而且你不能在 paint() 方法中组织无限循环 - 这样做,你会阻止你的小程序中的绘图。

    【讨论】:

    • (挑剔 - 你“可以”从 paint 中调用 repaint,没有什么能阻止你,但你“不应该”;)) +1
    • @MadProgrammer 确实,我的意思是你shouldn't。很明显:)
    【解决方案4】:

    x_pos是一个 int 值,因此它是通过值而不是引用传递给方法的。这就是为什么当你改变它的值时,你圈内的值不会更新......

    【讨论】:

      【解决方案5】:

      您创建一个没有run() 方法的线程。这个方法应该包含可运行的代码......此外,paint() 方法是绘制东西,而不是更新东西!

      因此,将您的 while 循环从 paint() 方法移到线程的 run() 方法中:

      t=new Thread("t") {
          @Override
          public void run()
          {
              while(true)
              {
                  x_pos++;
      
                  //validate();
                  repaint();
      
                  try
                  {
                      Thread.sleep(100);
                  }
                  catch(Exception e)
                  {
                      e.printStackTrace();
                  }
              }//end of while
          }
      };
      

      请注意,ballGame 不需要 implement Runnable 部分。因为您创建的线程将提供它。

      【讨论】:

      • 实际上我已经编写了 run() 并且只在其中编写了 while 循环,但它不起作用......我还是会再试一次......谢谢你的帮助
      • 我想你是直接在ballGame类中写了run()方法...你应该把它放在Thread中以便为Thread添加功能,否则它赢了什么都不做!
      • 并且需要实现 runnable 我认为否则 run() 会显示错误
      • 正如我所说,您应该删除类定义中的 implements Runnable 部分。
      • 将 run() 放入线程中意味着您可以在哪里告诉我
      【解决方案6】:

      在 Runnable 的 run 方法中有 while 循环。

      更新:

      在启动方法中有这个。

      t=new Thread(this);
      t.start();
      

      【讨论】:

      • 我尝试将它放在 run() 中,但球仍然没有移动
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-09-28
      • 2017-05-29
      • 2021-12-22
      • 2020-06-09
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多