2012-12-11 65 views
2

我有下面的程序,当它运行时有一些非常奇怪和不想要的行为。 。它应该有两个按钮,“开始”和“停止,但是当我点击‘开始’另一个按钮显示了下方的‘开始’这里是打印屏幕什么我谈论:奇怪的JFrame行为

enter image description here

什么我做错了,我怎么解决这个难看的问题

下面的代码:?

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 
} 

而且我试图让开始按钮做两件事情之一是的当然开始动画,但是当按下停止按钮时,我按下再次开始,我希望它能够清理屏幕,然后再次开始动画。任何提示呢?

回答

4

你不被覆盖的paintComponent(..)方法,你应该为了纪念油漆链,因此其它部件的绘画调用super.paintComponent(Graphics g)

此呼叫也应该是该方法中的第一呼叫:

@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 

    //do painting here 
} 

甲probem可能出现的附图不是永久性的。您必须有一种方法来存储图纸并每次重绘。最常见的是一个ArrayList,它将保存要绘制的对象(因此你不能添加到列表删除等),你会迭代列表并重绘每个对象在paintComponent。以我的回答here为例。

  • 也请记住在Event Dispatch Thread创建和操纵Swing组件:

    SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         //create UI and components here 
        } 
    }); 
    
  • 不要呼叫setSize(..)JFrame,而覆盖的JPanelgetPreferredSize()并返回相应的高度适合所有的组件,不是打电话JFrame#pack()在设置JFrame之前可见(但添加完所有组件后)。

  • 无需getContentPane().add(..)从Java 6+ add(..)默认为的contentPane

  • 不要重新申报Random我。È​​每次paintComponent称为这将使值少随机而被创建的类时,一旦启动它,并调用实例

此处方法的分布是固定的代码(与上述修正实现):

enter image description here

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; 
    } 
} 
+0

如果我做super.paintComponent(g),每次调用paintComponent都不会刷新屏幕吗?这不是我想要做的事情。或者我对我的理解错了吗? –

+1

@nickecarlo当然你想要重画屏幕,并且你可以看到如果你不叫它会发生什么。唯一可能发生的问题是你的图形将被擦除,通过使它们持久化,即将所有要绘制的矩形添加到一个ArrayList并迭代'paintComponent'上的数组,这样它们将在每次屏幕显示时重绘 –

+0

感谢您的意见。我也赞同你的回答。我会尝试两种答案,并接受最好的作品。 –

2

我通过调用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 
+0

谢谢。如果它适合我​​,我会尝试一下并接受答案。 –

+0

-1我不认为这是正确的。看起来偶然使用'SwingUtilities'实际上甚至有所作为,即使在调用'EDT'时,如果没有调用super.paintComponent,这种异常仍然可能发生。 –

+0

@David Kroukamp:除非扩展JPanel类具有JComponent子级,否则不需要super.paintComponent。由于OP使用扩展的JPanel类作为画布,所以不需要超级调用。 –

3

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