Creating an animation using JApplet

2019-07-15 08:09发布

I am working on a Java program that uses a JApplet which makes a ball bounce up and down. I am able to paint the shape onto the JApplet and everything like that. I just can't seem to get it to move. I have looked into this and saw that I need to create a method that pauses the shape then clears it from the JApplet, changes coordinates, and then redraws the shape in the new area but for some reason it's just not working for me.

Thank you in advance for the help.

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;


public class Circle extends JApplet {

int x=100;
int y=100;
int diameter=50;


public void paint(Graphics g) {

int xResize=500;
int yResize=500;

super.paint(g);
resize(xResize,yResize);
g.drawOval(x, y, diameter, diameter);   
}

public Circle (int startX, int startY,int startDiameter) {

this.x=startX;
this.y=startY;
this.diameter=startDiameter;

} 

public int getX() {
return x;
}
public void setX(int startX){
x=startX;
}
public int getY() {
return y;
}
public void setY(int startY){
y=startY;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(int startDiameter){
diameter=startDiameter;


}

while (ball.getY() + ballDiameter < windowHeight) {

g.clearRect(x-1,100,20,20); 

g.fillOval(x,100,20,20); 

try 

{ 

Thread.sleep(70); 

} 

catch(Exception e) 

{ 

} 


pause.wait(0.05);

//g.clearRect(0,0,windowWidth,windowHeight);

g.clearRect(x-1,100,20,20); 

g.fillOval(x,100,20,20); 

try 

{ 

Thread.sleep(70); 

} 

catch(Exception e) 

{ 

} 

ball.setY( ball.getY()+spacer); 


}


while (ball.getY() + ballDiameter > 0) {

g.clearRect(x-1,100,20,20); 

g.fillOval(x,100,20,20); 

try 

{ 

Thread.sleep(70); 

} 

catch(Exception e) 

{ 

} 

pause.wait(0.05);
//g.clearRect(0,0, windowWidth, windowHeight);
ball.setY(ball.getY()-spacer);


}

Bouncing Ball Class:

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;

public class BouncingBall extends JApplet {

public void paint(Graphics g) {

super.paint(g);

final int x = 0;
int y = 0;
final int diameter = 15;
final int spacer = 5;
int windowWidth = getWidth();
int windowHeight = getHeight();

Circle ball = new Circle(x, y, diameter);
Pause pause = new Pause();
int ballDiameter = ball.getDiameter();
int roof = getHeight();

1条回答
等我变得足够好
2楼-- · 2019-07-15 08:45

Firstly, animation is the illusion of change over time. So first, you need some way to update your values on a regular bases.

Secondly, Swing is a single threaded framework, this means that anything that blocks this thread will stop the Event Dispatching Thread from process new events, including repaint requests.

Thirdly, all interactions, changes or updates to the UI are expected to be executed from within the context of the EDT.

This means, you need some way to wait in the background (off the EDT), which can notify you when it's time to perform an update and have those updates synched back to the EDT.

javax.swing.Timer is a perfect candidate for this purpose. It can wait in the background for a specified period of time; It will notify the ActionListener within the context of the EDT when the time period expires and it can repeat.

Start by overriding the init, start and stop methods of the JApplet

@Override
public void init() {
    super.init(); 
    timer = new Timer(40, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
        }
    });
}

@Override
public void start() {
    super.start();
    timer.start();
}

@Override
public void stop() {
    timer.stop();
    super.stop(); 
}

Basically, this constructs a Timer, starts and stops it appropriately.

Next, we need to supply some logic for the animation...

As I said before, animation is the illusion of movement over time. We have the time part taken care of (more or less), now we need the movement.

The basic idea is to apply a small amount of change to the current value and provide boundary checking and finally repaint the result.

x += delta;
if (x < 0) {
    x = 0;
    delta *= -1;
} else if (x + diameter > getWidth()) {
    x = getWidth() - diameter;
    delta *= -1;
}
repaint();

Here, I've declared delta as instance variable within the applet and set it's value to 2. This logic should be added to the actionPerformed method of the ActionListener registered with the Timer

To get this to run, you will need to remove your constructor, as applets should must have a default/empty constructor.

You should also remove the resize call from the paint method, as this is just going to cause more repaint requests to be issued, which will eventually consume your CPU.

When you run it, you might get lucky and see the circle, occasionally (or it will flicker). This is because top level containers in Swing aren't double buffered. Now, you could implement your own buffering strategy, but Swing components are double buffered by default...

To fix this, we can create a simple DrawPanel that extends from a JPanel and override it's paintComponent method

So you could end up with something more like...

enter image description here

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Circle extends JApplet {

    private int delta = 2;

    private Timer timer;
    private DrawPane drawPane;

    @Override
    public void init() {
        super.init();
        setLayout(new BorderLayout());
        drawPane = new DrawPane();
        add(drawPane);
        timer = new Timer(40, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                int x = drawPane.getAnimationX();
                int diameter = drawPane.getDiameter();
                x += delta;
                if (x < 0) {
                    x = 0;
                    delta *= -1;
                } else if (x + diameter > getWidth()) {
                    x = getWidth()- diameter;
                    delta *= -1;
                }
                drawPane.setAnimationX(x);
                repaint();
            }
        });
    }

    @Override
    public void start() {
        super.start();
        timer.start();
    }

    @Override
    public void stop() {
        timer.stop();
        super.stop();
    }

    public class DrawPane extends JPanel {

        int x = 100;
        int y = 100;
        int diameter = 50;

        public void setAnimationX(int x) {
            this.x = x;
        }

        public void setAnimationY(int y) {
            this.y = y;
        }

        public int getAnimationX() {
            return x;
        }

        public int getAnimationY() {
            return y;
        }

        public int getDiameter() {
            return diameter;
        }

        public void setDiameter(int startDiameter) {
            diameter = startDiameter;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawOval(x, y, diameter, diameter);
        }
    }
}

Also, be careful when overriding methods, the getX and getY methods have very special meanings, you could cripple your application by overrding them...

I'd also question the need to use JApplet, it would be better to simply start with using a JFrame which is significantly easier to get working properly.

Take a look at Concurrency in Swing and How to use Swing Timers for more details

查看更多
登录 后发表回答