2010-07-15 32 views
9

Có ai từng cố gắng sử dụng Swing để xây dựng môi trường kết xuất nhiều bộ đệm thích hợp trên đầu trang mà các yếu tố giao diện người dùng Swing có thể được thêm?JTextFields trên đầu trang của bản vẽ hoạt động trên JPanel, vấn đề luồng

Trong trường hợp này, tôi có hình chữ nhật màu đỏ hoạt hình được vẽ lên nền. Nền không cần phải được cập nhật mỗi khung hình vì vậy tôi đưa nó vào BufferedImage và vẽ lại chỉ phần cần thiết để xóa vị trí trước của hình chữ nhật. Xem mã đầy đủ bên dưới, điều này mở rộng ví dụ được đưa ra bởi @trashgod trong một chủ đề trước, here.

Cho đến nay rất tốt; hoạt hình mượt mà, sử dụng CPU thấp, không nhấp nháy.

Sau đó, tôi thêm JTextField vào Jpanel (bằng cách nhấp vào vị trí bất kỳ trên màn hình) và tập trung vào nó bằng cách nhấp vào bên trong hộp văn bản. Xóa vị trí trước của hình chữ nhật hiện không thành công trên mọi nháy mắt, xem hình ảnh bên dưới.

Tôi tò mò liệu có ai có ý tưởng về lý do tại sao điều này có thể xảy ra hay không?

Đây là trên Mac OS 10.5, Java 1,6

JPanel redraw fail http://www.arttech.nl/javaredrawerror.png

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsDevice; 
import java.awt.GraphicsEnvironment; 
import java.awt.Insets; 
import java.awt.Rectangle; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentEvent; 
import java.awt.event.ComponentListener; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.image.BufferedImage; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.Timer; 

public class NewTest extends JPanel implements 
    MouseListener, 
    ActionListener, 
    ComponentListener, 
    Runnable 
{ 

JFrame f; 
Insets insets; 
private Timer t = new Timer(20, this); 
BufferedImage buffer1; 
boolean repaintBuffer1 = true; 
int initWidth = 640; 
int initHeight = 480; 
Rectangle rect; 

public static void main(String[] args) { 
    EventQueue.invokeLater(new NewTest()); 
} 

@Override 
public void run() { 
    f = new JFrame("NewTest"); 
    f.addComponentListener(this); 
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    f.add(this); 
    f.pack(); 
    f.setLocationRelativeTo(null); 
    f.setVisible(true); 
    createBuffers(); 
    insets = f.getInsets(); 
    t.start(); 
} 

public NewTest() { 
    super(true); 
    this.setPreferredSize(new Dimension(initWidth, initHeight)); 
    this.setLayout(null); 
    this.addMouseListener(this); 
} 

void createBuffers() { 
    int width = this.getWidth(); 
    int height = this.getHeight(); 

    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
    GraphicsDevice gs = ge.getDefaultScreenDevice(); 
    GraphicsConfiguration gc = gs.getDefaultConfiguration(); 

    buffer1 = gc.createCompatibleImage(width, height, Transparency.OPAQUE);   

    repaintBuffer1 = true; 
} 

@Override 
protected void paintComponent(Graphics g) { 
    int width = this.getWidth(); 
    int height = this.getHeight(); 

    if (repaintBuffer1) { 
     Graphics g1 = buffer1.getGraphics(); 
     g1.clearRect(0, 0, width, height); 
     g1.setColor(Color.green); 
     g1.drawRect(0, 0, width - 1, height - 1); 
     g.drawImage(buffer1, 0, 0, null); 
     repaintBuffer1 = false; 
    } 

    double time = 2* Math.PI * (System.currentTimeMillis() % 5000)/5000.; 
    g.setColor(Color.RED); 
    if (rect != null) { 
     g.drawImage(buffer1, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this); 
    } 
    rect = new Rectangle((int)(Math.sin(time) * width/3 + width/2 - 20), (int)(Math.cos(time) * height/3 + height/2) - 20, 40, 40); 
    g.fillRect(rect.x, rect.y, rect.width, rect.height); 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    this.repaint(); 
} 

@Override 
public void componentHidden(ComponentEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void componentMoved(ComponentEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void componentResized(ComponentEvent e) { 
    int width = e.getComponent().getWidth() - (insets.left + insets.right); 
    int height = e.getComponent().getHeight() - (insets.top + insets.bottom); 
    this.setSize(width, height); 
    createBuffers(); 
} 

@Override 
public void componentShown(ComponentEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mouseClicked(MouseEvent e) { 
    JTextField field = new JTextField("test"); 
    field.setBounds(new Rectangle(e.getX(), e.getY(), 100, 20)); 
    this.add(field); 
    repaintBuffer1 = true; 
} 

@Override 
public void mouseEntered(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mouseExited(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mousePressed(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mouseReleased(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 
} 

Trả lời

18

NewTest kéo dài JPanel; nhưng vì bạn không vẽ mỗi điểm ảnh trên mỗi cuộc gọi đến paintComponent(), bạn cần phải gọi phương pháp siêu lớp và xóa các bản vẽ cũ:

@Override 
protected void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    int width = this.getWidth(); 
    int height = this.getHeight(); 
    g.setColor(Color.black); 
    g.fillRect(0, 0, width, height); 
    ... 
} 

Phụ Lục: Khi bạn lưu ý, thiết lập màu nền trong constructor ngăn cản sự cần thiết phải điền vào bảng điều khiển trong paintComponent(), trong khi super.paintComponent() cho phép (các) trường văn bản hoạt động chính xác. Khi bạn quan sát, cách giải quyết được đề xuất là mong manh. Thay vào đó, đơn giản hóa mã và tối ưu hóa như được bảo hành. Ví dụ, bạn có thể không cần các biến chứng của insets, bộ đệm bổ sung và một người nghe thành phần.

Phụ lục 2: Lưu ý rằng super.paintComponent() gọi phương thức update() của đại biểu giao diện người dùng, điền vào thành phần được chỉ định bằng màu nền của nó (nếu thuộc tính mờ của nó là đúng). " Bạn có thể sử dụng setOpaque(false) để loại trừ điều này.

Animation Test

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsEnvironment; 
import java.awt.Rectangle; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentAdapter; 
import java.awt.event.ComponentEvent; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.util.Random; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.Timer; 

/** @see http://stackoverflow.com/questions/3256941 */ 
public class AnimationTest extends JPanel implements ActionListener { 

    private static final int WIDE = 640; 
    private static final int HIGH = 480; 
    private static final int RADIUS = 25; 
    private static final int FRAMES = 24; 
    private final Timer timer = new Timer(20, this); 
    private final Rectangle rect = new Rectangle(); 
    private BufferedImage background; 
    private int index; 
    private long totalTime; 
    private long averageTime; 
    private int frameCount; 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new AnimationTest().create(); 
      } 
     }); 
    } 

    private void create() { 
     JFrame f = new JFrame("AnimationTest"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(this); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
     timer.start(); 
    } 

    public AnimationTest() { 
     super(true); 
     this.setOpaque(false); 
     this.setPreferredSize(new Dimension(WIDE, HIGH)); 
     this.addMouseListener(new MouseHandler()); 
     this.addComponentListener(new ComponentHandler()); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     long start = System.nanoTime(); 
     super.paintComponent(g); 
     int w = this.getWidth(); 
     int h = this.getHeight(); 
     g.drawImage(background, 0, 0, this); 
     double theta = 2 * Math.PI * index++/64; 
     g.setColor(Color.blue); 
     rect.setRect(
      (int) (Math.sin(theta) * w/3 + w/2 - RADIUS), 
      (int) (Math.cos(theta) * h/3 + h/2 - RADIUS), 
      2 * RADIUS, 2 * RADIUS); 
     g.fillOval(rect.x, rect.y, rect.width, rect.height); 
     g.setColor(Color.white); 
     if (frameCount == FRAMES) { 
      averageTime = totalTime/FRAMES; 
      totalTime = 0; frameCount = 0; 
     } else { 
      totalTime += System.nanoTime() - start; 
      frameCount++; 
     } 
     String s = String.format("%1$5.3f", averageTime/1000000d); 
     g.drawString(s, 5, 16); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     this.repaint(); 
    } 

    private class MouseHandler extends MouseAdapter { 

     @Override 
     public void mousePressed(MouseEvent e) { 
      super.mousePressed(e); 
      JTextField field = new JTextField("test"); 
      Dimension d = field.getPreferredSize(); 
      field.setBounds(e.getX(), e.getY(), d.width, d.height); 
      add(field); 
     } 
    } 

    private class ComponentHandler extends ComponentAdapter { 

     private final GraphicsEnvironment ge = 
      GraphicsEnvironment.getLocalGraphicsEnvironment(); 
     private final GraphicsConfiguration gc = 
      ge.getDefaultScreenDevice().getDefaultConfiguration(); 
     private final Random r = new Random(); 

     @Override 
     public void componentResized(ComponentEvent e) { 
      super.componentResized(e); 
      int w = getWidth(); 
      int h = getHeight(); 
      background = gc.createCompatibleImage(w, h, Transparency.OPAQUE); 
      Graphics2D g = background.createGraphics(); 
      g.clearRect(0, 0, w, h); 
      g.setColor(Color.green.darker()); 
      for (int i = 0; i < 128; i++) { 
       g.drawLine(w/2, h/2, r.nextInt(w), r.nextInt(h)); 
      } 
      g.dispose(); 
      System.out.println("Resized to " + w + " x " + h); 
     } 
    } 
} 
+0

Cảm ơn trả lời của bạn. Nếu bạn thêm super.paintComponent (g) và chạy ứng dụng thử nghiệm mà tôi đã cung cấp, bạn sẽ thấy rằng rất tiếc đây không phải là giải pháp. Phương thức paintComponent mặc định của JPanel xóa nền của nó, điều này làm cho nó cần thiết để vẽ lại toàn bộ hình nền, chủ yếu là đánh bại mục đích tạo hình nền trước. Tôi có linh cảm vẽ lại chỉ vùng cần thiết, nhưng có vẻ như có một số vấn đề với vẽ lại trường văn bản trong một chuỗi khác so với vẽ lại hình nền gây ra các tạo phẩm vẽ này .. – Mattijs

+0

Về phụ lục: cảm ơn nhiều lần vì đã dành thời gian để đi sâu vào điều này. Tôi nhận ra rằng quan điểm của bạn, là không có cách nào mà JTextFields vẽ lại tự động sẽ hoạt động chính xác cùng với vẽ lại * nếu không có * bằng cách sử dụng super.paintComponent (g), là chính xác. Tuy nhiên sử dụng super.paintComponent (g) ngụ ý rằng toàn bộ cửa sổ phải được vẽ lại trên mọi khung hình động, làm cho việc sử dụng CPU phụ thuộc vào kích thước cửa sổ, đó là những gì tôi muốn ngăn chặn. Vì vậy, câu hỏi của tôi được trả lời, nhưng vấn đề của tôi vẫn còn đứng. Tôi sẽ tạo một bài đăng mới về điều này. – Mattijs

+0

@Mattijs: Sử dụng setOpaque (false) sẽ tránh được việc điền. Tôi đã cập nhật ví dụ để hiển thị thời gian vẽ. Sự khác biệt là đáng kể, nhưng nó phải được cân nhắc chống lại nỗ lực quản lý cập nhật. – trashgod

2

Tôi tìm thấy một workaround.

Điều tôi nghĩ đã xảy ra: bất cứ khi nào JTextfield cần được cập nhật (tức là trên mỗi chớp con trỏ), paintComponent() được ghi đè của JPanel, nhưng với đối số Đồ họa khác so với khi được gọi bởi repaint(). Vì vậy, trên mỗi nhấp nháy của con trỏ, hình chữ nhật của tôi đã bị xóa và vẽ lại trên một đối tượng Graphics sai, để lại đồ họa nhìn thấy trên màn hình không hợp lệ.

Điều này có ý nghĩa gì không? Nếu có, đây có phải là một sự bất tiện kỳ ​​lạ trong Swing không?

Dù sao, bằng cách giữ một boolean (activeRedraw) của cuộc gọi đến từ đâu, có vẻ như tôi đã xoay xở giải quyết vấn đề này.Vì vậy, có vẻ như tôi cuối cùng đã tìm thấy một cách để làm bản vẽ hoạt động mà không sơn lại toàn bộ khu vực màn hình trên mỗi khung, có nghĩa là sử dụng CPU thấp độc lập với kích thước cửa sổ!

mã hoàn chỉnh ở đây:

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsDevice; 
import java.awt.GraphicsEnvironment; 
import java.awt.Insets; 
import java.awt.Rectangle; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentEvent; 
import java.awt.event.ComponentListener; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.image.BufferedImage; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.Timer; 

public class NewTest extends JPanel implements 
    MouseListener, 
    ActionListener, 
    ComponentListener, 
    Runnable 
{ 

    JFrame f; 
    Insets insets; 
    private Timer t = new Timer(20, this); 
    BufferedImage buffer1; 
    boolean repaintBuffer1 = true; 
    int initWidth = 640; 
    int initHeight = 480; 
    Rectangle rect; 
    boolean activeRedraw = true; 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new NewTest()); 
    } 

    @Override 
    public void run() { 
     f = new JFrame("NewTest"); 
     f.addComponentListener(this); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(this); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
     createBuffers(); 
     insets = f.getInsets(); 
     t.start(); 
    } 

    public NewTest() { 
     super(true); 
     this.setPreferredSize(new Dimension(initWidth, initHeight)); 
     this.setLayout(null); 
     this.addMouseListener(this); 
    } 

    void createBuffers() { 
     int width = this.getWidth(); 
     int height = this.getHeight(); 

     GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
     GraphicsDevice gs = ge.getDefaultScreenDevice(); 
     GraphicsConfiguration gc = gs.getDefaultConfiguration(); 

     buffer1 = gc.createCompatibleImage(width, height, Transparency.OPAQUE);   

     repaintBuffer1 = true; 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     //super.paintComponent(g); 
     int width = this.getWidth(); 
     int height = this.getHeight(); 

     if (activeRedraw) { 
      if (repaintBuffer1) { 
       Graphics g1 = buffer1.getGraphics(); 
       g1.clearRect(0, 0, width, height); 
       g1.setColor(Color.green); 
       g1.drawRect(0, 0, width - 1, height - 1); 
       g.drawImage(buffer1, 0, 0, null); 
       repaintBuffer1 = false; 
      } 

      double time = 2* Math.PI * (System.currentTimeMillis() % 5000)/5000.; 
      g.setColor(Color.RED); 
      if (rect != null) { 
       g.drawImage(buffer1, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this); 
      } 
      rect = new Rectangle((int)(Math.sin(time) * width/3 + width/2 - 20), (int)(Math.cos(time) * height/3 + height/2) - 20, 40, 40); 
      g.fillRect(rect.x, rect.y, rect.width, rect.height); 

      activeRedraw = false; 
     } 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     activeRedraw = true; 
     this.repaint(); 
    } 

    @Override 
    public void componentHidden(ComponentEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void componentMoved(ComponentEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void componentResized(ComponentEvent e) { 
     int width = e.getComponent().getWidth() - (insets.left + insets.right); 
     int height = e.getComponent().getHeight() - (insets.top + insets.bottom); 
     this.setSize(width, height); 
     createBuffers(); 
    } 

    @Override 
    public void componentShown(ComponentEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mouseClicked(MouseEvent e) { 
     JTextField field = new JTextField("test"); 
     field.setBounds(new Rectangle(e.getX(), e.getY(), 100, 20)); 
     this.add(field); 
     repaintBuffer1 = true; 
    } 

    @Override 
    public void mouseEntered(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mouseExited(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mousePressed(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mouseReleased(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 
} 
+0

Lưu ý: trên Windows, hành vi này một lần nữa khác và cách giải quyết không đủ. – Mattijs

+0

Tôi đã xây dựng trong câu trả lời của mình. – trashgod

Các vấn đề liên quan