2012-12-11 113 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來繪製單擊按鈕的圖像,但由於您還將任何繪製的對象添加到中心面板,因此它也會繪製在那裏。由於此特定重繪不指定位置,因此它位於左上角。