这是一个动画,其中给定数量的图标从框架的左侧开始,并在屏幕上向右侧比赛。每个图标都被绘制到自己的JPanel中,该JPanel填充容器的单列GridLayout中的一行,并且每个JPanel都在自己的线程上进行竞争。 (必须使用线程,即使Swing Timer可能是更好的方法。)
终点线也应该涂在容器上,但它没有出现。我尝试将Racer的JPanel不透明度设置为false,但这不起作用。我怎样才能让其他线程允许终点线的绘画执行?
无效的代码:
gui = new JPanel() {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(finishLineXPos, 0, finishLineXPos, windowHeight);
}
};
完整代码:
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Races2 {
private JFrame frame;
private JPanel gui; // to hold all components
private int finishLineXPos; // x-coordinate of finish line
private Icon racerImg;
private int racerImgWidth;
private int racerImgHeight;
private int numOfRacers;
private ArrayList<Racer> racers;
private Racer winner;
private int windowHeight;
private int windowWidth;
public Races(int num) {
numOfRacers = num;
racerImg = new ImageIcon("races.png");
racerImgWidth = racerImg.getIconWidth();
racerImgHeight = racerImg.getIconHeight();
windowHeight = racerImgHeight * numOfRacers;
windowWidth = racerImgWidth * 20;
finishLineXPos = racerImgWidth * 18; // two icon widths from the right
frame = new JFrame("Off to the Races - by Brienna Herold");
frame.setResizable(false); // prevents window resizing which affects painting
gui = new JPanel() {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(finishLineXPos, 0, finishLineXPos, windowHeight);
}
};
gui.setLayout(new GridLayout(numOfRacers,1));
gui.setPreferredSize(new Dimension(windowWidth, windowHeight));
// Create and add racers to gui panel
racers = new ArrayList<Racer>();
for (int i = 0; i < numOfRacers; i++) {
Racer racer = new Racer();
gui.add(racer);
racers.add(racer);
}
// Start racers
for (Racer racer : racers) {
Thread racerThread = new Thread(racer);
racerThread.start();
}
frame.add(gui);
frame.pack();
frame.setVisible(true);
}
protected class Racer extends JPanel implements Runnable {
private int lastPosX;
private int posX;
public Racer() {
posX = 0;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
racerImg.paintIcon(this, g, posX, 0);
posX += Math.random() * 20;
}
@Override
public void run() {
// While the race has not been won yet, proceed with race
while (winner == null) {
repaint();
try {
Thread.sleep(100); // slows down racing a bit
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// If racer passes specified x-coordinate, set it as winner
if (posX >= finishLineXPos) {
System.out.println("Winner: " + this.getName());
winner = this;
}
}
}
}
}
答案 0 :(得分:2)
每个图标都会绘制到自己的JPanel中,该JPanel会在容器的单列GridLayout
中填充一行
首先,您需要学习如何进行自定义绘画。首先阅读Custom Painting上Swing教程中的部分内容,了解一些基础知识。几个关键点:
您需要覆盖getPreferredSize()方法,使组件具有首选大小,以便布局管理器可以完成其工作。如果您没有指定首选尺寸,那么尺寸可能为零,因此无需绘画。
绘画方法仅适用于绘画。您不应该在绘制方法中修改组件的状态,因为您无法控制Swing重绘组件的时间。所以你需要像setPositionX(...)
这样的方法来控制它应该被绘制的图像位置。然后在Thread(或Timer)中调用此方法来更改位置并在组件上调用repaint()。
终点线也应该涂在容器上,但它没有出现
您将所有Racer组件添加到比赛面板的顶部,以便这些组件覆盖面板上绘制的线条。
您已获得一种使用racer.setOpaque(false)
;
另一种方法是覆盖paint()方法。 (这是在paintComponent()方法中进行自定义绘制的一般规则的例外)。如果您阅读我提供的教程链接,您将看到使用此方法将导致在绘制完所有Racer组件后绘制比赛面板。
frame.setResizable(false); // prevents window resizing which affects painting
没有必要这样做。绘画受影响的唯一原因是因为您正在更改绘制方法中组件的状态。请参阅上面的评论。
racerImg.paintIcon(this, g, posX, 0);
人们通常使用drawImage(...)方法绘制图像。没有理由创建一个Icon来绘制图像。
racerImg = new ImageIcon("races.png");
不要使用ImageIcon来读取文件。使用ImageIO.read(...)读取图像。然后如上所述绘制图像。
并且每个JPanel都在自己的线程上比赛。
这似乎是一个愚蠢的要求,因为它为比赛带来了不同的随机性。您已经有逻辑为图像生成随机距离,因此为什么需要单独的线程。相反,您应该只有一个Thread,然后遍历所有Races并调用上面建议的setPositionX()
方法。然后所有种族将同时重新粉刷,随机距离变化。
编辑:
说完以上所有内容后,只需一次更改,您的代码就可以正常工作:
posX = 0;
setOpaque(false);
你只需要让Racer透明化它的构造函数。
由于这似乎是学校作业,我猜你必须遵守规则,但你真的应该理解作业的问题。
正如本帖子中的其他人所建议的那样,我仍然相信更好的方法是拥有一个可以绘制的ArrayList或Racer对象(不是组件)。我在这个主题的最后一个问题中给了你一个绘制Ball对象的工作示例。