Java 使用多线程更新GUI
我正在学习使用java线程,所以我决定制作一个简单的弹跳球程序。 然而,程序显示多个线程,但只有一个线程利用了窗口大小,其他球仅限于一个区域 我试着为每个球的JPanel和不同的布局设置大小,但都不起作用 BouncingBall.javaJava 使用多线程更新GUI,java,multithreading,swing,animation,custom-painting,Java,Multithreading,Swing,Animation,Custom Painting,我正在学习使用java线程,所以我决定制作一个简单的弹跳球程序。 然而,程序显示多个线程,但只有一个线程利用了窗口大小,其他球仅限于一个区域 我试着为每个球的JPanel和不同的布局设置大小,但都不起作用 BouncingBall.java import java.awt.*; 导入java.awt.event.ActionEvent; 导入java.awt.event.ActionListener; 导入java.awt.event.MouseEvent; 导入java.awt.event.M
import java.awt.*;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.MouseEvent;
导入java.awt.event.MouseListener;
导入java.util.ArrayList;
导入javax.swing.*;
公共类BouncingBall扩展JFrame{
ArrayList balls=新的ArrayList();
//GUI元素
JLabel-lblCount;
JButton btn=新JButton(“停止”);
弹跳球{
//设置DefaultLookandFeelDecorated(true);
片名(“弹跳球”);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
设置大小(300200);
对于(int i=0;i<5;i++){
添加(新的Ball());
}
setLayout(新的FlowLayout());
setContentPane(balls.get(0));
get(0.init();
b:球
) {
System.out.println(b.getHeight());
如果(b!=balls.get(0)){
b、 init();
获取(0)并添加(b);
}
}
添加(btn,BorderLayout.SOUTH);
btn.addActionListener(新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
b:球
) {
b、 停止移动();
}
}
});
addMouseListener(新MouseListener(){
@凌驾
公共无效mouseClicked(MouseEvent e){
b:球
) {
b、 开始移动();
}
}
@凌驾
公共无效鼠标按下(MouseEvent e){
}
@凌驾
公共无效MouseEvent(MouseEvent e){
}
@凌驾
公共无效鼠标事件(鼠标事件e){
}
@凌驾
公共无效mouseExited(MouseEvent e){
}
});
此.setVisible(true);
}
公共静态void main(字符串[]args){
新弹跳球();
}
}
Ball.java
导入javax.swing.*;
导入java.awt.*;
公共类Ball扩展JPanel实现Runnable{
//盒子的高度和宽度
整数宽度;
内部高度;
//球大小
浮动半径=5;
浮子直径=半径*2;
//呼叫中心
浮动X=半径+50;
浮动Y=半径+20;
//方向
浮动dx;
浮动dy;
//瓦尔斯
整数计数=0;
float[]colorHSB=新浮点[3];
布尔移动=假;
//线
螺纹t;
Ball(){
dx=(float)Math.random()*10;
dy=(float)Math.random()*10;
宽度=getWidth();
高度=getHeight();
对于(int i=0;i<3;i++){
colorHSB[i]=(float)Math.random()*255;
}
t=新螺纹(本螺纹);
}
void init(){
t、 start();
}
公开募捐{
while(true){
宽度=getWidth();
高度=getHeight();
如果(移动){
X=X+dx;
Y=Y+dy;
}
如果(X-半径<0){
dx=-dx;
X=半径;
addCount();
}否则如果(X+半径>宽度){
dx=-dx;
X=宽度-半径;
addCount();
}
如果(Y-半径<0){
dy=-dy;
Y=半径;
addCount();
}如果((Y+半径)>高度),则为其他情况{
dy=-dy;
Y=高度-半径;
addCount();
}
重新油漆();
试一试{
睡眠(50);
}捕获(中断异常例外){
}
}
}
公共无效开始移动(){
移动=真;
}
公共空间停止移动(){
移动=假;
}
公共组件(图形g){
超级组件(g);
g、 setColor(Color.getHSBColor(colorHSB[0],colorHSB[1],colorHSB[2]);
g、 椭圆((int)(X-半径),(int)(Y-半径),(int)直径,(int)直径);
}
公共void addCount(){
计数++;
系统输出打印项次(计数);
}
}
程序运行的照片
它应该显示利用整个窗口在帧周围反弹的所有球。我的答案基于。这将在模型、视图和控制器之间划分责任。每一个(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();
}
}
请注意,您可以将观察者
注册到球
。观察者的定义如下:
//listening interface. Implemented by View and used by Ball to notify changes
interface Observer {
void onObservableChanged();
}
球用它来通知观察者发生了变化。
一个Ball
还有一些synchronized
getter和setter,因此它的属性可以被多个线程访问。
我们还应该定义一个模型
,另一个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;
}
}
请注意,视图(实际上是BallsPane
)实现了Observer
。它将观察(或倾听)球的变化,并对其作出响应
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
}
}
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();
}
}
}
}
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();
}
}
}
}