绘制Runnable JPanel

时间:2013-08-19 19:11:13

标签: java multithreading user-interface paintcomponent repaint

我正在研究这款小型赛马模拟器并且坚持使用它。我希望用户首先选择比赛中的马匹数量(2-6),然后单击“开始”按钮。然后,我想绘制/绘制赛道和马(由圆圈表示)。出于某种原因,当代码到达创建Horse实例的点时,它永远不会被绘制到框架中。 下面是代码。我错过了什么?

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 个答案:

答案 0 :(得分:6)

要获得更好的设计,请不要为应用程序使用null布局。使用框架的默认BorderLayout。

  1. 创建一个包含所有单选按钮的JPanel,并将该面板添加到框架的PAGE_START。
  2. 将JButton添加到PAGE_END
  3. 创建一个RaceTrackPane并将您的Horses添加到此面板。此面板可以使用空布局,因为您将移动马匹。此面板已添加到CENTER。
  4. 代码的问题在于如何定义组件的边界并执行自定义绘制:

    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作为您的Horse组件,这样您就不必进行自定义绘画并担心所有这些。要获得更高级(但可能更灵活)的解决方案,请查看Playing With Shapes

答案 1 :(得分:3)

  

我错过了什么?

您缺少数据模型。你正试图在视图中做所有事情。

该视图用于显示模型中的数据。

你的Horse课程看起来应该更像这样:

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

}

该类中的最后一个方法是draw。创建动画时,如果对象自行绘制,则会更容易。

这是一个种族课程。

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匹马。视图不会改变。

仅模型更改。

这应该是足以帮助您入门的信息。