import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class Ball extends JPanel implements Runnable {
List<Ball> balls = new ArrayList<Ball>();
Color color;
int diameter;
long delay;
private int x;
private int y;
private int vx;
private int vy;
public Ball(String ballcolor, int xvelocity, int yvelocity) {
if(ballcolor == "red") {
color = Color.red;
}
else if(ballcolor == "blue") {
color = Color.blue;
}
else if(ballcolor == "black") {
color = Color.black;
}
else if(ballcolor == "cyan") {
color = Color.cyan;
}
else if(ballcolor == "darkGray") {
color = Color.darkGray;
}
else if(ballcolor == "gray") {
color = Color.gray;
}
else if(ballcolor == "green") {
color = Color.green;
}
else if(ballcolor == "yellow") {
color = Color.yellow;
}
else if(ballcolor == "lightGray") {
color = Color.lightGray;
}
else if(ballcolor == "magenta") {
color = Color.magenta;
}
else if(ballcolor == "orange") {
color = Color.orange;
}
else if(ballcolor == "pink") {
color = Color.pink;
}
else if(ballcolor == "white") {
color = Color.white;
}
diameter = 30;
delay = 40;
x = 1;
y = 1;
vx = xvelocity;
vy = yvelocity;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(x,y,30,30); //adds color to circle
g.setColor(Color.black);
g2.drawOval(x,y,30,30); //draws circle
}
public void run() {
while(isVisible()) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move();
repaint();
}
}
public void move() {
if(x + vx < 0 || x + diameter + vx > getWidth()) {
vx *= -1;
}
if(y + vy < 0 || y + diameter + vy > getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
}
private void start() {
while(!isVisible()) {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.exit(1);
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
public static void main(String[] args) {
Ball ball1 = new Ball("red",3,2);
Ball ball2 = new Ball("blue",6,2);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(ball1);
f.getContentPane().add(ball2);
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
new Thread(ball1).start();
new Thread(ball2).start();
}
}
我想创建一个球列表,然后循环绘制每个球,但是我仍然很难将两个球都添加到内容窗格。
感谢您的帮助。
#1 楼
使用当前的方法...我能看到的主要问题是,您将两个不透明的组件彼此叠放...实际上,您可能会发现自己正在绕过一个组件其中一个用于其他...
您应该使用
null
布局管理器,否则它将接管您认为合适的球并进行布局。您需要确保控制尺寸和尺寸球窗格的位置。这意味着您已经接任了布局经理的角色。
您需要对球的速度和位置进行随机化处理,以减少它们从相同位置开始并在相同位置移动的机会。
仅在EDT的上下文中更新
Ball
。您并不需要X / Y值,可以使用面板。
。
public class AnimatedBalls {
public static void main(String[] args) {
new AnimatedBalls();
}
public AnimatedBalls() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Balls());
frame.setSize(400, 400);
frame.setVisible(true);
}
});
}
public class Balls extends JPanel {
public Balls() {
setLayout(null);
// Randomize the speed and direction...
add(new Ball("red", 10 - (int) Math.round((Math.random() * 20)), 10 - (int) Math.round((Math.random() * 20))));
add(new Ball("blue", 10 - (int) Math.round((Math.random() * 20)), 10 - (int) Math.round((Math.random() * 20))));
}
}
public class Ball extends JPanel implements Runnable {
Color color;
int diameter;
long delay;
private int vx;
private int vy;
public Ball(String ballcolor, int xvelocity, int yvelocity) {
if (ballcolor == "red") {
color = Color.red;
} else if (ballcolor == "blue") {
color = Color.blue;
} else if (ballcolor == "black") {
color = Color.black;
} else if (ballcolor == "cyan") {
color = Color.cyan;
} else if (ballcolor == "darkGray") {
color = Color.darkGray;
} else if (ballcolor == "gray") {
color = Color.gray;
} else if (ballcolor == "green") {
color = Color.green;
} else if (ballcolor == "yellow") {
color = Color.yellow;
} else if (ballcolor == "lightGray") {
color = Color.lightGray;
} else if (ballcolor == "magenta") {
color = Color.magenta;
} else if (ballcolor == "orange") {
color = Color.orange;
} else if (ballcolor == "pink") {
color = Color.pink;
} else if (ballcolor == "white") {
color = Color.white;
}
diameter = 30;
delay = 100;
vx = xvelocity;
vy = yvelocity;
new Thread(this).start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int x = getX();
int y = getY();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(0, 0, 30, 30); //adds color to circle
g.setColor(Color.black);
g2.drawOval(0, 0, 30, 30); //draws circle
}
@Override
public Dimension getPreferredSize() {
return new Dimension(30, 30);
}
public void run() {
try {
// Randamize the location...
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
int x = (int) (Math.round(Math.random() * getParent().getWidth()));
int y = (int) (Math.round(Math.random() * getParent().getHeight()));
setLocation(x, y);
}
});
} catch (InterruptedException exp) {
exp.printStackTrace();
} catch (InvocationTargetException exp) {
exp.printStackTrace();
}
while (isVisible()) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
move();
repaint();
}
});
} catch (InterruptedException exp) {
exp.printStackTrace();
} catch (InvocationTargetException exp) {
exp.printStackTrace();
}
}
}
public void move() {
int x = getX();
int y = getY();
if (x + vx < 0 || x + diameter + vx > getParent().getWidth()) {
vx *= -1;
}
if (y + vy < 0 || y + diameter + vy > getParent().getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
// Update the size and location...
setSize(getPreferredSize());
setLocation(x, y);
}
}
}
这种方法的“主要”问题是,每个
Ball
都有自己的Thread
。当您增加球的数量时,这将很快真正地吞噬您的系统资源... 不同的方法
如Hovercraft所言,您会更好创建一个用于容纳球的容器,其中球不是组件,而是球的“虚拟”概念,其中包含足够的信息,可以使它们从墙壁弹回。
public class SimpleBalls {
public static void main(String[] args) {
new SimpleBalls();
}
public SimpleBalls() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Spot");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
Balls balls = new Balls();
frame.add(balls);
frame.setSize(400, 400);
frame.setVisible(true);
new Thread(new BounceEngine(balls)).start();
}
});
}
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public class Balls extends JPanel {
private List<Ball> ballsUp;
public Balls() {
ballsUp = new ArrayList<Ball>(25);
for (int index = 0; index < 10 + random(90); index++) {
ballsUp.add(new Ball(new Color(random(255), random(255), random(255))));
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : ballsUp) {
ball.paint(g2d);
}
g2d.dispose();
}
public List<Ball> getBalls() {
return ballsUp;
}
}
public class BounceEngine implements Runnable {
private Balls parent;
public BounceEngine(Balls parent) {
this.parent = parent;
}
@Override
public void run() {
int width = getParent().getWidth();
int height = getParent().getHeight();
// Randomize the starting position...
for (Ball ball : getParent().getBalls()) {
int x = random(width);
int y = random(height);
Dimension size = ball.getSize();
if (x + size.width > width) {
x = width - size.width;
}
if (y + size.height > height) {
y = height - size.height;
}
ball.setLocation(new Point(x, y));
}
while (getParent().isVisible()) {
// Repaint the balls pen...
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getParent().repaint();
}
});
// This is a little dangrous, as it's possible
// for a repaint to occur while we're updating...
for (Ball ball : getParent().getBalls()) {
move(ball);
}
// Some small delay...
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
}
public Balls getParent() {
return parent;
}
public void move(Ball ball) {
Point p = ball.getLocation();
Point speed = ball.getSpeed();
Dimension size = ball.getSize();
int vx = speed.x;
int vy = speed.y;
int x = p.x;
int y = p.y;
if (x + vx < 0 || x + size.width + vx > getParent().getWidth()) {
vx *= -1;
}
if (y + vy < 0 || y + size.height + vy > getParent().getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
ball.setSpeed(new Point(vx, vy));
ball.setLocation(new Point(x, y));
}
}
public class Ball {
private Color color;
private Point location;
private Dimension size;
private Point speed;
public Ball(Color color) {
setColor(color);
speed = new Point(10 - random(20), 10 - random(20));
size = new Dimension(30, 30);
}
public Dimension getSize() {
return size;
}
public void setColor(Color color) {
this.color = color;
}
public void setLocation(Point location) {
this.location = location;
}
public Color getColor() {
return color;
}
public Point getLocation() {
return location;
}
public Point getSpeed() {
return speed;
}
public void setSpeed(Point speed) {
this.speed = speed;
}
protected void paint(Graphics2D g2d) {
Point p = getLocation();
if (p != null) {
g2d.setColor(getColor());
Dimension size = getSize();
g2d.fillOval(p.x, p.y, size.width, size.height);
}
}
}
}
由于它是由单个线程驱动的,因此具有更大的可伸缩性。
您还可以查看图像没有加载,这是一个类似的问题;)
评论
嘿,您如何解决这个“第二个示例”中的“这有点危险,因为可能在我们更新时发生重新绘制...”?我尝试检查是否为空,但由于此时的球不再是真正的空,这没有太大帮助。有任何想法吗?
–user2875404
2015年12月16日在15:31
您有两种选择,要么围绕球列表进行同步,要么使用BufferedStrategy并控制绘画过程
– MadProgrammer
15年12月16日在19:51
@AdamsonJeremiah Goid,因为这里太热了……哦,别等那种风扇了🤣
– MadProgrammer
17年11月23日在8:19
关于上述评论中讨论的“危险重绘”,我们是否也不能仅在PanelBalls中添加布尔标志“ allowRepaint”,并让move()在第一行将其设置为false,在最后一行将其设置为true。代码行?
– SND
18年1月11日在7:59
@SeekAndDestroy您必须确保将标志设置得足够远,以确保维持正确的线程同步(这样就不会导致脏读)-事实是,通过使用多个线程,您获得的收益更少同步,线程锁定和等待等额外开销将使您避免使用单个计时器/线程。由于绘画随时可能发生,因此您还冒着“冻结” UI的风险。
– MadProgrammer
18年1月11日在8:01
#2 楼
您需要在这里使用两个完全不同的类:一个用于BallContainer,它扩展了JPanel并且是绘制Ball的组件;另一个用于Ball,它不扩展任何东西,而是保留了球的坐标和颜色。 BallContainer应该在移动它们和绘制它们时对其进行遍历的List<Ball>
。评论
感谢您的回答,如果我理解正确,我将创建球列表并在列表中循环,绘制每个球。我会只调用仍然属于Ball类的paintComponent函数吗?我对Java相当陌生,在调用单独类中的函数时遇到了一些问题。
– nomad2986
2012年10月23日,下午3:05
@nomad:同样,如果我要编写代码,Ball不会有paintComponent方法,因为它不是组件。我给它一个draw(Graphics g)方法,然后在BallContainer的paintComponent(...)方法中遍历我的Ball List,在循环内调用每个Ball的draw(g)方法。
–气垫船充满鳗鱼
2012年10月23日在3:16
#3 楼
您需要做的就是增加您的paintComponent
方法。不仅要画一个球,还需要遍历所有球,然后画每个球。
示例:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball b: balls) {
g.setColor(color);
g.fillOval(x,y,30,30); //adds color to circle
g.setColor(Color.black);
g2.drawOval(x,y,30,30); //draws circle
}
}
#4 楼
package BouncingBallApp.src.copy;
import java.awt.*;
public class Ball {
private Point location;
private int radius;
private Color color;
private int dx, dy;
//private Color[] ballArr;
public Ball(Point l, int r, Color c){
location = l;
radius = r;
color = c;
}
public Ball(Point l, int r){
location = l;
radius = r;
color = Color.RED;
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public void setMotion(int dx, int dy){
this.dx = dx;
this.dy = dy;
}
public void move(){
location.translate(dx, dy);
}
public void moveTo(int x, int y){
location.move(x, y);
}
public void paint (Graphics g) {
g.setColor (color);
g.fillOval (location.x-radius, location.y-radius, 2*radius, 2*radius);
}
public void reclectHoriz() {
dy = -dy;
}
public void reclectVert() {
dx = -dx;
}
}
package BouncingBallApp.src.copy;
public class MyApp {
public static void main(String[] args) {
MyFrame frm = new MyFrame(10);
frm.setVisible(true);
for (int i=0; i<1000; i++){
frm.stepTheBall();
}
}
}
package BouncingBallApp.src.copy;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;
import javax.swing.JFrame;
public class MyFrame extends JFrame {
public final int FRAMEWIDTH = 600;
public final int FRAMEHEIGHT = 400;
private Ball[] ballArr;
private Random random =new Random ();
private Color[] colors={Color.RED,Color.blue,Color.yellow};
private int ballCnt;
public MyFrame(int ballCnt){
super();
setSize(FRAMEWIDTH, FRAMEHEIGHT);
setTitle("My Bouncing Ball Application");
ballArr = new Ball[ballCnt];
this.ballCnt = ballCnt;
int c;
for (int i=0; i < ballCnt; i++){
int bcn =random.nextInt(colors.length);
Color ballcolor=colors[bcn];
ballArr[i] = new Ball(new Point(50,50),c=(int) (Math.random()*10+3)%8,ballcolor);
int ddx = (int) (Math.random()*10+2)%8;
int ddy = (int) (Math.random()*10+2)%8;
ballArr[i].setMotion(ddx, ddy);
//c++;
}
}
public void paint(Graphics g){
super.paint(g);
for (int i=0; i < ballCnt; i++){
ballArr[i].paint(g);
}
}
public void stepTheBall(){
for (int i=0; i < ballCnt; i++){
ballArr[i].move();
Point loc = ballArr[i].getLocation();
if (loc.x < ballArr[i].getRadius() ||
loc.x > FRAMEWIDTH-ballArr[i].getRadius()){
ballArr[i].reclectVert();
}
if (loc.y < ballArr[i].getRadius() ||
loc.y > FRAMEHEIGHT-ballArr[i].getRadius()){
ballArr[i].reclectHoriz();
}
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
评论
没有解释的代码几乎一文不值。
–user1803551
16-2-3在20:51
评论
您是否可以更改Ball的构造函数参数?是的,我只需要绘制多个弹跳球,这是唯一的要求。
这不会回答您的问题,但是我建议您更改构造函数的参数,使其仅接受Color对象而不是String,并且必须使用该荒谬的if-else语句对其进行解析。 (即public Ball(Color ballColor,...){color = ballColor; ...}
谢谢,很好的建议。将其添加到我的代码中。
另请参见此处的示例。