奇怪的JFrame行为

我有以下程序,它运行时有一些非常奇怪和不需要的行为。 它应该有两个按钮,“开始”和“停止,但当我点击”开始“另一个按钮显示在”开始“的正下方。这是我正在谈论的打印屏幕:

在此处输入图像描述

我做错了什么,如何解决这个丑陋的问题?

这是代码:

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 } 

此外,我正在尝试使用“开始”按钮执行两项操作。 一个当然是开始动画,但是当按下停止按钮并再次按下开始时,我希望它清理屏幕以便说话并再次启动新动画。 关于那个的任何提示?

你不应该在覆盖paintComponent(..)方法中调用super.paintComponent(Graphics g) ,你应该为了纪念油漆链而绘制其他组件。

此调用也应该是方法中的第一个调用:

 @Override public void paintComponent(Graphics g) { super.paintComponent(g); //do painting here } 

可能会出现一些问题,即图纸不是持久性的。 您必须每次都有办法存储图纸和重绘。 最常见的是一个ArrayList ,它将保存要绘制的对象(因此您可以添加到列表中删除等),您可以迭代列表并重绘paintComponent每个对象。 请参阅我的答案以获取示例。

  • 另请注意在Event Dispatch Thread上创建和操作Swing组件:

     SwingUtilities.invokeLater(new Runnable() { @Override public void run() { //create UI and components here } }); 
  • 不要在JFrame上调用setSize(..)而是覆盖JPanel getPreferredSize()并返回适合所有组件的适当高度,而不是在设置JFrame可见之前调用JFrame#pack() (但是在添加所有组件之后)。

  • 不需要getContentPane().add(..)从Java 6+ add(..)默认为contentPane

  • 每次调用paintComponent都不要重新声明RandomRandom r=new Random()因为这将使得值的分布更少随机而是在创建类时启动它并在实例上调用方法

这是固定代码(已实现上述修复):

在此处输入图像描述

 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 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(rx, ry, 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; } } 

我希望我能提供解决方案,但到目前为止我还没有找到解决方案。 我可以告诉你,问题的根源在于你绘制BorderLayout的Center部分的方式。 您将覆盖此程序的整个paintComponent()函数,并将其创建的内容放入BoarderLayout的Center中。 在这种情况下,每次单击按钮时,程序都会调用重绘以绘制单击按钮的图像,但由于您还将任何绘制的对象添加到“中心”面板,因此它也会在那里绘制。 由于此特定重绘不指定位置,因此它位于左上角。

我通过调用SwingUtilities修复了我的Windows XP计算机上的按钮问题。

我格式化了你的Java代码。

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; public class TwoButtonsTest implements Runnable { JFrame frame; Timer timer; boolean isClicked; public static void main(String[] args) { SwingUtilities.invokeLater(new TwoButtonsTest()); } @Override public void run() { 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 { @Override 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 } 

要在按“开始”按钮时清理屏幕,您将不得不向DrawPanel类添加一些方法。

这是一种方法。

 class DrawPanel extends JPanel { protected boolean eraseCanvas; public void setEraseCanvas(boolean eraseCanvas) { this.eraseCanvas = eraseCanvas; } @Override public void paintComponent(Graphics g) { if (eraseCanvas) { g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(), getHeight()); } else { 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