如何将同一类的多个对象绘制到一个JPanel上? [英] How To Paint Multiple Objects Of Same Class Onto One JPanel?

查看:166
本文介绍了如何将同一类的多个对象绘制到一个JPanel上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名大学生,我在分配任务时遇到麻烦。通常情况下,我只是去实验室去问问技术人员,但他整个星期都病了,所以我们没有任何实验时间,这个任务将在周一到期!



我遇到的具体问题与创建一个Java应用程序有关,该应用程序显示带有按钮的框架,允许用户创建一个在屏幕上弹开并反弹帧边界的球。



以前任务中的一个练习是创建一个类似的程序,但运行时会立即显示ONE ball bouncing。 (我开始工作)现在,我们必须修改我们的代码并加入允许我们创建多个球的按钮。



起初,我认为这将是一个简单的修改,但现在我对如何真正实例化Ball对象感到困惑。我的思考过程是我首先必须使ReboundPanel和按钮面板出现(工作),然后每当用户按下按钮时,一个新的Ball对象被实例化并显示在ReboundPanel上。 (目前无效)



感谢所有人的帮助!

主程序: p>

  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);


$ b

按钮面板:

  import java.awt。*; 

public class ButtonPanel extends JPanel {

private final int WIDTH = 400,HEIGHT = 35;
$ b $ 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();

}
});


$ / code $ / pre



  import java.awt。*; 
import java.awt.event。*;
import javax.swing。*;

public class Ball继承JPanel {

private final int DELAY = 20,IMAGE_SIZE = 35;

私人ImageIcon图片;
私人定时器定时器;
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);

$ b $ private class ReboundListener实现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();




解决方案

请考虑应用程序元素的关系,并让它帮助您形成类和对象设计。



描述:

应用程序将有一个窗口,其中包含一个按钮和一个容纳0个或更多球的容器区域。
点击按钮时,应该在容器中添加一个新球。
球应该以固定的速度在容器周围移动并从容器边界反弹。

设计:

这个描述告诉我们很多关于如何构建我们的代码。
我们有一些名词:(应用程序),窗口,按钮,容器,边界和球。
我们有一些动词:(有,包含,保持,添加),移动[球],反弹[球],点击[按钮]。

名词暗示可能的类来实现。



让我们创建一个类来表示窗口并将其称为Rebound,这是一个表示名为BallPanel的容器的类,一个代表球的球员称为球。在这种情况下,窗口和应用程序可以被认为是相同的。事实证明,该按钮可以实现整洁,而无需为其创建单独的类。上面我刚刚解释了一种方法来帮助澄清问题,下面我将提供一种可能的实现方式。这些旨在提供一些教学提示来帮助您理解。有很多方法可以分析这个问题或实现解决方案,我希望你能找到这些帮助。

  import java.awt。尺寸; 
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;
导入javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Rebound extends JFrame {
/ *每次移动球之间的毫秒数* /
static final int MOVE_DELAY = 20;

/ * JButton添加一个新球。一个AbstractAction
*提供了一个简洁的方式来指定标签并点击
*代码按钮内联* /
JButton addBallButton = new JButton(new AbstractAction(Add ball){
public void actionPerformed(ActionEvent e){
ballContainer.addBall();
}
});

/ *小组举行球。它需要
*保存每个球的轨迹,所以我们将它作为JPanel的一个子类
*,并为球管理添加额外的代码(参见
*定义,在结束之后的回弹类)* /
BallPanel ballContainer = new BallPanel();

public Rebound(){
super(Rebound);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane()。setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));

/ *当我们声明它时,没有简单的方法来指定按钮大小
*,所以让我们现在就这样做* /
addBallButton.setPreferredSize(new Dimension(400, 35));
$ b $ / *将组件添加到此窗口* /
getContentPane()。add(addBallButton);
getContentPane()。add(ballContainer);

pack();
$ b $ *创建一个计时器,它将每MOVE_DELAY毫秒发送一个ActionEvent
*到我们的BallPanel * /
新计时器(MOVE_DELAY,ballContainer).start();

$ b $ *我们程序的入口点* /
public static void main(String [] args){
/ *我们使用这个工具来保证(Swing事件
* dispatcher线程)* /
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new Rebound()。setVisible(true);
}
});
}
}

/ *我们的JPanel的子类还管理
*球的列表。它实现了ActionListener,使它可以在我们在Rebound类中设置的Timer事件上作用
* * / b $ b class BallPanel extends JPanel implements ActionListener {
/ *自动扩展列表结构
*包含0个或更多的Ball对象。我们将创建一个Ball
*类来管理每个球的位置,移动和绘制代码
*。 * /
列表<球> balls = new ArrayList< Ball>();
/ *让我们添加一些代码,当面板调整大小后,将会运行
*(如果窗口大小调整,会发生
*)。我们需要确保
*每个球被告知组件的新边界
*,所以它知道应该反弹的地方
*已经改变* /
public BallPanel(){
超();
setPreferredSize(新维(400,300));
addComponentListener(new ComponentAdapter(){
public void componentResized(ComponentEvent e){
if(BallPanel.this == e.getComponent()){
for(Ball ball:球){
ball.setBounds(getWidth(),getHeight());
}
}
}
});
}

/ *这个方法是我们继承的JPanel类的一部分。
*在这里我们改变方法的实现,确保
*我们称之为原始实现,以便我们只增加
*来增加它的功能。 * /
public void paintComponent(Graphics g){
/ *调用此方法的原始实现* /
super.paintComponent(g);

/ *让我们在组件
*的边界附近绘制一个黑色边框,以清楚球应该从* /
g.drawRect(0,0,的getWidth(),的getHeight());

/ *现在让我们画出我们目前储存在
*列表中的所有球。 * /
(球球:球){
ball.draw(g);
}
}
/ *这种方法将在我们的列表中添加一个新球。请记住早些时候我们在点击按钮时调用此函数的
*。 * /
public void addBall(){
balls.add(new Ball(this,10,10,getWidth(),getHeight()));
}
/ *这个方法将从Timer中接收事件,我们在
*中设置了Rebound类。我们希望它能够让所有的球到
*移动到他们的下一个位置。 * /
public void actionPerformed(ActionEvent e){
for(Ball ball:balls){
ball.move();
}
/ *请求Swing重新绘制这个JPanel。这应该是
*导致我们实现
*的paintComponent()方法在不久之后被调用。 * /
repaint();
}
}
/ *这是我们的课程,用于跟踪单个球
*以及它的位置,移动方式以及它如何绘制。 * /
class Ball {
/ *假设所有的球将具有相同的直径35.
*静态修饰符表示这是一个值
* Ball的所有实例。 * /
static final int SIZE = 35;
/ *假设所有球在X和Y
*轴上的速度都为3.静态修饰符表示这是一个值
*,它被所有的实例共享球。 * /
static final int SPEED = 3;
/ *每个球需要知道它的位置,我们将把
*存储为2D空间中的x和y坐标* /
int x,y;
/ *每个球需要知道它所处的界限,所以
*知道何时反弹。我们将假设2D空间中的最小
* bound为0,0。最大边界将是
* maxX,可能在二维空间中。我们可以使这些静态
*并由所有球共享,但这意味着我们将有
*记住将它们更改为不是静态的,如果在我们希望的
*未来中Ball可用于多个JPanel。
*如果我们不记得,那么我们会看到一些错误行为。 * /
int maxX,maxY;
/ *每个球需要知道X和Y
*方向的当前速度。我们可以使用正值和负值来追踪球的运动方向。 * /
int speedX = SPEED,speedY = SPEED;
/ *每个球需要知道它被绘制到哪个面板
*(这是ImageIcon#drawImage()需要的)。 * /
JPanel面板;
public Ball(JPanel面板,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;
}
/ *此方法更新此球的位置,使用
*当前的速度和界限来计算新的
*位置应该是多少。
*这应该由我们的BallPanel#actionPerformed()
*方法调用,以响应我们在Rebound
*类中设置的Timer。 * /
public void move(){
x + = speedX;
y + = speedY;
//近似反弹,对于小速度可以
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; }
}
/ *这个方法负责在
*提供的图形上下文(它应该来自与球关联的JPanel的
*)上绘制这个球。我们也有
*面板,如果我们需要的话(ImageIcon#drawImage()需要
* this,但是Graphics#drawOval()不会。)
* /
public void draw(Graphics g){
//image.paintIcon(panel,g,x,y); - 注释掉了,因为我没有ImageIcon
g.drawOval(x,y,SIZE,SIZE);
}
}


I am a university student and I'm having trouble with my assignment. Normally, I would just go to the lab time and ask the TA, but he's been sick all week so we haven't had any lab time and this assignment is due on Monday!

the specific problem I have is related to creating a java application that displays a frame with a button allowing the user to create a ball that starts bouncing on the screen and rebounds off the frame boundaries.

One of the exercises from a previous assignment was to create a similar program but that when run, would immediately display ONE ball bouncing. (which I got to work) Now, we have to modify our code and incorporate the button which allows us to create multiple balls.

At first, I thought this would be an easy modification, but now I am getting confused about how to actually instantiated the Ball objects. My thought process is that I first have to make the ReboundPanel and button panel appear (which works), and then whenever the user presses the button, a new Ball object is instantiated and displayed onto the ReboundPanel. (which does not work at present)

thanks to all for the help!

Main program:

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);
}
}

Panel where ball should appear:

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);

}
}

Button Panel:

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(); 

        }
    });
}
}

Ball Class:

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();
    }
}
}

解决方案

Think about the relationship of the elements of your application and let that help form your class and object design.

Description:

The application will have a window that contains a button and a container area to hold 0 or more balls. When the button is clicked, a new ball should be added to the container. A ball should move around its container at a fixed speed and bounce off the boundaries of the container.

Design:

This description tells us a lot about how we could structure our code. We have some nouns: (application), window, button, container, boundaries and ball. We have some verbs: (have, contains, hold, add), move[ball], bounce[ball], click[button].

The nouns hint at possible classes to implement. And the verbs at possible methods to be implemented in the associated classes.

Let's create a class to represent the window and call it Rebound, a class representing the container called BallPanel and a class representing a ball called Ball. In this context the window and the application can be considered to be the same. It turns out that the button can be implemented neatly without creating a separate class for it. And the boundaries are simple enough to be represented by integers.

Above I have just explained one approach to help clarify the problem and below I will provide one possible implementation. These are intended to offer some instructional hints to help your understanding. There are many ways you could analyse this problem or implement the solution, I hope you find these helpful.

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);
    }
}

这篇关于如何将同一类的多个对象绘制到一个JPanel上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆