2012-06-17 11 views
14

I podklasy JPanel, aby nadpisać paintComponent (Graphics), chcę narysować obraz na jpanel w jframe.JPanel nie aktualizuje się, dopóki nie zmieni rozmiaru Jframe

Ale moje zdjęcie nie wyświetliło się, dopóki nie zmieniłem rozmiaru jframe. To jest mój kod:

public class ImagePanel extends JPanel{ 

    public void setImage(BufferedImage bi) 
    { 
     image = bi; 
     revalidate(); 
    } 

    @Override 
    public void paintComponent(Graphics g) 
    { 
     super.paintComponent(g); 
     if(image != null) 
     { 
      g.drawImage(image, 0, 0, this); 
     } 
    } 
} 

Odpowiedz

7

Jeśli chcesz do "odświeżenia" JPanel to należy zadzwonić repaint(), która będzie zadzwonić do paintComponent(). To powinno naprawić Twój problem:

public void setImage(BufferedImage bi) 
{ 
    image = bi; 
    EventQueue.invokeLater(new Runnable() 
    { 
     public void run() 
     { 
      repaint(); 
     } 
    }); 
} 

Jego dobrą praktyką jest aktualizacja i zmiana GUI za pomocą EDT. Herezje Więcej informacji na temat EDT jeśli jesteś zainteresowany:

How does the event dispatch thread work?

repaint nie musi być wywoływana z EDT. Jeśli zmieniasz GUI, taki jak ustawienie tekstu na JLabel, powinien on znajdować się wewnątrz EDT. Herezje więcej informacji na temat tego, co można nazwać poza EDT (dzięki uprzejmości ładny krowa):

Safe to use Component.repaint() outside EDT?

+0

Dziękuję John, działa idealnie! – nautilusvn

+1

+1 za wartościowy wkład. Tylko sugestia, nie ma potrzeby wprowadzania wywołania 'repaint()' wewnątrz EDT, ponieważ można bezpiecznie wywoływać 'repaint()' z dowolnego wątku, jak opisano [tutaj] (http://stackoverflow.com/questions/9786497/safe -to-use-component-repaint-outside-edt/9786598 # 9786598) –

+1

"podklasy komponentów Swing, które mają delegata UI ... powinny wywoływać' super.paintComponent() '" - [* The Paint Methods *] (http: //java.sun.com/products/jfc/tsc/articles/painting/index.html#callback). – trashgod

18

Sprawdź, czy powołać setVisible()po dodawanie komponentów i nazywając pack(), jak to omówiono w tym związanych example. Może być również konieczne przyjęcie odpowiedniego layout. Wywołanie repaint(), zgodnie z sugestią here, może naprawić objaw, ale nie przyczynę.

2

Miałem ten sam problem i naprawiłem go przez wywołanie setVisible (true); JFrame, którego używałem.

Przykład: jeśli JFrame nie aktualizuje po użyciu:

jframe.setContentPane(new MyContentPane()); 

Fix z:

jframe.setContentPane(new MyContentPane()); 
jframe.setVisible(true); 

Wiem, że to brzmi głupio, aby to zrobić, mimo że JFrame jest już widoczny, ale to jedyny sposób, jaki znalazłem do tej pory, aby rozwiązać ten problem (rozwiązanie zaproponowane powyżej nie działało dla mnie).

Oto pełny przykład. Uruchom go, a następnie odkomentuj "f.setVisible (true);" instrukcje w klasach Panel1 i Panel2, a zobaczysz różnicę. Nie zapomnij o importach (Ctrl + Shift + O dla importów automatycznych).

główne klasy:

public class Main { 
    private static JFrame f; 
    public static void main(String[] args) { 
     f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setContentPane(new Panel1(f)); 
     f.pack();  
     f.setVisible(true); 
    } 
} 

Panel1 klasa:

public class Panel1 extends JPanel{ 
    private JFrame f; 
    public Panel1(JFrame frame) { 
     f = frame; 
     this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); 
     JButton b = new JButton("Panel 1"); 
     b.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       f.setContentPane(new Panel2(f)); 
       // Uncomment the instruction below to fix GUI "update-on-resize-only" problem 
       //f.setVisible(true); 
      } 
     }); 
     add(b); 
    } 
} 

Panel2 klasa:

public class Panel2 extends JPanel{ 
    private JFrame f; 
    public Panel2(JFrame frame) { 
     f = frame; 
     this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); 
     JButton b = new JButton("Panel 2"); 
     b.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       f.setContentPane(new Panel1(f)); 
       // Uncomment the instruction below to fix GUI "update-on-resize-only" problem 
       //f.setVisible(true); 
      } 
     }); 
     add(b); 
    } 
} 

nadzieję, że pomoże.

Pozdrawiam.

+0

Idealnie! Nie wiem, dlaczego to działa, ale to jedyna rzecz, która rozwiązała mój problem. Wcześniej robiłem to: Dimension size = frame.getSize(); \t Boolean maximized = frame.getExtendedState() == JFrame.MAXIMIZED_BOTH; \t frame.setSize (new Dimension ((int) size.getWidth() - 1, (int) size.getHeight() - 1)); \t jeśli (zmaksymalizowane) frame.setExtendedState (JFrame.MAXIMIZED_BOTH); \t else frame.setSize (rozmiar); Twoja odpowiedź jest znacznie, dużo czystsza i mniej hackowata. Dzięki! – dberm22

+1

@ dberm22 w rzeczywistości, nie ma potrzeby hackowania (przynajmniej nie w tym przykładzie, nie sprawdzałem oryginału z obrazem o potencjalnie różnych rozmiarach, ani rozszerzonych stanów rozmiarów, które mogą być trudne): ustawianie contentPane jest-a modyfikacja hierarchii kontenera ramki i jako taka wymaga ponownego sprawdzenia poprawności w ramce. – kleopatra

5

Spójrz na the docs dla JPanel.add(), która dziedziczy z java.awt.Container:

Dołącza określonego składnika do końca tego pojemnika. Jest to wygodna metoda dla addImpl (java.awt.Component, java.lang.Object, int). Ta metoda zmienia informacje związane z układem, dlatego też unieważnia hierarchię komponentów. Jeśli kontener został już wyświetlony, hierarchia musi zostać następnie zatwierdzona, aby wyświetlić dodany komponent.

Podkreślenie dodane.

Dlatego też, jeśli zmodyfikować Container po już wyświetlany to, ty koniecznością połączenia validate() w celu użycia go, aby pokazać się. Po prostu wywołanie repaint() nie wystarczy. Być może zauważyłeś, że zadzwonienie pod numer setVisible(true) również działa; to dlatego, że it calls validate() internally.

Powiązane problemy