未将线绘制到 JPanel
Line isn't being drawn to JPanel
这是一个动画,其中给定数量的图标从画面的左侧开始,并在屏幕上快速移动到右侧。每个图标都绘制到其自己的 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;
}
}
}
}
}
Each icon is drawn to its own JPanel that fills a row within a container's single-column GridLayout
首先你需要学习如何进行自定义绘画。首先阅读 Swing 教程中关于 Custom Painting 的部分,了解一些基础知识。几个关键点:
您需要覆盖 getPreferredSize() 方法以使组件具有首选大小,以便布局管理器可以完成其工作。如果您不指定首选大小,则大小可能为零,因此没有可绘制的内容。
一种绘画方法,仅作绘画之用。永远不要在绘制方法中修改组件的状态,因为您无法控制 Swing 何时重新绘制组件。所以你需要像setPositionX(...)
这样的方法来控制应该绘制的图像位置。然后在线程(或计时器)中调用此方法来更改位置并在组件上调用 repaint()。
A finish line is also supposed to get painted onto the container, but it is not showing up
好吧,您将所有 Racer 组件添加到比赛面板的顶部,这样这些组件将覆盖面板上绘制的线条。
您已获得一种使用方法 racer.setOpaque(false)
;
另一种方法是覆盖 paint() 方法。 (这是在 paintComponent() 方法中进行自定义绘制的一般规则的例外)。如果您阅读了我提供的教程 link,您会发现使用这种方法会导致在绘制完所有 Racer 组件后才完成比赛面板的绘制。
frame.setResizable(false); // prevents window resizing which affects painting
没有这个必要。绘画受到影响的唯一原因是因为您正在更改绘画方法中组件的状态。请参阅我上面的评论。
racerImg.paintIcon(this, g, posX, 0);
人们通常使用 drawImage(...) 方法来绘制图像。没有理由创建一个图标只是为了绘制图像。
racerImg = new ImageIcon("races.png");
不要使用 ImageIcon 来读取文件。使用 ImageIO.read(...) 读取图像。然后按照上面描述的方式绘制图像。
and each JPanel races on its own thread.
这似乎是一个愚蠢的要求,因为它给比赛带来了另一种随机性。你已经有了为图像移动生成随机距离的逻辑,那么为什么你需要单独的线程呢。相反,您应该只有一个线程,然后遍历所有 Races 并调用上面建议的 setPositionX()
方法。然后所有种族将同时重新绘制,并有自己的随机距离变化。
编辑:
综上所述,您的代码只需更改一下就可以正常工作:
posX = 0;
setOpaque(false);
您只需要在其构造函数中使 Racer 透明即可。
因为这似乎是一项学校作业,我想你必须遵守规则,但你真的应该理解作业的问题。
正如这篇文章中的其他人所建议的那样,我仍然认为更好的方法是拥有一个可以绘制的 ArrayList 或 Racer 对象(不是组件)。在你关于这个主题的最后一个问题中,我给了你一个绘制 Ball 对象的工作示例。
这是一个动画,其中给定数量的图标从画面的左侧开始,并在屏幕上快速移动到右侧。每个图标都绘制到其自己的 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;
}
}
}
}
}
Each icon is drawn to its own JPanel that fills a row within a container's single-column GridLayout
首先你需要学习如何进行自定义绘画。首先阅读 Swing 教程中关于 Custom Painting 的部分,了解一些基础知识。几个关键点:
您需要覆盖 getPreferredSize() 方法以使组件具有首选大小,以便布局管理器可以完成其工作。如果您不指定首选大小,则大小可能为零,因此没有可绘制的内容。
一种绘画方法,仅作绘画之用。永远不要在绘制方法中修改组件的状态,因为您无法控制 Swing 何时重新绘制组件。所以你需要像
setPositionX(...)
这样的方法来控制应该绘制的图像位置。然后在线程(或计时器)中调用此方法来更改位置并在组件上调用 repaint()。
A finish line is also supposed to get painted onto the container, but it is not showing up
好吧,您将所有 Racer 组件添加到比赛面板的顶部,这样这些组件将覆盖面板上绘制的线条。
您已获得一种使用方法 racer.setOpaque(false)
;
另一种方法是覆盖 paint() 方法。 (这是在 paintComponent() 方法中进行自定义绘制的一般规则的例外)。如果您阅读了我提供的教程 link,您会发现使用这种方法会导致在绘制完所有 Racer 组件后才完成比赛面板的绘制。
frame.setResizable(false); // prevents window resizing which affects painting
没有这个必要。绘画受到影响的唯一原因是因为您正在更改绘画方法中组件的状态。请参阅我上面的评论。
racerImg.paintIcon(this, g, posX, 0);
人们通常使用 drawImage(...) 方法来绘制图像。没有理由创建一个图标只是为了绘制图像。
racerImg = new ImageIcon("races.png");
不要使用 ImageIcon 来读取文件。使用 ImageIO.read(...) 读取图像。然后按照上面描述的方式绘制图像。
and each JPanel races on its own thread.
这似乎是一个愚蠢的要求,因为它给比赛带来了另一种随机性。你已经有了为图像移动生成随机距离的逻辑,那么为什么你需要单独的线程呢。相反,您应该只有一个线程,然后遍历所有 Races 并调用上面建议的 setPositionX()
方法。然后所有种族将同时重新绘制,并有自己的随机距离变化。
编辑:
综上所述,您的代码只需更改一下就可以正常工作:
posX = 0;
setOpaque(false);
您只需要在其构造函数中使 Racer 透明即可。
因为这似乎是一项学校作业,我想你必须遵守规则,但你真的应该理解作业的问题。
正如这篇文章中的其他人所建议的那样,我仍然认为更好的方法是拥有一个可以绘制的 ArrayList 或 Racer 对象(不是组件)。在你关于这个主题的最后一个问题中,我给了你一个绘制 Ball 对象的工作示例。