2012-03-25 23 views
7

Tôi đang viết một ứng dụng java cần cuộn một dạng sóng trơn tru trên màn hình. Tôi đã dành nhiều lứa tuổi đi qua các hướng dẫn khác nhau để tìm ra cách làm cho hoạt ảnh này mượt mà nhất có thể. Theo như tôi có thể thấy tôi đã làm tất cả những thứ bình thường để loại bỏ nhấp nháy (vẽ ra bộ đệm ngoài màn hình và hiển thị trong một bước, cộng với việc cập nhật để màn hình không bị xóa), nhưng hoạt ảnh của tôi vẫn nhấp nháy và màn hình có vẻ như nó đang bị xóa trước mỗi lần cập nhật.Làm thế nào để loại bỏ hoạt ảnh java nhấp nháy

Tôi chắc chắn có điều gì đó cơ bản (và có thể là đơn giản) tôi bị thiếu, nhưng tôi không có ý tưởng. Tôi sẽ đăng một lớp dưới đây minh họa vấn đề. Bất kì sự trợ giúp nào đều được đánh giá cao.

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

public class FlickerPanel extends JPanel implements Runnable { 

    private float [] pixelMap = new float[0]; 

    /** Cached graphics objects so we can control our animation to reduce flicker **/ 
    private Image screenBuffer; 
    private Graphics bufferGraphics; 

    public FlickerPanel() { 
     Thread t = new Thread(this); 
     t.start(); 
    } 

    private float addNoise() { 
     return (float)((Math.random()*2)-1); 
    } 

    private synchronized void advance() { 
     if (pixelMap == null || pixelMap.length == 0) return; 
     float [] newPixelMap = new float[pixelMap.length]; 
     for (int i=1;i<pixelMap.length;i++) { 
      newPixelMap[i-1] = pixelMap[i]; 
     } 

     newPixelMap[newPixelMap.length-1] = addNoise();  

     pixelMap = newPixelMap; 
    } 

    public void run() { 
     while (true) { 
      advance(); 
      repaint(); 

      try { 
       Thread.sleep(25); 
      } catch (InterruptedException e) {} 

     } 
    } 

    private int getY (float height) { 
     double proportion = (1-height)/2; 
     return (int)(getHeight()*proportion); 
    } 

    public void paint (Graphics g) { 

     if (screenBuffer == null || screenBuffer.getWidth(this) != getWidth() || screenBuffer.getHeight(this) != getHeight()) { 
      screenBuffer = createImage(getWidth(), getHeight()); 
      bufferGraphics = screenBuffer.getGraphics(); 
     } 

     if (pixelMap == null || getWidth() != pixelMap.length) { 
      pixelMap = new float[getWidth()]; 
     } 

     bufferGraphics.setColor(Color.BLACK); 

     bufferGraphics.fillRect(0, 0, getWidth(), getHeight()); 

     bufferGraphics.setColor(Color.GREEN); 

     int lastX = 0; 
     int lastY = getHeight()/2; 

     for (int x=0;x<pixelMap.length;x++) { 
      int y = getY(pixelMap[x]); 
      bufferGraphics.drawLine(lastX, lastY, x, y); 
      lastX = x; 
      lastY = y; 
     } 

     g.drawImage(screenBuffer, 0, 0, this); 
    } 

    public void update (Graphics g) { 
     paint(g); 
    } 

    public static void main (String [] args) { 
     JFrame frame = new JFrame("Flicker test"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setContentPane(new FlickerPanel()); 
     frame.setSize(500,300); 
     frame.setVisible(true); 
    } 


} 
+0

Tôi không thể tìm thấy bất kỳ sự cố nào với mã của bạn. Trông khá trơn tru trên máy tính để bàn của tôi. ps. Bạn có thể làm điều này hiệu quả hơn nhiều bằng cách chỉ có một pixelMap và sử dụng nó như một bộ đệm tuần hoàn, và bảo vệ quyền truy cập vào nó bằng một khối đồng bộ. Vì nó là viết tắt của việc tạo ra một cấu trúc mới mỗi bản cập nhật nên được OK. – Adam

+0

+1 cho [sscce] (http://sscce.org/). Nó shimmers khá nặng trên nền tảng của tôi. – trashgod

+0

Có chơi nhiều hơn một chút có vẻ như một số vấn đề có thể là do cơ sở hạ tầng pixelMap đang được sửa đổi trong quá trình vẽ, vì vậy tôi đang rách giữa các phần khác nhau của màn hình. Làm một bản sao() của pixelMap ở đầu của phương pháp vẽ có vẻ để cải thiện mọi thứ, nhưng nhấp nháy vẫn còn đó vì vậy tôi không nghĩ rằng đây là toàn bộ câu trả lời. –

Trả lời

6

JPanel được đôi đệm theo mặc định, và "chương trình Swing nên ghi đè paintComponent() thay vì trọng paint()." - Painting in AWT and Swing: The Paint Methods. Tương phản này example ghi đè paintComponent() với điều này example ghi đè paint().

Phụ lục: Cắt được tạo ra bằng cách tạo và sao chép toàn bộ hình ảnh trên mỗi lần cập nhật. Thay vào đó, tích lũy điểm trên GeneralPathdraw() số Shape trên mỗi lần lặp lại, như được hiển thị here.

+0

Tôi đã cố gắng ghi đè paintComponent và cũng thiết lập bảng điều khiển để không được tăng gấp đôi đệm, nhưng dường như không có bất kỳ sự khác biệt ở đây, nhấp nháy vẫn xảy ra. –

+0

[ví dụ] liên quan này (http://stackoverflow.com/a/5048863/230513) không nhấp nháy. Chỉnh sửa: Nó dựa trên 'javax.swing.Timer' để cập nhật trên EDT. – trashgod

+0

Tôi đã xây dựng ở trên. Cũng xem xét ['nextGaussian()'] (http://docs.oracle.com/javase/7/docs/api/java/util/Random.html#nextGaussian%28%29) cho tiếng ồn. – trashgod

7
  1. Trong một JPanel, ghi đè paintComponent(Graphics) hơn paint(Graphics)
  2. Thay vì gọi Thread.sleep(n) thực hiện một Swing Timer cho lặp lại thao tác hay một SwingWorker cho các nhiệm vụ chạy dài. Xem Concurrency in Swing để biết thêm chi tiết.
+1

Có; mặc dù 'sleep()' xuất hiện trên một luồng khác và 'repaint()' là an toàn luồng, 'advance()' sẽ phải được đồng bộ hóa. – trashgod

+0

OK, đó là một điểm tốt, nhưng tôi không nghĩ rằng nó có bất kỳ hiệu ứng trực tiếp trên nhấp nháy - nó xuất hiện cùng một phương pháp nào tôi ghi đè lên. –

+1

@trashgod Tôi quyết định loại bỏ câu 1 của điểm 2 * "Không chặn EDT (Event Dispatch Thread) - GUI sẽ 'đóng băng' khi điều đó xảy ra." * Vì trong khi thực sự, nó dường như không áp dụng ở đây . –

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