Java Code for Simple Animation only runs on Window

2019-07-18 11:06发布

问题:

In this simple code example for an animation of a bouncing ball:

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

The animation is drawn fine and runs on a Windows machine (using JCreator), but will not run on Mac OS X compiled with either IntelliJ or Eclipse. Tried on two different OS X machines, and both will draw the ball and background (after a long wait) but will not proceed with the animation.

Is there some sort of platform-specific code in here that I am missing? Thanks!

回答1:

Never do anything in any paint method that would either block or might trigger a repaint request.

You should always call super.paintXxx, these methods do a lot of work in the background, usually a lot better then you can.

You shouldn't need to (very rare cases) extend from top level containers, like JApplet or JFrame. You are better of creating a custom container (such as JPanel) and add your components to it (or perform your custom painting). Apart from the double buffering support, you also gain flexibility in deployment choices.

Don't kid yourself, you don't control the paint process, it's up to the repaint manager to make those decisions, you can however, "encourage" it to update. This causes the most pain when people start playing with animation.

You should modify the "state" of the animation outside of the influence of the Event Dispatching Thread (EDT) or in your case, out side the paint context.

You problem is simple enough that a simple javax.swing.Timer will solve it. More complex animations might require a "animation" thread.

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

I'm concerned with the "delay" of 10 milliseconds, it's enough to want me to face a fit :P

You might find reading through...

  • Concurrency
  • Concurrency in Swing
  • How to Use Swing Timers
  • Performing Custom Painting
  • Painting in AWT and Swing

Of some interest



回答2:

paint() method is called in the UI thread, and it should return as fast as possible.

So where will you put the animation code then?The answer is simple: you will need to put the code into a separate thread.

For the difference between Windows and OS X, all I can say is that it should be related on how they schedule threads, or something like that.