2012-02-17 43 views
0

我是一名大学生,我在分配任务时遇到了麻烦。通常情况下,我只想去实验室时间询问技术支持,但他整个星期都病了,所以我们没有任何实验时间,这项工作将在星期一开始!如何将同一类的多个对象绘制到一个JPanel上?

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

以前任务中的一个练习是创建一个类似的程序,但运行时会立即显示ONE ball bouncing。 (我开始工作)现在,我们必须修改我们的代码并合并允许我们创建多个球的按钮。起初,我认为这将是一个简单的修改,但现在我对如何真正实例化Ball对象感到困惑。我的思考过程是我首先必须使ReboundPanel和按钮面板出现(工作),然后每当用户按下按钮时,一个新的Ball对象被实例化并显示在ReboundPanel上。 (目前无效)

感谢各位的帮忙!

主程序:

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让球应该出现:

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

     } 
    }); 
} 
} 

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

按下按钮时,您调用Ball的构造函数,但不存储新对象的地址。你不应该保留对它的引用,以便垃圾收集器不会摆脱它吗?尝试制作一个球的矢量,并添加每个新的球。 – broncoAbierto 2012-02-17 11:46:01

+0

不知道你的意思是由矢量... 我对类设计感到困惑......什么类应该做什么? – Wangagat 2012-02-17 11:51:47

+0

[链接] http://docs.oracle.com/javase/6/docs/api/java/util/Vector.html矢量是一个动态的对象数组。它没有规定的大小限制(除了你的可用内存,我认为)。在ButtonPanel中创建这个Vector vectorOfBalls = new Vector ;并用vectorOfBalls.add(new Ball())添加球; – broncoAbierto 2012-02-17 12:06:25

回答

0

我让ReboundPanel负责告诉球移动并绘制–它有一个计时器和一个所有球的ArrayList。我标记了对Rebound.java和ButtonPanel.java的更改。另外两个变化很大,没有任何意义。

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

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

ButtonPanel.java

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

我认为用常规的绘图方法处理ReboundPanel中的绘图可能有助于闪烁。当然,它需要访问Ball对象。 – broncoAbierto 2012-02-17 13:10:59

+0

嗨汤姆,非常感谢您的帮助! 我复制了粘贴的所有代码,看看它是如何工作的,虽然我没有得到任何JVM错误,但是当我单击按钮时,屏幕上没有出现球。它适合你吗? – Wangagat 2012-02-17 14:19:05

+0

@Wangagat是的,它在我的电脑上工作(但闪烁)。我打算使用broncoAbierto的建议改进它,也许那么它也可以在你的电脑上运行。 – tom 2012-02-17 14:42:07

3

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

描述:

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

设计:

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

名词暗示可能的类来实现。以及在相关类中可能的方法的动词。

让我们创建一个类来表示窗口,并调用它的反弹,代表叫BallPanel和代表叫球球一类的容器类。在这种情况下,窗口和应用程序可以被认为是相同的。事实证明,该按钮可以实现整洁,而无需为其创建单独的类。而且边界很简单,可以用整数表示。

上面我刚刚解释了一种方法来帮助澄清问题,下面我将提供一种可能的实现。这些旨在提供一些教学提示来帮助您理解。有很多方法可以分析此问题或实施解决方案,我希望您能找到这些帮助。

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

嗨Fd! 哇,这么大的帮助!我非常感谢意见,以帮助我理解。我还有其他问题,想你可以给我发电子邮件吗? wangagat [at] gmail。感谢! – Wangagat 2012-02-19 11:51:54

相关问题