2013-08-19 63 views
1

我正在研究這個小型賽馬模擬器,並堅持下去。我希望用戶首先選擇比賽中的馬匹數量(2-6)並點擊「開始」按鈕。然後,我想繪製/繪製賽馬和馬匹(用圓圈表示)。出於某種原因,當代碼達到創建一個馬的實例的時候,它永遠不會被拖入框架。 以下是代碼。我錯過了什麼?繪製一個可運行的JPanel

Main.java:

import javax.swing.SwingUtilities; 

public class Main { 
    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() {  
      @Override 
      public void run() { 
       RaceTrack myRace = new RaceTrack(); 
       myRace.setVisible(true); 
      } 
     }); 
    } 
} 

RaceTrack.java:

import java.awt.BorderLayout; 
import java.awt.Container; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.BorderFactory; 
import javax.swing.ButtonGroup; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JRadioButton; 
import javax.swing.border.Border; 

public class RaceTrack extends JFrame implements Runnable { 
    public RaceTrack() { 
     initUI(); 
    } 
    public static int selectedRaceSize = 2; 
    private void initUI() { 
     final Container pane = getContentPane(); 
     String horseNum[] = { "2", "3", "4", "5", "6" }; 
     JPanel buttonPanel = new JPanel(); 
     Border border = BorderFactory.createTitledBorder("Please select number of horses:"); 
     buttonPanel.setBorder(border); 
     ButtonGroup buttonGroup = new ButtonGroup(); 
     JRadioButton aRadioButton; 
     // For each String passed in: 
     // Create button, add to panel, and add to group 
     for (int i = 0, n = horseNum.length; i < n; i++) { 
      if (i == 0) { 
       // Default selection 
       aRadioButton = new JRadioButton(horseNum[i], true); 
      } else { 
       aRadioButton = new JRadioButton(horseNum[i]); 
      } 
      buttonPanel.add(aRadioButton); 
      buttonGroup.add(aRadioButton); 
     } 

     pane.add(buttonPanel, BorderLayout.PAGE_START); 
     final JPanel raceTrackPanel = new JPanel(null); 
     final JButton startButton = new JButton("Start!"); 
     startButton.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent actionEvent) { 
       startButton.setEnabled(false); 
       Horse horse1 = new Horse("horse1"); 
       raceTrackPanel.add(horse1); 
       pane.add(raceTrackPanel, BorderLayout.CENTER); 
       repaint(); 
      } 
     }); 
     pane.add(startButton, BorderLayout.PAGE_END); 
     startButton.setBounds(50, 200, 300, 30); 

     setTitle("Horse Race v1.0"); 
     setSize(400, 300); 
     setResizable(false); 
     setLocationRelativeTo(null); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
    } 
    @Override 
    public void run() { 
      try { 
       Thread.sleep(50); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      repaint(); 
    } 
} 

Horse.java:

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 

import javax.swing.JPanel; 

@SuppressWarnings("serial") 
public class Horse extends JPanel implements Runnable { 
    Thread runner; 
    public Horse() { 
    } 
    public Horse(String threadName) { 
     runner = new Thread(this, threadName); 
     runner.start(); 
    } 
    public void run() { 
     this.repaint(); 
    } 
    @Override 
    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     Graphics2D g2d = (Graphics2D) g; 
     g2d.setColor(new Color(252, 211, 61)); 
     g2d.drawOval(20, 25, 10, 10); 
     g2d.fillOval(20, 25, 10, 10); 
    } 
} 

回答

2

我錯過了什麼?

您錯過了一個數據模型。你正試圖在視圖中做所有事情。

該視圖用於顯示模型中的數據。

你的馬類應該看起來更像是這樣的:

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Point; 
import java.util.Random; 

public class Horse { 

    public static final int RADIUS = 15; 
    public static final int MARGIN = 15; 
    public static final int DIAMETER = RADIUS + RADIUS; 
    public static final int POSITION = DIAMETER + MARGIN; 

    private static Point currentPosition; 

    static { 
     int x = MARGIN + RADIUS; 
     int y = MARGIN + RADIUS; 
     currentPosition = new Point(x, y); 
    } 

    private static Random random = new Random(); 


    /** Distance in pixels */ 
    private double distance; 

    /** Velocity in pixels per second */ 
    private int velocity; 

    private Color color; 

    /** Initial position in pixels */ 
    private Point initialPosition; 

    private String name; 

    public Horse(Color color, String name) { 
     setInitialPosition(); 
     this.color = color; 
     this.name = name; 
     init(); 
    } 

    private void setInitialPosition() { 
     this.initialPosition = 
       new Point(currentPosition.x, currentPosition.y); 
     currentPosition.y += POSITION; 
    } 

    public void init() { 
     this.distance = 0.0D; 
    } 

    public void setVelocity() { 
     this.velocity = random.nextInt(5) + 6; 
    } 

    public double getDistance() { 
     return distance; 
    } 

    public String getName() { 
     return name; 
    } 

    public void moveHorse(int milliseconds) { 
     double pixels = 0.001D * velocity * milliseconds; 
     this.distance += pixels; 
    } 

    public void draw(Graphics g) { 
     g.setColor(color); 
     g.fillOval(initialPosition.x + (int) Math.round(distance) - RADIUS, 
       initialPosition.y - RADIUS, DIAMETER, DIAMETER); 
    } 

} 

類中的最後一個方法是畫。創建動畫時,如果對象自己繪製,則會更容易。

這是Race類。

import java.awt.Color; 
import java.awt.Graphics; 
import java.util.ArrayList; 
import java.util.List; 

public class Race { 

    /** Distance of race in pixels */ 
    private double  distance; 

    private long  elapsedTime; 

    private List<Horse> horses; 

    public Race(double distance) { 
     this.distance = distance; 
     this.horses = new ArrayList<Horse>(); 
     this.elapsedTime = 0; 
    } 

    public void init() { 
     this.elapsedTime = 0; 
     for (Horse horse : horses) { 
      horse.init(); 
     } 
    } 

    public void addHorse(Horse horse) { 
     this.horses.add(horse); 
    } 

    public int getHorseCount() { 
     return horses.size(); 
    } 

    public double getDistance() { 
     return distance; 
    } 

    public void setElapsedTime(long elapsedTime) { 
     if (isWinner() == null) { 
      this.elapsedTime = elapsedTime; 
     } 
    } 

    public String getElapsedTime() { 
     int centiseconds = (int) (((elapsedTime % 1000L) + 5L)/10L); 
     int seconds = (int) (elapsedTime/1000L); 
     if (seconds < 60) { 
      return String.format("%2d.%02d", seconds, centiseconds); 
     } else { 
      int minutes = seconds/60; 
      seconds -= minutes * 60; 
      return String.format("%2d:%02d.%02d", minutes, seconds, 
        centiseconds); 
     } 
    } 

    public int getTrackWidth() { 
     return (int) Math.round(getDistance()) + 100; 
    } 

    public int getTrackHeight() { 
     return getHorseCount() * Horse.POSITION + Horse.MARGIN; 
    } 

    public void setHorseVelocity() { 
     for (Horse horse : horses) { 
      horse.setVelocity(); 
     } 
    } 

    public void updateHorsePositions(int milliseconds) { 
     for (Horse horse : horses) { 
      horse.moveHorse(milliseconds); 
     } 
    } 

    public Horse isWinner() { 
     for (Horse horse : horses) { 
      if ((distance - Horse.RADIUS) <= horse.getDistance()) { 
       return horse; 
      } 
     } 

     return null; 
    } 

    public boolean allHorsesRunning() { 
     for (Horse horse : horses) { 
      if ((distance + Horse.RADIUS + 6) > horse.getDistance()) { 
       return true; 
      } 
     } 

     return false; 
    } 

    public void draw(Graphics g) { 
     drawLine(g, Horse.POSITION, 6); 
     drawLine(g, (int) Math.round(getDistance()) + Horse.RADIUS 
       + Horse.MARGIN, 6); 

     for (Horse horse : horses) { 
      horse.draw(g); 
     } 
    } 

    private void drawLine(Graphics g, int x, int width) { 
     int y = Horse.MARGIN; 
     int height = getHorseCount() * Horse.POSITION - y; 
     g.setColor(Color.BLACK); 
     g.fillRect(x, y, width, height); 
    } 

} 

再次,繪製方法吸引了比賽。

那麼,您實際繪製的JPanel會是什麼樣子?

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 

import javax.swing.JPanel; 

import com.ggl.horse.race.model.Race; 

public class RacePanel extends JPanel { 

    private static final long serialVersionUID = 1040577191811714944L; 

    private Race race; 

    public RacePanel(Race race) { 
     this.race = race; 
     int width = race.getTrackWidth(); 
     int height = race.getTrackHeight(); 
     this.setPreferredSize(new Dimension(width, height)); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     drawBackground(g); 
     race.draw(g); 
    } 

    private void drawBackground(Graphics g) { 
     g.setColor(Color.WHITE); 
     g.fillRect(0, 0, getWidth(), getHeight()); 
    } 

} 

該視圖不關心是否有一匹馬,三匹馬或10匹馬。該視圖不會改變。

只有模型更改。

這應該是足夠的信息來幫助您入門。

+0

我改寫了我的根據你的建議完整的代碼。我從來沒有學過數據模型和視圖,所以我仍然試圖理解這裏發生了什麼。其中一個問題是:Race類也可以作爲我的框架嗎?如果不是,我應該爲它使用不同的類還是隻在我的Main類中創建它? – coyote1982

+0

@ coyote1982:你的JFrame存在可以容納TrackPanel,也可能是一個菜單。您的JFrame是視圖的一部分。種族是模型的一部分。您需要編寫控制器類來將模型和視圖連接在一起(一個或多個線程)。我看到了你的代碼中的線程,所以我想你可以做到這一點。看看這篇文章,看看如何把一個Swing GUI放在一起。 http://java-articles.info/articles/?p=196 –

+0

感謝你的精彩指南,在過去的幾天裏,我取得了很多進展。我的程序即將完成。我仍然必須編寫比賽結果對話框,但在開始之前,我遇到了一個問題:我無法弄清楚真正讓馬匹移動的正確方法。每匹馬都有自己的HorseRunnable模擬其隨機進展。我還創建了RaceRunnable,每隔50毫秒重新繪製一次TrackPanel(通過睡眠,然後重繪)。我可以向您發送該程序,以便我可以更好地解釋嗎? – coyote1982

6

爲了更好的設計,不使用空佈局你的申請。使用框架的默認邊框佈局。

  1. 使用所有單選按鈕創建一個JPanel並將該面板添加到您的框架的PAGE_START。
  2. 將JButton添加到PAGE_END
  3. 創建RaceTrackPane並將您的馬匹添加到此面板。此面板可以使用空白布局,因爲您將移動馬匹。該面板已添加到CENTER中。

與您的代碼的問題是你如何定義組件的邊界並做風俗畫:

horse1.setBounds(20, 120, 20, 20); 
... 
g2d.drawOval(20, 25, 10, 10); 
g2d.fillOval(20, 25, 10, 10); 

第一個問題是你的馬被定位在(20,120),所以他們將被繪製在另一個之上。

更大的問題是每匹馬的大小是(20,20)。當你做繪畫的時候,你在(20,25)處畫馬,因此它超出了你的組件的大小。嘗試使用(0,0,10,10)。那是你應該總是做你的繪畫相對於(0,0)的組件。然後通過更改組件的位置來移動組件。

我會考慮使用帶有Icon的JLabel作爲你的馬組件,所以你不必做自定義繪畫並且擔心所有這些。對於更先進(但可能更靈活)的解決方案,請查看Playing With Shapes

+0

謝謝你的回答!目前我只是想讓這匹馬出現在我的框架中,後來與其他馬匹交易。我已經用你的建議糾正了我的RaceTrack.java,但馬仍然沒有出現在框架中。我已經編輯了我的原始問題以包含新代碼...我很抱歉,我是新來的線程與圖形相結合,我不知道我在做什麼錯... – coyote1982