2012-02-17 56 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

相關問題