我正在学习使用Java线程,因此我决定制作一个简单的弹跳球程序。 但是,该程序显示了多个线程,但是只有一个线程利用了窗口大小,其他球被限制在一个区域。
我尝试设置每个球的JPanel的大小以及不起作用的不同布局。
BouncingBall.java
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.*;
public class BouncingBall extends JFrame {
ArrayList<Ball> balls = new ArrayList<Ball>();
//GUI Elements
JLabel lblCount;
JButton btn= new JButton("Stop");
BouncingBall() {
// setDefaultLookAndFeelDecorated(true);
setTitle("BouncingBall");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 200);
for (int i = 0; i < 5; i++) {
balls.add(new Ball());
}
setLayout(new FlowLayout());
setContentPane(balls.get(0));
balls.get(0).init();
for (Ball b : balls
) {
System.out.println(b.getHeight());
if (b != balls.get(0)) {
b.init();
balls.get(0).add(b);
}
}
this.add(btn,BorderLayout.SOUTH);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Ball b :balls
) {
b.stopMoving();
}
}
});
addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
for (Ball b :balls
) {
b.startMoving();
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
});
this.setVisible(true);
}
public static void main(String[] args) {
new BouncingBall();
}
}
Ball.java
import javax.swing.*;
import java.awt.*;
public class Ball extends JPanel implements Runnable {
// Box height and width
int width;
int height;
// Ball Size
float radius = 5;
float diameter = radius * 2;
// Center of Call
float X = radius + 50;
float Y = radius + 20;
// Direction
float dx;
float dy;
//Vars
int count = 0;
float[] colorHSB = new float[3];
boolean moving = false;
//Thread
Thread t;
Ball() {
dx = (float) Math.random() * 10;
dy = (float) Math.random() * 10;
width = getWidth();
height = getHeight();
for (int i = 0; i < 3; i++) {
colorHSB[i] = (float) Math.random() * 255;
}
t = new Thread(this);
}
void init() {
t.start();
}
public void run() {
while (true) {
width = getWidth();
height = getHeight();
if (moving){
X = X + dx;
Y = Y + dy;
}
if (X - radius < 0) {
dx = -dx;
X = radius;
addCount();
} else if (X + radius > width) {
dx = -dx;
X = width - radius;
addCount();
}
if (Y - radius < 0) {
dy = -dy;
Y = radius;
addCount();
} else if ((Y + radius) > height) {
dy = -dy;
Y = height - radius;
addCount();
}
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
}
}
}
public void startMoving() {
moving = true;
}
public void stopMoving(){
moving=false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.getHSBColor(colorHSB[0], colorHSB[1], colorHSB[2]));
g.fillOval((int) (X - radius), (int) (Y - radius), (int) diameter, (int) diameter);
}
public void addCount() {
count++;
System.out.println(count);
}
}
程序正在运行的照片
它应该显示所有球在框架周围反弹,并利用整个窗口。
答案 0 :(得分:1)
我的答案基于MCV model。这将在模型,视图和控制器之间划分职责。
每个(M,V和C)都成为定义明确的单一职责类别。
首先,类的数量以及它们之间的关系可能令人困惑。在学习并理解了结构之后,您会意识到它实际上将您要解决的“问题”划分为更小且更易于处理的部分。
球可以是模型的简单示例。实际上,它是一种pojo,可以容纳视图绘制球所需的所有信息:
//a model representing ball
class Ball {
//Ball attributes
private static final int SIZE = 10; //diameter
private int x, y; // Position
private final Color color;
private Observer observer; //to be notified on changes
Ball() {
Random rnd = new Random();
color = new Color(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
}
Color getColor() {
return color;
}
int getSize(){
return SIZE;
}
synchronized int getX() {
return x;
}
synchronized void setX(int x) {
this.x = x;
notifyObserver();
}
synchronized int getY() {
return y;
}
synchronized void setY(int y) {
this.y = y;
notifyObserver();
}
void registerObserver(Observer observer){
this.observer = observer;
}
void notifyObserver(){
if(observer == null) return;
observer.onObservableChanged();
}
}
请注意,您可以将Observer
注册到Ball
。 Observer
的定义如下:
//listening interface. Implemented by View and used by Ball to notify changes
interface Observer {
void onObservableChanged();
}
Ball
使用它来通知观察者已发生更改。
Ball
还具有一些synchronized
的getter和setter方法,因此它的属性可以被多个线程访问。
我们还应该定义Model
,这是另一个pojo,它是封装视图所需的所有信息的类:
//view model: hold info that view needs
class Model {
private final ArrayList<Ball> balls;
private final int width, height;
Model(){
balls = new ArrayList<>();
width = 300; height = 200;
}
boolean addBall(Ball ball){
return balls.add(ball);
}
List<Ball> getBalls() {
return new ArrayList<>(balls); //return a copy of balls
}
int getWidth() {
return width;
}
int getHeight() {
return height;
}
}
顾名思义,View
就是:
class View {
private final BallsPane ballsPane;
View(Model model){
ballsPane = new BallsPane(model);
}
void createAndShowGui(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(ballsPane);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
Observer getObserver(){
return ballsPane;
}
}
class BallsPane extends JPanel implements Observer {
private final Model model;
BallsPane(Model model){
this.model = model;
setPreferredSize(new Dimension(model.getWidth(), model.getHeight()));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(Ball b : model.getBalls()){
g.setColor(b.getColor());
g.fillOval(b.getX(), b.getY(), b.getSize(), b.getSize());
}
}
@Override
public void onObservableChanged() {
repaint(); //when a change was notified
}
}
请注意,View
(实际上是BallsPane
)实现了Observer
。它将观察(或监听)Ball
中的更改,并通过调用repaint()
来响应每一个更改。
由于每个Ball
都有synchronized
个位置(x,y)的获取和设置者,因此您可以更改这些属性:
class BallAnimator implements Runnable{
private final Ball ball;
private final int maxX, maxY;
private final Random rnd;
private boolean moveRight = true, moveDown = true;
private static final int STEP =1, WAIT = 40;
BallAnimator(Ball ball, int maxX, int maxY) {
this.ball = ball;
this.maxX = maxX;
this.maxY = maxY;
rnd = new Random();
ball.setX(rnd.nextInt(maxX - ball.getSize()));
ball.setY(rnd.nextInt(maxY - ball.getSize()));
new Thread(this).start();
}
@Override
public void run() {
while(true){
int dx = moveRight ? STEP : -STEP ;
int dy = moveDown ? STEP : -STEP ;
int newX = ball.getX() + dx;
int newY = ball.getY() + dy;
if(newX + ball.getSize()>= maxX || newX <= 0){
newX = ball.getX() - dx;
moveRight = ! moveRight;
}
if(newY +ball.getSize()>= maxY || newY <= 0){
newY = ball.getY() - dy;
moveDown = ! moveDown;
}
ball.setX(newX);
ball.setY(newY);
try {
Thread.sleep(WAIT);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
添加一个控制器并将其放在一起:BouncingBalls
充当控制器。它“连接”解决方案的不同部分。
为了方便和简单起见,可以将以下完整代码复制粘贴到一个名为BouncingBalls.java的文件中,然后运行。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BouncingBalls{
BouncingBalls(int numOfBalls) {
Model model = new Model();
View view = new View(model);;
for (int i = 0; i < numOfBalls; i++) {
Ball b = new Ball(); //construct a ball
model.addBall(b); //add it to the model
b.registerObserver(view.getObserver()); //register view as an observer to it
new BallAnimator(b, model.getWidth(), model.getHeight()); //start a thread to update it
}
view.createAndShowGui();
}
public static void main(String[] args) {
new BouncingBalls(5);
}
}
//listening interface. Implemented by View and used by Ball to notify changes
interface Observer {
void onObservableChanged();
}
class View {
private final BallsPane ballsPane;
View(Model model){
ballsPane = new BallsPane(model);
}
void createAndShowGui(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(ballsPane);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
Observer getObserver(){
return ballsPane;
}
}
class BallsPane extends JPanel implements Observer {
private final Model model;
BallsPane(Model model){
this.model = model;
setPreferredSize(new Dimension(model.getWidth(), model.getHeight()));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(Ball b : model.getBalls()){
g.setColor(b.getColor());
g.fillOval(b.getX(), b.getY(), b.getSize(), b.getSize());
}
}
@Override
public void onObservableChanged() {
repaint(); //when a change was notified
}
}
//view model: hold info that view needs
class Model {
private final ArrayList<Ball> balls;
private final int width, height;
Model(){
balls = new ArrayList<>();
width = 300; height = 200;
}
boolean addBall(Ball ball){
return balls.add(ball);
}
List<Ball> getBalls() {
return new ArrayList<>(balls); //return a copy of balls
}
int getWidth() {
return width;
}
int getHeight() {
return height;
}
}
//a model representing ball
class Ball {
//Ball attributes
private static final int SIZE = 10; //diameter
private int x, y; // Position
private final Color color;
private Observer observer; //to be notified on changes
Ball() {
Random rnd = new Random();
color = new Color(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
}
Color getColor() {
return color;
}
int getSize(){
return SIZE;
}
synchronized int getX() {
return x;
}
synchronized void setX(int x) {
this.x = x;
notifyObserver();
}
synchronized int getY() {
return y;
}
synchronized void setY(int y) {
this.y = y;
notifyObserver();
}
void registerObserver(Observer observer){
this.observer = observer;
}
void notifyObserver(){
if(observer == null) return;
observer.onObservableChanged();
}
}
class BallAnimator implements Runnable{
private final Ball ball;
private final int maxX, maxY;
private final Random rnd;
private boolean moveRight = true, moveDown = true;
private static final int STEP =1, WAIT = 40;
BallAnimator(Ball ball, int maxX, int maxY) {
this.ball = ball;
this.maxX = maxX;
this.maxY = maxY;
rnd = new Random();
ball.setX(rnd.nextInt(maxX - ball.getSize()));
ball.setY(rnd.nextInt(maxY - ball.getSize()));
new Thread(this).start();
}
@Override
public void run() {
while(true){
int dx = moveRight ? STEP : -STEP ;
int dy = moveDown ? STEP : -STEP ;
int newX = ball.getX() + dx;
int newY = ball.getY() + dy;
if(newX + ball.getSize()>= maxX || newX <= 0){
newX = ball.getX() - dx;
moveRight = ! moveRight;
}
if(newY +ball.getSize()>= maxY || newY <= 0){
newY = ball.getY() - dy;
moveDown = ! moveDown;
}
ball.setX(newX);
ball.setY(newY);
try {
Thread.sleep(WAIT);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}