【问题标题】:Java Code for Simple Animation only runs on Windows简单动画的 Java 代码仅在 Windows 上运行
【发布时间】:2014-03-03 05:54:15
【问题描述】:

在这个弹跳球动画的简单代码示例中:

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

public class GraphicsMovement extends JApplet
{
public static void pause()
{
    try {
        Thread.sleep(10);
        } catch(InterruptedException e) {
          }
}

public static void main(String args[])
{
    JApplet example = new GraphicsMovement();
    JFrame frame = new JFrame("Movement");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(example);
    frame.setSize(new Dimension(500,300));       //Sets the dimensions of panel to appear when run
    frame.setVisible(true);
}

  public void paint (Graphics page)
  {
 int width = getWidth();    // width = the width of the panel which appears when run
 int height = getHeight();  // height = the height of the panel which appears when run.

//Changes background color to a blueish color
page.setColor(new Color (140,214,225));
page.fillRect(0,0,width,height);
for(int i = 0; i <= 5; i++)
{
    for (int j = 0; j <= 100; j++)
    {
        page.setColor(Color.YELLOW);
        page.fillOval(100,55 + j,100,100);  //draws a yellow oval
        pause();
        page.setColor(new Color (140,214,225));
        page.fillOval(100,55 + j,100,100);  //draws a blueish oval over the yellow oval
    }
    for (int k = 100; k >= 0; k--)
    {
        page.setColor(Color.YELLOW);
        page.fillOval(100,55 + k,100,100);  //draws a yellow oval
        pause();
        if (k != 0)
        {
            page.setColor(new Color (140,214,225));  //draws a blueish oval over the yellow oval
            page.fillOval(100,55 + k,100,100);
        }
    }
}
 }
 }

动画画得很好,可以在 Windows 机器上运行(使用 JCreator),但不能在用 IntelliJ 或 Eclipse 编译的 Mac OS X 上运行。在两台不同的 OS X 机器上尝试过,两者都将绘制球和背景(经过长时间的等待),但不会继续动画。

这里是否有一些我缺少的特定于平台的代码? 谢谢!

【问题讨论】:

  • 不要在 EDT 上睡觉; Concurrency in Swing,尤其是Initial Threads..
  • 对不起,我还是 java 新手……这是因为惯例还是 EDT 正在休眠导致这些特殊问题的事实?我阅读了链接,但我不确定为什么线程在这个特定示例中很重要。
  • @kmypwn :您不需要Thread.sleep(),而是需要使用javax.swing.Timer。这是一个很棒的example。线程与您的具体示例无关,线程与整个 Swing 有关,这就是@trashgod 提到的这两个链接的原因。
  • 拜托,拜托,请调用 super.paint(g);,品脱方法 o 很多重要的后台工作,你永远不应该忽视

标签: java macos swing animation paint


【解决方案1】:

paint() 方法在 UI 线程中被调用,它应该尽快返回。

那么你会把动画代码放在哪里呢?答案很简单:你需要把代码放到一个单独的线程中。

对于 Windows 和 OS X 之间的区别,我只能说它应该与它们如何调度线程或类似的东西有关。

【讨论】:

  • 不确定差异是否真的与线程调度有关,更多关于图形上下文的工作方式。在 Windows 中,似乎更新会立即传播到图形内存 - 我可以想象 OS X 隐式使用双缓冲区,在这种情况下,整个动画首先被绘制到缓冲区(延迟),并且只有最后一帧的动画从缓冲区翻转到视频内存。
  • 这是有道理的,金-谢谢!这可以解释在屏幕上绘制球的延迟,因为一旦显示视图,动画就已经“完成”了。我会修改代码并让它在每一帧重绘屏幕。
  • 所以我尝试添加许多不同的验证和重绘语句组合,但动画仍然无法自行运行(有时,我可以通过切换窗口来强制它,但它仍然会出现故障)。你知道我应该在哪里添加这些语句吗?
  • 这与您的重绘语句无关——应该编写paint() 方法,以便它只绘制动画的单帧,因此没有在paint 方法中执行动画的for 循环——您必须单独保留“状态”(在您的示例中为 i,j,k),并根据当前状态绘制框架。用几句话来解释有点复杂,看看如何在java-gaming.org/index.php?topic=24220.0上写一个“游戏循环”——你可以简化一些,但基本的想法是成立的。
【解决方案2】:

永远不要在任何paint 方法中做任何会阻塞或可能触发重绘请求的事情。

你应该总是打电话给super.paintXxx,这些方法在后台做了很多工作,通常比你能做的好很多。

您不需要(在极少数情况下)从顶级容器扩展,例如 JAppletJFrame。您最好创建一个自定义容器(例如JPanel)并将您的组件添加到其中(或执行您的自定义绘制)。除了双缓冲支持外,您还可以灵活地选择部署。

不要自欺欺人,您无法控制绘制过程,这些决定取决于重绘管理器,但是您可以“鼓励”它进行更新。当人们开始玩动画时,这会造成最大的痛苦。

您应该在事件调度线程 (EDT) 的影响之外修改动画的“状态”,或者在您的情况下,在 paint 上下文之外。

你的问题很简单,一个简单的javax.swing.Timer 就可以解决它。更复杂的动画可能需要一个“动画”线程。

public class GraphicsMovement extends JApplet {

    @Override
    public void init() {
        setLayout(new BorderLayout());
        add(new AnimatedPane());
    }

    @Override
    public void start() {
    }

    public class AnimatedPane extends JPanel {

        private Timer timer;
        private boolean colorSwitch = false;

        private int yOffset = 0;
        private int direction = 1;

        public AnimatedPane() {
            timer = new Timer(10, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
//                    colorSwitch = !colorSwitch;
                    yOffset += direction;
                    if (yOffset > 100) {
                        direction = -1;
                        yOffset = 100;
                    } else if (yOffset < 0){
                        direction = 1;
                        yOffset = 0;
                    }
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

            setBackground(new Color(140, 214, 225));
        }

        @Override
        protected void paintComponent(Graphics page) {
            super.paintComponent(page);
            int width = getWidth();    // width = the width of the panel which appears when run
            int height = getHeight();  // height = the height of the panel which appears when run.

            if (colorSwitch) {
                page.setColor(new Color(140, 214, 225));
            } else {
                page.setColor(Color.YELLOW);
            }
            page.fillOval(100, 55 + yOffset, 100, 100);  //draws a yellow oval
        }
    }
}

我担心 10 毫秒的“延迟”,这足以让我面对一个健康:P

你可能会发现通读...

有点兴趣

【讨论】:

  • 在您改进的示例中,您忘记了 J 和 K for 循环实际上是在做动画(在原始版本中,它首先绘制椭圆,然后休眠,然后使用背景颜色擦除椭圆并继续下一次迭代)。 J 循环用于动画“向下”,K 循环用于动画“向上”。
  • @kim 公平点,我没有太多时间,并且很难尝试找出所有循环
  • 代码现在更好地说明了您的观点 - 计算偏移量并切换线程中的移动方向,并在您实际绘画时仅以适当的偏移量绘画。此外,计时器中的 repaint() 是一个不错的选择 - 虽然它实际上并不强制重新绘制,但它会告诉系统 shomething 发生了变化,并且应该在下次系统开始执行此操作时进行绘制。
  • 我真的必须再次感谢你们所有人对 Java 学生的慷慨帮助......我为你们在这方面与我合作的热情和意愿感到谦卑。据我了解,虽然 Windows 在“绘制”每个对象时重新绘制屏幕,​​但 Mac OS X 会等待整个 paint() 方法运行,然后才实际绘制屏幕上“左侧”的内容(为什么 j 和k 循环不起作用)。上面的代码通过一个摇摆计时器执行“动画”,然后paint() 方法在每次调用该方法时绘制一帧。最后一件事:
  • 上面改进的代码可以编译(一旦我添加了适当的导入语句),但在运行时崩溃并出现以下错误:线程“main”中的异常 java.lang.NoSuchMethodException: GraphicsMovement.main( [Ljava.lang.String;) at java.lang.Class.getMethod(Class.java:1605) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:113) 据我了解,@重写 init 方法意味着上面编写的方法在执行时立即被调用,而不是作为 JApplet 的一部分的“stock”方法。这是被认为是错误中的“主要”方法吗?
猜你喜欢
  • 2020-12-02
  • 1970-01-01
  • 2015-12-28
  • 1970-01-01
  • 2012-12-24
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 2017-11-04
相关资源
最近更新 更多