Java 如何在一个JPanel上绘制同一类的多个对象?
我是一名大学生,我的作业有问题。通常情况下,我会去实验室问助教,但他病了整整一周,所以我们没有任何实验室时间,而这项作业将在周一到期 我遇到的具体问题与创建一个java应用程序有关,该应用程序显示一个带有按钮的帧,允许用户创建一个开始在屏幕上反弹并从帧边界反弹的球 上一个作业中的一个练习是创建一个类似的程序,但当运行时,会立即显示一个球反弹。(我开始工作)现在,我们必须修改代码并加入允许我们创建多个球的按钮 起初,我认为这是一个简单的修改,但现在我对如何实际实例化Ball对象感到困惑。我的思考过程是,我首先必须让ReboardPanel和button panel出现(这很有效),然后每当用户按下按钮时,一个新的Ball对象就会被实例化并显示在ReboardPanel上。(目前不起作用) 谢谢大家的帮助 主程序:Java 如何在一个JPanel上绘制同一类的多个对象?,java,swing,graphics,Java,Swing,Graphics,我是一名大学生,我的作业有问题。通常情况下,我会去实验室问助教,但他病了整整一周,所以我们没有任何实验室时间,而这项作业将在周一到期 我遇到的具体问题与创建一个java应用程序有关,该应用程序显示一个带有按钮的帧,允许用户创建一个开始在屏幕上反弹并从帧边界反弹的球 上一个作业中的一个练习是创建一个类似的程序,但当运行时,会立即显示一个球反弹。(我开始工作)现在,我们必须修改代码并加入允许我们创建多个球的按钮 起初,我认为这是一个简单的修改,但现在我对如何实际实例化Ball对象感到困惑。我的思考过
import java.awt.*;
public class Rebound {
public static void main(String[] args) {
JFrame frame = new JFrame ("Rebound");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
JPanel reboundPanel = new ReboundPanel();
JPanel buttonPanel = new ButtonPanel();
frame.getContentPane().add(reboundPanel);
frame.getContentPane().add(buttonPanel);
frame.pack();
frame.setVisible(true);
}
}
应显示球的面板:
import java.awt.*;
public class ReboundPanel extends JPanel {
private final int WIDTH = 400, HEIGHT = 300;
public ReboundPanel() {
setPreferredSize (new Dimension(WIDTH, HEIGHT));
setBackground (Color.black);
}
}
按钮面板:
import java.awt.*;
public class ButtonPanel extends JPanel {
private final int WIDTH = 400, HEIGHT = 35;
public ButtonPanel() {
setPreferredSize (new Dimension(WIDTH, HEIGHT));
setBackground (Color.GRAY);
JButton button = new JButton("New ball");
add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
new Ball();
}
});
}
}
球类:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Ball extends JPanel {
private final int DELAY = 20, IMAGE_SIZE = 35;
private ImageIcon image;
private Timer timer;
private int x, y, moveX, moveY;
public Ball() {
timer = new Timer(DELAY, new ReboundListener());
image = new ImageIcon ("/src/pa1/images/earth.gif");
x = 0;
y = 40;
moveX = moveY = 3;
draw(null);
timer.start();
}
public void draw(Graphics page) {
super.paintComponent (page);
image.paintIcon (new ReboundPanel(), page, x, y);
}
private class ReboundListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
x += moveX;
y += moveY;
if (x <= 0 || x >= WIDTH-IMAGE_SIZE)
moveX = moveX * -1;
if (y <= 0 || x >= WIDTH-IMAGE_SIZE)
moveY = moveY * -1;
repaint();
}
}
}
import java.awt.*;
导入java.awt.event.*;
导入javax.swing.*;
公共班级舞会{
私有最终整数延迟=20,图像大小=35;
私有图像图标图像;
私人定时器;
私有整数x,y,moveX,moveY;
公共舞会{
计时器=新计时器(延迟,新侦听器());
image=newimageicon(“/src/pa1/images/earth.gif”);
x=0;
y=40;
moveX=moveY=3;
抽签(空);
timer.start();
}
公共空白绘图(图形页){
super.paintComponent(第页);
image.paintIcon(新面板(),第页,x,y);
}
私有类侦听器实现ActionListener{
已执行的公共无效操作(操作事件){
x+=moveX;
y+=moveY;
如果(x=宽度图像大小)
moveX=moveX*-1;
if(y=宽度-图像大小)
moveY=moveY*-1;
重新油漆();
}
}
}
我让篮板面板负责告诉球移动和绘画——它有一个计时器和一个所有球的数组列表。我将更改标记为response.java和ButtonPanel.java。另外两个变化太大了,没有任何意义
java
import java.awt.*;
import javax.swing.*;
public class Rebound {
public static void main(String[] args) {
JFrame frame = new JFrame ("Rebound");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
ReboundPanel reboundPanel = new ReboundPanel(); /****/
JPanel buttonPanel = new ButtonPanel(reboundPanel); /****/
frame.getContentPane().add(reboundPanel);
frame.getContentPane().add(buttonPanel);
frame.pack();
frame.setVisible(true);
}
}
java
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;
public class ReboundPanel extends JPanel {
private final int WIDTH = 400, HEIGHT = 300;
private final int DELAY = 20;
private ArrayList<Ball> balls;
private Timer timer;
public ReboundPanel() {
balls = new ArrayList<Ball>();
timer = new Timer(DELAY, new ReboundListener());
setPreferredSize (new Dimension(WIDTH, HEIGHT));
setBackground (Color.black);
timer.start();
}
public void addBall(Ball b) {
balls.add(b);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Ball b : balls) {
b.paint(g);
}
}
private class ReboundListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
for (Ball b : balls) {
b.move(getWidth(), getHeight());
}
repaint();
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonPanel extends JPanel {
private final int WIDTH = 400, HEIGHT = 35;
public ButtonPanel(final ReboundPanel panel) { /****/
setPreferredSize (new Dimension(WIDTH, HEIGHT));
setBackground (Color.GRAY);
JButton button = new JButton("New ball");
add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
panel.addBall(new Ball()); /****/
}
});
}
}
Ball.java
import javax.swing.*;
import java.awt.*;
public class Ball {
private final int IMAGE_SIZE = 15;
private int x, y, moveX, moveY;
private ImageIcon image;
public Ball() {
x = 0;
y = 40;
moveX = moveY = 3;
image = new ImageIcon("/src/pa1/images/earth.gif");
}
public void move(int width, int height) {
x += moveX;
y += moveY;
if (x <= 0 || x >= width - IMAGE_SIZE)
moveX = moveX * -1;
if (y <= 0 || y >= height - IMAGE_SIZE)
moveY = moveY * -1;
}
public void paint(Graphics g) {
image.paintIcon(null, g, x, y);
}
}
import javax.swing.*;
导入java.awt.*;
公共班级舞会{
私有最终整数图像_大小=15;
私有整数x,y,moveX,moveY;
私有图像图标图像;
公共舞会{
x=0;
y=40;
moveX=moveY=3;
image=newimageicon(“/src/pa1/images/earth.gif”);
}
公共空白移动(整数宽度、整数高度){
x+=moveX;
y+=moveY;
如果(x=宽度-图像大小)
moveX=moveX*-1;
如果(y=高度-图像大小)
moveY=moveY*-1;
}
公共空间涂料(图g){
图像.paintIcon(null,g,x,y);
}
}
我让篮板面板负责告诉球移动和绘画——它有一个计时器和一个所有球的数组列表。我将更改标记为response.java和ButtonPanel.java。另外两个变化太大了,没有任何意义
java
import java.awt.*;
import javax.swing.*;
public class Rebound {
public static void main(String[] args) {
JFrame frame = new JFrame ("Rebound");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
ReboundPanel reboundPanel = new ReboundPanel(); /****/
JPanel buttonPanel = new ButtonPanel(reboundPanel); /****/
frame.getContentPane().add(reboundPanel);
frame.getContentPane().add(buttonPanel);
frame.pack();
frame.setVisible(true);
}
}
java
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;
public class ReboundPanel extends JPanel {
private final int WIDTH = 400, HEIGHT = 300;
private final int DELAY = 20;
private ArrayList<Ball> balls;
private Timer timer;
public ReboundPanel() {
balls = new ArrayList<Ball>();
timer = new Timer(DELAY, new ReboundListener());
setPreferredSize (new Dimension(WIDTH, HEIGHT));
setBackground (Color.black);
timer.start();
}
public void addBall(Ball b) {
balls.add(b);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Ball b : balls) {
b.paint(g);
}
}
private class ReboundListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
for (Ball b : balls) {
b.move(getWidth(), getHeight());
}
repaint();
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonPanel extends JPanel {
private final int WIDTH = 400, HEIGHT = 35;
public ButtonPanel(final ReboundPanel panel) { /****/
setPreferredSize (new Dimension(WIDTH, HEIGHT));
setBackground (Color.GRAY);
JButton button = new JButton("New ball");
add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
panel.addBall(new Ball()); /****/
}
});
}
}
Ball.java
import javax.swing.*;
import java.awt.*;
public class Ball {
private final int IMAGE_SIZE = 15;
private int x, y, moveX, moveY;
private ImageIcon image;
public Ball() {
x = 0;
y = 40;
moveX = moveY = 3;
image = new ImageIcon("/src/pa1/images/earth.gif");
}
public void move(int width, int height) {
x += moveX;
y += moveY;
if (x <= 0 || x >= width - IMAGE_SIZE)
moveX = moveX * -1;
if (y <= 0 || y >= height - IMAGE_SIZE)
moveY = moveY * -1;
}
public void paint(Graphics g) {
image.paintIcon(null, g, x, y);
}
}
import javax.swing.*;
导入java.awt.*;
公共班级舞会{
私有最终整数图像_大小=15;
私有整数x,y,moveX,moveY;
私有图像图标图像;
公共舞会{
x=0;
y=40;
moveX=moveY=3;
image=newimageicon(“/src/pa1/images/earth.gif”);
}
公共空白移动(整数宽度、整数高度){
x+=moveX;
y+=moveY;
如果(x=宽度-图像大小)
moveX=moveX*-1;
如果(y=高度-图像大小)
moveY=moveY*-1;
}
公共空间涂料(图g){
图像.paintIcon(null,g,x,y);
}
}
考虑应用程序元素之间的关系,让它们帮助形成类和对象设计
说明:
应用程序将有一个窗口,其中包含一个按钮和一个容器区域,用于容纳0个或多个球。
单击按钮时,应将新球添加到容器中。
球应该以固定的速度围绕容器移动,并从容器的边界反弹
设计:
这个描述告诉了我们很多关于如何构造代码的内容。
我们有一些名词:(应用程序)、窗口、按钮、容器、边界和球。
我们有一些动词:(have,contains,hold,add),move[ball],bounce[ball],click[button]
这些名词暗示了可能要实现的类。以及在关联类中实现的可能方法的谓词
让我们创建一个类来表示窗口并将其称为response,一个类表示容器BallPanel,另一个类表示球ball。在此上下文中,可以认为窗口和应用程序是相同的。事实证明,该按钮可以灵活地实现,而无需为其创建单独的类。边界很简单,可以用整数表示
上面我刚刚解释了一种方法来帮助澄清问题,下面我将提供一种可能的实现。这些旨在提供一些指导性提示,帮助您理解。您可以通过多种方法分析此问题或实施解决方案,我希望这些方法对您有所帮助
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Rebound extends JFrame {
/* Milliseconds between each time balls move */
static final int MOVE_DELAY = 20;
/* The JButton for adding a new ball. An AbstractAction
* provides a neat way to specify the label and on-click
* code for the button inline */
JButton addBallButton = new JButton(new AbstractAction("Add ball") {
public void actionPerformed(ActionEvent e) {
ballContainer.addBall();
}
});
/* The Panel for holding the balls. It will need to
* keep tracks of each ball, so we'll make it a subclass
* of JPanel with extra code for the ball management (see
* the definition, after the end of the Rebound class) */
BallPanel ballContainer = new BallPanel();
public Rebound() {
super("Rebound");
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
/* There was no neat way to specify the button size
* when we declared it, so let's do that now */
addBallButton.setPreferredSize(new Dimension(400, 35));
/* Add the components to this window */
getContentPane().add(addBallButton);
getContentPane().add(ballContainer);
pack();
/* Create a timer that will send an ActionEvent
* to our BallPanel every MOVE_DELAY milliseconds */
new Timer(MOVE_DELAY, ballContainer).start();
}
/* The entry point for our program */
public static void main(String[] args) {
/* We use this utility to ensure that code
* relating to Swing components is executed
* on the correct thread (the Swing event
* dispatcher thread) */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Rebound().setVisible(true);
}
});
}
}
/* Our subclass of JPanel that also manages a list of
* balls. It implements ActionListener so that it can
* act on the Timer event we set up in the Rebound class */
class BallPanel extends JPanel implements ActionListener {
/* An automatically expanding list structure that can
* contain 0 or more Ball objects. We'll create a Ball
* class to manage the position, movement and draw code
* for each ball. */
List<Ball> balls = new ArrayList<Ball>();
/* Let's add some code that will be run
* when the panel is resized (which will happen
* if its window is resized.) We need to make sure
* that each Ball is told about the new bounds
* of the component, so it knows that the place
* where it should bounce has changed */
public BallPanel() {
super();
setPreferredSize(new Dimension(400,300));
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (BallPanel.this == e.getComponent()) {
for (Ball ball : balls) {
ball.setBounds(getWidth(), getHeight());
}
}
}
});
}
/* This method is part of the JPanel class we are subclassing.
* Here we change the implementation of the method, ensuring
* we call the original implementation so that we are only
* adding to what it does. */
public void paintComponent(Graphics g) {
/* Call the original implementation of this method */
super.paintComponent(g);
/* Lets draw a black border around the bounds of the component
* to make it clear where the balls should rebound from */
g.drawRect(0,0,getWidth(),getHeight());
/* Now lets draw all the balls we currently have stored in
* our list. */
for (Ball ball : balls) {
ball.draw(g);
}
}
/* This method will add a new Ball into our list. Remember
* from earlier that we call this when our button is clicked. */
public void addBall() {
balls.add(new Ball(this,10,10,getWidth(),getHeight()));
}
/* This method will receive the event from Timer we set up in
* the Rebound class. We want it to cause all the ball to
* move to their next position. */
public void actionPerformed(ActionEvent e) {
for(Ball ball : balls) {
ball.move();
}
/* Request that Swing repaints this JPanel. This should
* cause the paintComponent() method we implemented
* above to be called soon after. */
repaint();
}
}
/* This is our class for keeping track of an individual ball
* and it's position, movement and how it is drawn. */
class Ball {
/* Let's say all balls will have the same diameter of 35.
* The static modifier says that this is a value
* that is shared by all instances of Ball. */
static final int SIZE = 35;
/* Let's say all balls will have a speed in both the X and Y
* axes of 3. The static modifier says that this is a value
* that is shared by all instances of Ball. */
static final int SPEED = 3;
/* Each ball needs to know its position, which we will store
* as x and y coordinates in 2D space */
int x, y;
/* Each ball needs to know the bounds in which it lives, so
* it knows when to bounce. We'll be assuming the minimum
* bound is 0,0 in 2D space. The maximum bound will be
* maxX,mayY in 2D space. We could have made these static
* and shared by all balls, but that means we would have
* to remember to change them to not be static if in the
* future we wanted Ball to be used on more than one JPanel.
* If we didn't remember, then we'd see some buggy behaviour. */
int maxX, maxY;
/* Each ball needs to know its current speed in the X and Y
* directions. We can use positive and negative values to
* keep track of the direction of the ball's movement. */
int speedX = SPEED, speedY = SPEED;
/* Each ball needs to know which panel it is being drawn to
* (this is needed by ImageIcon#drawImage()). */
JPanel panel;
public Ball(JPanel panel, int x, int y, int maxX, int maxY) {
this.x = x; this.y = y;
this.maxX = maxX; this.maxY = maxY;
this.panel = panel;
}
public void setBounds(int maxX, int maxY) {
this.maxX = maxX; this.maxY = maxY;
}
/* This method updates the position of this ball, using
* the current speed and bounds to work out what the new
* position should be.
* This should be called by our BallPanel#actionPerformed()
* method in response to the Timer we set up in the Rebound
* class. */
public void move() {
x += speedX;
y += speedY;
// Approx bounce, okay for small speed
if (x<0) { speedX=-speedX; x=0; }
if (y<0) { speedY=-speedY; y=0; }
if (x+SIZE>maxX) { speedX=-speedX; x=maxX-SIZE; }
if (y+SIZE>maxY) { speedY=-speedY; y=maxY-SIZE; }
}
/* This method is responsible for drawing this ball on
* the provided graphics context (which should come from
* the JPanel associated with the ball). We also have
* the panel, should we need it (ImageIcon#drawImage() needs
* this, but Graphics#drawOval() does not.)
*/
public void draw(Graphics g) {
//image.paintIcon(panel, g, x, y); - commented out because I don't have an ImageIcon
g.drawOval(x, y, SIZE, SIZE);
}
}
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.ComponentAdapter;
导入java.awt.event.ComponentEvent;
导入java.util.ArrayList;
导入java.util.List;
导入javax.swing.AbstractAction;
导入javax.swing.BoxLayout;
导入javax.swing.JButton;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.SwingUtilities;
导入javax.swing.Timer;
公共类Rebo