奇怪的 JFrame 行为
我有以下程序在运行时有一些非常奇怪和不需要的行为.它应该有两个按钮,开始"和停止",但是当我单击开始"时,另一个按钮会显示在开始"下方.这是我正在谈论的打印屏幕:
I have the following program which has some very strange and unwanted behavior when it runs. Its supposed to have two buttons, "Start" and "Stop, but when I click "Start" another button shows up right below "Start". Here's a print screen of what I'm talking about:
我做错了什么,我该如何解决这个丑陋的问题?
What am I doing wrong and how do I fix this ugly problem?
代码如下:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if(!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int)(Math.random()*256);
int blue = (int)(Math.random()*256);
int green = (int)(Math.random()*256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth()-wd);
int y = rand.nextInt(getHeight()-ht);
g.fillRect(x,y,wd,ht);
}
} // close inner class
}
我还试图让开始"按钮做两件事.一个当然是开始动画,但是当按下停止按钮并再次按下开始时,我希望它可以清理屏幕并重新开始动画.有什么建议吗?
Also I'm trying to get the Start button to do two things. One is to of course start the animation but when the Stop button is pressed and I press Start again, I want it to clean the screen so to speak and start the animation again a new. Any tips on that?
推荐答案
你没有在重写的 paintComponent(..)
方法中调用 super.paintComponent(Graphics g)
为了尊重 paint chain 以及其他组件的绘制,您应该这样做.
You do not call super.paintComponent(Graphics g)
in overriden paintComponent(..)
method which you should in order to honor the paint chain and thus the painting of other components.
这个调用也应该是方法中的第一个调用:
This call should also be the first call within the method:
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//do painting here
}
可能会出现绘图不持久的问题.您必须有一种方法来存储图纸并每次重绘.最常见的是一个 ArrayList
,它将保存要绘制的对象(因此您可以添加到列表中删除等),您将遍历列表并重绘 paintComponent中的每个对象代码>.查看我的回答这里例子.
A probem might arise that drawings are not persistent. You must than have a way to store drawings and redraw every time. The most common is an ArrayList
which will hold objects to be drawn (thus you cann add to the list remove etc), you would than iterate over the list and redraw each object in paintComponent
. See my answer here for an example.
另外请记得在 上创建和操作 Swing 组件事件调度线程 :
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//create UI and components here
}
});
不要在 JFrame
上调用 setSize(..)
而是覆盖 JPanel
的 getPreferredSize()
并返回适合所有组件的适当高度,而不是在设置 JFrame
可见(但在添加所有组件之后)之前调用 JFrame#pack()
.
Dont call setSize(..)
on JFrame
rather override getPreferredSize()
of JPanel
and return an appropriate height which fits all components, than call JFrame#pack()
before setting JFrame
visible (but after adding all components).
从 Java 6+ 开始,不需要 getContentPane().add(..)
add(..)
默认为 contentPane
No need for getContentPane().add(..)
as of Java 6+ add(..)
defaults to contentPane
不要在每次调用 paintComponent
时重新声明 Random
即 Random r=new Random()
,因为这会使值的分布较少随机,而是在创建类并调用实例上的方法时启动一次
Do not re declare Random
i.e Random r=new Random()
each time paintComponent
is called as this will make the distributions of the values less random rather initiate it once when class is created and call methods on the instance
这是固定代码(已实现上述修复):
Here is the fixed code (with above fixes implemented):
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
});
}
final DrawPanel myDraw = new DrawPanel();
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
frame.add(myDraw, BorderLayout.CENTER);
frame.add(startButton, BorderLayout.NORTH);
frame.add(stopButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if (!isClicked) {
}
myDraw.clearRects();
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
private ArrayList<MyRectangle> rects = new ArrayList<>();
private Random rand = new Random();
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
addRect();
for (MyRectangle r : rects) {
g.setColor(r.getColor());
g.fillRect(r.x, r.y, r.width, r.height);
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public void clearRects() {
rects.clear();
}
public void addRect() {
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green)));
}
} // close inner class
}
class MyRectangle extends Rectangle {
Color color;
public MyRectangle(int x, int y, int w, int h, Color c) {
super(x, y, w, h);
this.color = c;
}
public Color getColor() {
return color;
}
}
相关文章