我正在尝试创建一个Java applet,它在applet窗口中弹出几个球,每个都有自己的线程。使用下面的代码绘制所有球,但只有第一个球移动。我做错了什么?
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static java.awt.Color.*;
public class BouncingBalls extends Applet implements Runnable {
List<Ball> ballList = new ArrayList(); // holds Ball objects
Color[] colors = new Color[]{BLACK, GRAY, WHITE, PINK, RED, ORANGE, YELLOW,
GREEN, BLUE, CYAN}; // array holding available colors
static int width, height; // variables for applet dimensions
int ballCount; // number of balls to be created, set by html parameter
Random random = new Random(); // random number generator
public void init() {
// get window dimensions
width = getSize().width;
height = getSize().height;
//get number of balls from html
String ballCountString = this.getParameter("ballCount");
try {
ballCount = Integer.parseInt(ballCountString);
} catch (NumberFormatException e) {
ballCount = 10; // set to 10 by default
}
for (int i = 0; i < ballCount; i++) {
// randomly assign ballDiameter between 1 and 20
int ballDiameter = random.nextInt(20) + 1;
// create and add balls to ballList
ballList.add(new Ball(
random.nextInt(width - ballDiameter), // set x coordinate
random.nextInt(height - ballDiameter), // set y coordinate
ballDiameter, // set ballDiameter
random.nextInt(ballDiameter) + 1, // deltaX <= ballDiameter
random.nextInt(ballDiameter) + 1, // deltaY <= ballDiameter
colors[i % 10] // use remainder to choose colors[] element
)
);
} // end for
} // end init
public void start() {
for (Ball ball: ballList) {
Thread t;
t = new Thread(this);
t.start();
} // end for
} // end start
public void run() {
for (Ball ball : ballList) {
// infinite loop: ball moves until applet is closed
while (true) {
ball.move();
repaint(); // call paint method to draw circle in new location
// set ball repaint delay using Thread sleep method
try {
Thread.sleep(20); // wait 20 msec before continuing
} catch (InterruptedException e) {
return;
}
} // end while
} // end for
} // end run
public void paint(Graphics g) {
super.paint(g);
for (Ball ball : ballList) {
// set current color
g.setColor(ball.ballColor);
// draw filled oval using current x and y coordinates and diameter
g.fillOval(ball.x, ball.y, ball.diameter, ball.diameter);
} // end for
} // end paint
}
class Ball {
int x, y, // coordinates of upper-left corner of circle
diameter, // circle diameter
deltaX, deltaY; // number of pixels ball moves each time it's repainted
Color ballColor;
public Ball(int x, int y, int diameter, int deltaX, int deltaY,
Color ballColor) {
this.x = x;
this.y = y;
this.diameter = diameter;
this.deltaX = deltaX;
this.deltaY = deltaY;
this.ballColor = ballColor;
} // end Ball constructor
public void move() {
// update x and y coordinates using delta values
x += deltaX;
y += deltaY;
// reverse x direction when ball reaches boundary
if (x >= BouncingBalls.width - diameter || x <= 0){
deltaX = -deltaX;
} // end if
// reverse y direction when ball reaches boundary
if (y >= BouncingBalls.height - diameter || y <= 0) {
deltaY = -deltaY;
} // end if
} // end move
} // end BouncingBalls
答案 0 :(得分:6)
您的while(true)
应该在for循环之外。它位于迭代器返回的第一个球上。
话虽这么说,你可能想要为每个线程一个球回顾你的逻辑。它实际上似乎创建N个线程(N是球的数量),其中每个线程将移动所有球而不是仅一个。
编辑以解决我的第二点:
假设你有10个球。你启动10个线程,每个线程迭代所有球。
例如:
Thread 1:
public void run(){
for(Ball b : ballList){
b.move();
b.repaint();
}
}
Thread 2:
public void run(){
for(Ball b : ballList){
b.move();
b.repaint();
}
}
Thread N:
public void run(){
for(Ball b : ballList){
b.move();
b.repaint();
}
}
这样做是因为你创建的线程具有相同的this
runnable实例,迭代每个球。
public void start() {
for (Ball ball: ballList) {
Thread t;
t = new Thread(this);
t.start();
} // end for
} // end start
所以我想,如果每个球应该每20毫秒移动1个单位。你应该最终看到每20毫秒在这种情况下每隔20毫秒移动N * 1个单位。
编辑 - 关于建议。
不是将this
设置为runnable,而是应该从this
类中删除Runnable的实现,并创建一个新的Runnable,它将单个Ball作为参数。
private static class MovingRunnable implements Runnable{
private final Ball b;
private MovingRunnable(Ball b){this.b=b;}
public void run(){
for(;;){
b.move();
b.repaint();
Thread.sleep(20);
}
}
}
稍后启动方法
public void start() {
for (Ball ball: ballList) {
Thread t;
t = new Thread(new MovingRunnable(ball));
t.start();
} // end for
} // end start
所以这里每个Ball都有它自己的线程,它拥有自己的Runnable。现在每个球只会每20毫秒一次调用move
一次。
但它仍然不完美,因为repaint
只能由UI线程调用,每个线程调用它会导致不同的问题(尽管你可能没有注意到任何问题,但值得一说)。 / p>