2012-06-17 5 views
14

Подкласс JPanel для перезаписи paintComponent (Graphics), я хочу нарисовать изображение на jpanel в jframe.JPanel не обновляется до изменения размера Jframe

Но мое изображение не появилось, пока я не внес изменения в размер jframe. Это мой код:

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); 
     } 
    } 
} 

ответ

7

Если вы хотите, чтобы «освежить» JPanel, то вы должны вызвать перерисовку(), который будет вызывать paintComponent(). Это должно устранить вашу проблему:

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

Хорошая практика обновления и изменения GUI с помощью EDT. Heres подробнее на EDT, если вы заинтересованы:

How does the event dispatch thread work?

repaint не нужно быть вызван из EDT. Если вы меняете GUI, например, устанавливаете текст в JLabel, он должен быть внутри EDT. Heres больше информации о том, что можно назвать вне EDT (любезно NICE КПС):

Safe to use Component.repaint() outside EDT?

+0

Спасибо, Джон, он отлично работает! – nautilusvn

+1

+1 для ценного ввода. Просто предложение, не нужно ставить 'repaint()' вызов внутри EDT, так как безопасно вызывать 'repaint()' из любого потока, как описано [здесь] (http://stackoverflow.com/questions/9786497/safe -to-use-component-repaint-outside-edt/9786598 # 9786598) –

+1

"подклассы компонентов Swing, которые имеют делегат UI ... должны вызывать' super.paintComponent() '" - [* The Paint Methods *] (http: //java.sun.com/products/jfc/tsc/articles/painting/index.html#callback). – trashgod

18

Убедитесь, что вы вызываете setVisible()после добавления компонентов и вызова pack(), как описано в этой связанной example. Возможно, вам также понадобится принять layout. Вызов repaint(), как предложено here, может исправить симптом, но не основной причиной.

2

У меня была такая же проблема и исправлена ​​ее вызовом setVisible (true); JFrame, который я использовал.

Пример: если ваш JFrame не обновляется после использования:

jframe.setContentPane(new MyContentPane()); 

исправить это с:

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

Я знаю, что это звучит глупо, чтобы сделать это, даже если ваш JFrame уже видно, но это единственный способ, который я нашел до сих пор, чтобы исправить эту проблему (предлагаемое выше решение не сработало для меня).

Вот полный пример. Запустите его, а затем раскомментируйте «f.setVisible (true)»; инструкции в классах Panel1 и Panel2, и вы увидите разницу. Не забывайте импортировать (Ctrl + Shift + O для автоматического импорта).

Основной класс:

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 класс:

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 класс:

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); 
    } 
} 

Надежда, что помогает.

С уважением.

+0

Отлично! Не знаю, почему это работает, но это единственное, что решило мою проблему. До этого я делал это: Размер размера = frame.getSize(); \t Boolean maximized = frame.getExtendedState() == JFrame.MAXIMIZED_BOTH; \t frame.setSize (новый размер ((int) size.getWidth() - 1, (int) size.getHeight() - 1)); \t if (максимальный) frame.setExtendedState (JFrame.MAXIMIZED_BOTH); \t еще frame.setSize (размер); Ваш ответ намного, намного чище и менее хаки. Благодаря! – dberm22

+1

@ dberm22 на самом деле, нет необходимости взломать (по крайней мере, не в этом примере, не проверять оригинал с изображением, возможно, разных размеров, а также расширенными состояниями размера, которые также могут быть сложными): настройка contentPane is-a изменение иерархии контейнера кадра и как таковое требует повторной проверки на фрейме. – kleopatra

5

Посмотрите на the docs для JPanel.add(), который наследует от java.awt.Container:

Добавляет указанный компонент к концу этого контейнера. Это удобный метод для addImpl (java.awt.Component, java.lang.Object, int). Этот метод изменяет информацию, относящуюся к компоновке, и поэтому аннулирует иерархию компонентов. Если контейнер уже был отображен, иерархия должна быть проверена после этого, чтобы отобразить добавленный компонент.

Акцент добавлен.

Поэтому, если вы изменяете контейнер после это уже отображается, необходимо вызов validate() для того, чтобы показать. Просто вызвать repaint() недостаточно. Возможно, вы заметили, что также работает звонок setVisible(true); это потому, что it calls validate() internally.

Смежные вопросы