2017-08-10 73 views
0

在java中使用Swing時,我試圖在單擊按鈕時從起始位置緩慢移動圓圈到結束位置。但是,我看不到這個圈子在移動。它只是瞬間從頭到尾移動。我無法看到移動的圓圈

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 

public class MyApp { 

    private int x = 10; 
    private int y = 10; 
    private JFrame f; 
    private MyDraw m; 
    private JButton b; 

    public void go() { 
     f = new JFrame("Moving circle"); 
     b = new JButton("click me to move circle"); 
     m = new MyDraw(); 
     f.add(BorderLayout.SOUTH, b); 

     f.add(BorderLayout.CENTER, m); 
     f.setSize(500, 500); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setVisible(true); 

     b.addActionListener(new Bute()); 
    } 

    public static void main(String[] args) { 
     MyApp m = new MyApp(); 
     m.go(); 
    } 

    private class Bute implements ActionListener { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      for (int i = 0; i < 150; i++) { 
       ++x; 
       ++y; 
       m.repaint(); 
       Thread.sleep(50); 
      } 
     } 
    } 

    private class MyDraw extends JPanel { 
     @Override 
     public void paintComponent(Graphics g) { 
      g.setColor(Color.white); 
      g.fillRect(0, 0, 500, 500); 
      g.setColor(Color.red); 
      g.fillOval(x, y, 40, 40); 
     } 
    } 
} 

我認爲問題是與動作偵聽器,因爲當我這樣做而不使用按鈕它正在工作。有什麼建議麼?

+2

我推薦一位配鏡師。 – Michael

+1

不要阻塞EDT(Event Dispatch Thread)。發生這種情況時,GUI將「凍結」。有關詳細信息和修補程序,請參見[Swing中的併發](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/)。 –

回答

2

正如安德魯·湯普森說,叫Thread.sleep()沒有界定第二線程凍結了一切,因此該解決方案是定義並運行另一個線程像這樣:

class Bute implements ActionListener, Runnable { 
    //let class implement Runnable interface 
    Thread t; // define 2nd thread 

    public void actionPerformed(ActionEvent e) { 

     t = new Thread(this); //start a new thread 
     t.start(); 
    } 

    @Override    //override our thread's run() method to do what we want 
    public void run() {  //this is after some java-internal init stuff called by start() 
     //b.setEnabled(false); 
     for (int i = 0; i < 150; i++) { 
      x++; 
      y++; 
      m.repaint(); 
      try { 
       Thread.sleep(50); //let the 2nd thread sleep 
      } catch (InterruptedException iEx) { 
       iEx.printStackTrace(); 
      } 
     } 
     //b.setEnabled(true); 
    } 

} 

這種解決方案的唯一問題是,按下按鈕多次會加快圈子的速度,但這可以通過在動畫中通過b.setEnabled(true/false)使該按鈕不可點擊來解決。不是最好的解決方案,但它工作。

+0

'public void go(){ JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyDrawPanel drawPanel = new MyDrawPanel(); frame.add(drawPanel); frame.setSize(300,300); frame.setVisible(true); for(int i = 0; i <130; i ++){ x ++; y ++; drawPanel.repaint(); 嘗試Thread.sleep(50); } catch(Exception ex){} } }' **我沒有在這裏使用按鈕和actionlistener,它工作正常,爲什麼 線程在這裏沒有被阻止?** –

2

正如在評論和另一個答案中所說的,不要阻止EDT。 Thead.sleep(...)會阻止它,所以你有兩個選擇:

  • 創建和管理你自己的(新)線程。
  • 使用一個Swing Timer

在這個答案,我將使用一個Swing Timer,因爲它更容易使用。我也改變了paintComponent方法使用Shape API和更改按鈕文本來啓動和停止相應,以及重複使用該按鈕的同ActionListener和定時器:

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.event.ActionListener; 
import java.awt.geom.Ellipse2D; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
import javax.swing.Timer; 

public class MovingCircle { 
    private JFrame frame; 
    private CustomCircle circle; 
    private Timer timer; 
    private JButton button; 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui); 
    } 

    private void createAndShowGui() { 
     frame = new JFrame(this.getClass().getSimpleName()); 
     circle = new CustomCircle(Color.RED); 
     timer = new Timer(100, listener); 
     button = new JButton("Start"); 
     button.addActionListener(listener); 

     circle.setBackground(Color.WHITE); 
     frame.add(circle); 
     frame.add(button, BorderLayout.SOUTH); 
     frame.pack(); 
     frame.setVisible(true); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    } 

    private ActionListener listener = (e -> { 
     if (!timer.isRunning()) { 
      timer.start(); 
      button.setText("Stop"); 
     } else { 
      if (e.getSource().equals(button)) { 
       timer.stop(); 
       button.setText("Start"); 
      } 
     } 
     circle.move(1, 1); 
    }); 

    @SuppressWarnings("serial") 
    class CustomCircle extends JPanel { 
     private Color color; 
     private int circleX; 
     private int circleY; 

     public CustomCircle(Color color) { 
      this.color = color; 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g; 
      g2d.setColor(color); 
      g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50)); 
     } 

     @Override 
     public Dimension preferredSize() { 
      return new Dimension(100, 100); 
     } 

     public void move(int xGap, int yGap) { 
      circleX += xGap; 
      circleY += yGap; 
      revalidate(); 
      repaint(); 
     } 

     public int getCircleX() { 
      return circleX; 
     } 

     public void setCircleX(int circleX) { 
      this.circleX = circleX; 
     } 

     public int getCircleY() { 
      return circleY; 
     } 

     public void setCircleY(int circleY) { 
      this.circleY = circleY; 
     } 
    } 
} 

我很抱歉,我不能按照我的意願發佈GIF,但此示例按預期運行。