2017-01-07 37 views
0

我创建了一个应用程序,它包含一个正方形,每次触及frame的边缘时都会弹出一个正方形。我没有问题发起应用程序,问题是我不知道如何创建各种线程以便拥有框架内的多个正方形。 我尝试了多件事,但我无法弄清楚我应该在哪里创建线程。 我还注意到,只有当我直接将其添加到框架内而不是当我将它放入JPanel内时,该正方形才可见。如何用paintComponent()多线程?

Square.java

public class Square extends JComponent implements ActionListener { 

    int width = 20; 
    int height = 20; 
    double y = Math.random() * 360; 
    double x = Math.random() * 360; 
    boolean xMax = false; 
    boolean yMax = false; 
    boolean xMin = true; 
    boolean yMin = true; 
    Rectangle2D.Double square = new Rectangle2D.Double(x, y, width, height); 

    public Square() { 
    Timer t = new Timer(2, this); 
    t.start(); 
    } 

    public void paintComponent(Graphics g) { 
    Graphics2D g2 = (Graphics2D) g; 
    super.paintComponent(g); 
    g2.setColor(Color.BLUE); 
    g2.fill(square); 

    x_y_rules(); 


    } 
    public void x_y_rules() { 
    if (xMax == true) { 
     x = x - 0.5; 
     if (x <= 0) { 
      xMax = false; 
     } 
    } else { 
     x = x + 0.5; 
     if (x >= this.getWidth()) { 
      xMax = true; 
     } 
    } 
    if (yMax == true) { 
     y = y - 0.5; 
     if (y <= 0) { 
      yMax = false; 
     } 
    } else { 
     y = y + 0.5; 
     if (y >= this.getHeight()) { 
      yMax = true; 
     } 
    } 
    square.setFrame(x, y, width, height); 
    } 

@Override 
public void actionPerformed(ActionEvent arg0) { 
    repaint(); 
} 
} 

App.java

public class App extends JFrame { 

public static void main(String[] args) { 
    JFrame jf = new JFrame(); 
    Square sqr = new Square(); 
    jf.setSize(400, 400); 
    jf.setVisible(true); 
    jf.add(sqr); 
    jf.setDefaultCloseOperation(EXIT_ON_CLOSE); 
    jf.setLocationRelativeTo(null); 
} 
} 

这是正常的,尽管我把2时定时器内,广场上移动很慢?

+0

谢谢。我把x_y_rules()方法ActionListener.The问题内部是,如果一个创建另一个正方形,我将它添加到帧,后者只显示一个正方形。这就是为什么我认为我应该使用线程。 – TomCa

+0

由于您不使用线程来解决布局管理器问题,因此您将需要研究布局管理器。 JFrame contentPane使用BorderLayout,并且当您将组件默认添加到JFrame时,只会显示最近添加的组件。 –

回答

2

问题:

  1. 你有程序逻辑,该x_y_rules()方法调用,该方法的paintComponent里面。因为它不属于那里,所以把它放到它所属的Timer的ActionListener代码中。
  2. 如果你愿意,你可以给每个Square自己的Swing Timer。这不是一个真正的线程问题,因为每个Timer的ActionListener都会在EDT上运行。
  3. 两毫秒是期望在摆动计时器中使用的不切实际的时间片,并且没有计时器会快速运行。 11到13是最快的期望或希望。
  4. 如果您希望精灵的移动速度更快,请为您的移动代码中的delta-x和delta-y赋予更大的值。
  5. 您的JComponent没有定义首选大小,这可能是为什么它不会显示在JPanel中,因为默认的FlowLayout会将其大小设置为[0,0]。覆盖其getPreferredSize()并使其返回合理的尺寸值。
  6. 在添加所有组件之前,您正在对JFrame调用setVisible(true),这是一个禁忌。

好吧,我把getPrefferedSize()方类中,但我遇到了一个问题:正方形不是“在一起”,这就像他们在不同的面板

反弹

然后你的程序结构坏了。你真的不想创建单独的Swing组件,事实上你的Square类不应该扩展JComponent或JPanel。相反

  • 广场应该是一个合乎逻辑的,一个从一无所有(除默认对象等)延伸。
  • 给它的绘图方法,说public void draw(Graphics g) {....}
  • 创建一个类继承JPanel,说叫DrawingPanel,并覆盖其的paintComponent方法。
  • 为DrawingPanel类指定一个ArrayList<Square>,以便它可以容纳多个Square对象。
  • 给这个DrawingPanel类Swing的计时器
  • 在DrawingPanel类的计时器,有它更新ArrayList中的所有方的位置,然后调用repaint()
  • 在paintComponent方法里面,通过在所有的广场迭代该列表使用for循环,并调用每个人的绘制方法。

例如:

enter image description here

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Image; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.image.BufferedImage; 
import java.util.ArrayList; 
import java.util.List; 

import javax.swing.*; 

@SuppressWarnings("serial") 
public class DrawingPanel extends JPanel { 
    private static final int PREF_W = 600; 
    private static final int PREF_H = PREF_W; 
    private static final int TIMER_DELAY = 20; 
    private static final Color[] SQUARE_COLOR = { Color.BLUE, Color.CYAN, Color.DARK_GRAY, 
      Color.BLACK, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, 
      Color.PINK, Color.RED, Color.YELLOW }; 
    List<Square> squareList = new ArrayList<>(); 

    public DrawingPanel() { 
     // create a bunch of squares 
     for (int i = 0; i < SQUARE_COLOR.length; i++) { 
      squareList.add(new Square(SQUARE_COLOR[i], PREF_W, PREF_H)); 
     } 

     setBackground(Color.WHITE); 

     // create and start the timer 
     new Timer(TIMER_DELAY, new TimerListener()).start(); 
    } 

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

     // simply draw all the squares in the list 
     for (Square square : squareList) { 
      square.draw(g); 
     } 
    } 

    // set size of JPanel 
    @Override 
    public Dimension getPreferredSize() { 
     if (isPreferredSizeSet()) { 
      return super.getPreferredSize(); 
     } 
     return new Dimension(PREF_W, PREF_H); 
    } 

    private class TimerListener implements ActionListener { 
     @Override 
     public void actionPerformed(ActionEvent e) {    
      // simply iterate through list and move all squares 
      for (Square square : squareList) { 
       square.move(); 
      } 
      repaint(); // then repaint the GUI 
     } 
    } 

    private static void createAndShowGui() { 
     DrawingPanel mainPanel = new DrawingPanel(); 

     JFrame frame = new JFrame("Drawing Panel"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.getContentPane().add(mainPanel); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> createAndShowGui()); 
    } 
} 

// this class does *not* extend JPanel or JComponent 
class Square { 
    public static final int WIDTH = 20; 

    // location of Square 
    private double sqrX; 
    private double sqrY; 

    // X and Y speed 
    private double deltaX; 
    private double deltaY; 

    // width and height of DrawingPanel JPanel 
    private int dpWidth; 
    private int dpHeight; 

    // image to draw 
    private Image image; 

    public Square(Color color, int dpWidth, int dpHeight) { 
     this.dpWidth = dpWidth; 
     this.dpHeight = dpHeight; 

     // create square at random location with random speed 
     sqrX = Math.random() * (dpWidth - WIDTH); 
     sqrY = Math.random() * (dpHeight - WIDTH); 
     deltaX = Math.random() * 10 - 5; 
     deltaY = Math.random() * 10 - 5; 

     // one way to draw it is to create an image and draw it 
     image = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB); 
     Graphics g = image.getGraphics(); 
     g.setColor(color); 
     g.fillRect(0, 0, WIDTH, WIDTH); 
     g.dispose(); 
    } 

    public void move() { 

     // check that we're not hitting boundaries 
     if (sqrX + deltaX < 0) { 
      deltaX = Math.abs(deltaX); 
     } 
     if (sqrX + deltaX + WIDTH >= dpWidth) { 
      deltaX = -Math.abs(deltaX); 
     } 
     sqrX += deltaX; 

     // check that we're not hitting boundaries 
     if (sqrY + deltaY < 0) { 
      deltaY = Math.abs(deltaY); 
     } 
     if (sqrY + deltaY + WIDTH >= dpHeight) { 
      deltaY = -Math.abs(deltaY); 
     } 
     sqrY += deltaY; 

    } 

    public void draw(Graphics g) { 
     int x = (int) sqrX; 
     int y = (int) sqrY; 
     g.drawImage(image, x, y, null); 
    } 
} 
+0

5.好的,我在方形内部放了一个getPrefferedSize(),但我遇到了一个问题:方块不是“在一起”,就像它们在单独的面板上弹起一样。 – TomCa

+0

@TomCa:看编辑回答 –

+0

哇,非常感谢你!我会仔细研究你的代码。我不知道你写的东西的一些,但我想我必须学习这种或那种.. – TomCa