2016-05-07 22 views
1

我目前正在爲學校開展一個小項目,而且我還處於開始階段。我剛剛開始閱讀JFrame和所有這些內容,所以不要猶豫,爲什麼我不會很熟悉你給我看的所有內容。Java對已添加的對象進行更改

現在的目標是創建一個程序,該程序可以發出圖像並能夠手動更改該圖像的每個像素。因此,我寫了下面的代碼:

public class JavaGraphicsTest { 
    private static Pixel pixel; 

    public static void main(String[] args) { 
     JFrame frame = new JFrame(); 
     frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 
     frame.setVisible(true); 
     pixel = new Pixel(1600, 900); 
     frame.getContentPane().add(pixel); 
     //pixel.testChange(); 
    } 
} 

和:

public class Pixel extends Component { 
    private BufferedImage img; 
    private int width; 
    private int height; 
    private Graphics graphics; 

    public Pixel(int w, int h) { 
     width = w; 
     height = h; 
    } 

    public void create() { 
     img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 

     //Set any color for now 
     for(int wc = 0; wc < width; wc++) { 
      for(int hc = 0; hc < height; hc++) { 
       img.setRGB(wc, hc, new Color(0xAAFFBB).getRGB()); 
      } 
     } 
    }  

    @Override 
    public void paint(Graphics g) { 
     super.paint(g); 
     graphics = g; 
     create(); 
     update(); 
    } 

    public void update() { 
     graphics.drawImage(img, 0, 0, null); 
    } 

    public void testChange() { 
     for(int i = 50; i < 80; i++) { 
      for(int j = 80; j < 120; j++) { 
       img.setRGB(i, j, new Color(0xFF8876).getRGB()); 
      } 
      for(int j = 460; j < 493; j++) { 
       img.setRGB(i, j, new Color(0xFF8876).getRGB()); 
      } 
     } 
    } 
} 

嗯,代碼工作至今(多小時討厭的錯誤察覺的xD之後),但我想要做的現在似乎還沒有工作到目前爲止:我想在main方法中調用方法「pixel.testChange()」(它現在已被註釋)。但據我瞭解JFrame是如何工作的,一旦我將它添加到JFrame中,我就無法做任何事情。但是,誰應該工作呢?我如何修改任何活動對象而不刪除並重新添加? PS:如果你不明白testChange方法應該做什麼:它應該將圖像的兩個塊更改爲另一種顏色,它基本上是一個測試,看看我是否成功地更改了圖像。

如果您需要對項目的更多信息,請向我:)

由於提前, 朱利安

+0

嗨朱利安,歡迎來到stackoverflow! :D我會建議看看revalidate()方法。我自己對他們不太瞭解,但據我所知,它應該做你想做的事情。 :) –

+0

@EthanMoore謝謝^^,這似乎是做這件事的基礎,但是通過更多的錯誤發現,我發現我目前正在戰鬥的問題是另一個:當我打電話給這行時:'img.setRGB (80,80,new Color(0xFF8876).getRGB());'在paint()方法中,它可以工作,但在testChange()方法中我總是得到一個NullPointer :(但我完全不知道爲什麼它是這樣的... –

+0

嘗試更改'新的顏色(0xFF8876).getRGB()'到一個現有的值(只需再放80?)並告訴我會發生什麼?@JulianThurner –

回答

0

在你testChange方法結束(或調用之後),你應該叫invalidatePixel對象,這是一個信號通知組件將被更改,因爲它已被更改。

也請檢查this SO question

+0

'#invalidate()'應該在佈局改變時使用。如果佈局已經是最新的,它可能會跳過觸發重新繪製組件。當你想確保重繪完成時使用'#repaint()'。 – AJNeufeld

+0

@AJNeufeld你可能指的是'#revalidate()'。我想,由於圖形表示的數據已經發生了變化,那麼這就是「髒」對象的定義,然後舊的表示被認爲是「無效的」,因此需要新的「有效」表示。 – sanastasiadis

4

不要存儲Graphics對象。永遠。

public class Pixel extends Component { 
    private Graphics graphics; // <<-- DO NOT DO THIS 

    @Override 
    public void paint(Graphics g) { 
     // ... 
     graphics = g; // <<-- DO NOT DO THIS 
     // ... 
    } 
} 

圖形對象可以每次#paint(Graphics g)被稱爲重新創建,並且可以被無效,破壞或損壞時#paint(Graphics g)退出。

同上,請勿在#paint(Graphics g)呼叫期間創建圖像。當您的Pixel被創建時,這應該只進行一次。

public class Pixel extends Component { 

    @Override 
    public void paint(Graphics g) { 
     // ... 
     create(); // <<-- DO NOT DO THIS, EITHER. 
     // ... 
    } 
} 

但是你可以安全的Graphics對象傳遞給來自paint(Graphics g)調用的其他方法。

public class Pixel extends Component { 
    // ... 

    public Pixel(int w, int h) { 
     width = w; 
     height = h; 
     create(); 
    } 

    @Override 
    public void paint(Graphics g) { 
     super.paint(g); 
     update(g); 
    } 

    private void update(Graphics g) { 
     graphics.drawImage(img, 0, 0, null); 
    } 
} 

好的。對於手頭的業務 - 您的#testChange()方法。更改圖像後,只需要使用#repaint()調用,再次請求Swing再次繪製組件。

public class Pixel extends Component { 

    public void testChange() { 
     int rgb = new Color(0xFF8876).getRGB()); // Cached, for efficiency. 

     for(int i = 50; i < 80; i++) { 
      for(int j = 80; j < 120; j++) { 
       img.setRGB(i, j, rgb); 
      } 
      for(int j = 460; j < 493; j++) { 
       img.setRGB(i, j, rgb); 
      } 
     } 

     repaint(); // <<-- Ask Swing to repaint the component. 
    } 
} 

最後一點:你真的不應該改變,除了在事件調度線程(EDT)Swing對象。創建第一個窗口時通常允許有一個例外,但在它被設置爲可見之前。

public static void main(String[] args) { 
    JFrame frame = new JFrame(); 
    frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 
    frame.setVisible(true);    // <<-- Window becomes visible here 
    pixel = new Pixel(1600, 900); 
    frame.getContentPane().add(pixel); // <<-- DANGEROUS!! 
} 

相反,你可以重組這樣的代碼:

public static void main(String[] args) { 
    JFrame frame = new JFrame(); 
    frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 
    pixel = new Pixel(1600, 900); 
    frame.getContentPane().add(pixel); // <<-- Safe - window not visible yet. 
    frame.setVisible(true);    // <<-- Window becomes visible here 
} 

更好,是真正切換到EDT,使用SwingUtilities.invokeLater(...)

public static void main(String[] args) { 
    SwingUtilities.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      JFrame frame = new JFrame(); 
      frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 
      frame.setVisible(true);    // <<-- Window becomes visible here 
      pixel = new Pixel(1600, 900); 
      frame.getContentPane().add(pixel); // <<-- Safe - running on EDT. 
      pixel.testChange(); // <<-- Also safe - running on EDT 
     } 
    }); 
} 

哦,有Java8,您可以擺脫Runnable鍋爐板碼的大部分:

public static void main(String[] args) { 
    SwingUtilities.invokeLater(() -> { 
     JFrame frame = new JFrame(); 
     frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 
     frame.setVisible(true);    // <<-- Window becomes visible here 
     pixel = new Pixel(1600, 900); 
     frame.getContentPane().add(pixel); // <<-- Safe - running on EDT. 
     pixel.testChange(); // <<-- Also safe - running on EDT 
    }); 
} 
+0

這非常有幫助! ^^似乎問題的確是我存儲了Graphics對象......那麼,另一個問題是:使用EDT而不是通常的JFrame有什麼好處? –

+0

那些是不同的東西。 'JFrame'是一個**窗口** - 一個容納菜單,面板,按鈕和其他圖形對象的容器。事件分派線程(EDT)是**(__singular!_)線程** Swing用於操作圖形對象。你的程序可以創建許多線程來執行計算,訪問數據庫等,但它只能操縱EDT中的圖形對象。 「SwingUtilities.invokeLater(...)」只是切換到EDT所需的魔法咒語。你仍然在創建你的「通常的」JFrame;你只是從一個「安全」的地方去做。 – AJNeufeld

+0

沒有運行所有代碼,但作爲一般規則,您應該在最後一幀調用'setVisible',否則您需要調用'revalidate'和'repaint';) – MadProgrammer