Как изменить JPanel внутри JFrame на лету?

проще говоря, есть простое приложение java swing, которое состоит из JFrame с некоторыми компонентами в нем. Одним из компонентов является JPanel, который должен быть заменен другим JPanel на действие пользователя.

Итак, каков правильный способ сделать такую вещь? Я пытался

panel = new CustomJPanelWithComponentsOnIt();
parentFrameJPanelBelongsTo.pack();

но это не сработает. Что бы вы предложили?

14 ответов


ваш случай использования, кажется идеальным для CardLayout.

в макете карты вы можете добавить несколько панелей в одном месте, но затем показать или скрыть одну панель за раз.


1) установка первой панели:

JFrame frame=new JFrame();
frame.getContentPane().add(new JPanel());

2)замена панели:

frame.getContentPane().removeAll();
frame.getContentPane().add(new JPanel());

Также обратите внимание, что вы должны сделать это в потоке событий, чтобы это использовать метода swingutilities.invokeLater или SwingWorker


frame.setContentPane(newContents());
frame.revalidate(); // frame.pack() if you want to resize.

помните, Java использует передачу аргумента "Копировать ссылку по значению". Таким образом, изменение переменной не изменит копии ссылки, переданной другим методам.

также Примечание!--1--> очень запутанно во имя удобства использования. Добавление компонента или установка макета (обычно) выполняет операцию на панели содержимого. Как ни странно, получение макета действительно дает вам менеджер макета фрейма.


надеюсь, что этот фрагмент кода даст вам идею изменения jPanels внутри JFrame.

public class PanelTest extends JFrame {

        Container contentPane;

        public PanelTest()  {
           super("Changing JPanel inside a JFrame");
           contentPane=getContentPane();
        }

        public void createChangePanel() {
           contentPane.removeAll();
           JPanel newPanel=new JPanel();
           contentPane.add(newPanel);
           System.out.println("new panel created");//for debugging purposes
           validate();
           setVisible(true);
        }
}

на действия пользователя:

// вы должны сделать что-то вроде

myJFrame.getContentPane().removeAll()
myJFrame.getContentPane().invalidate()

myJFrame.getContentPane().add(newContentPanel)
myJFrame.getContentPane().revalidate()

затем вы можете изменить размер стекла по мере необходимости.


все зависит от того как его будут использовать. Если вы хотите переключаться между этими двумя панелями, используйте CardLayout. Если вы только переключаетесь с первого на второй раз и (и не возвращаетесь), то я бы использовал telcontars предложение и просто заменить его. Хотя, если JPanel не единственная вещь в вашем кадре, я бы использовал удалить (java.ОУ.Компонент) вместо removeAll.

Если вы находитесь где-то между этими два случая-это в основном компромисс во времени и пространстве. CardLayout сохранит вас время но займет больше памяти путем держать всю эту другую панель в памяти все время. Но если вы просто заменяете панель, когда это необходимо, и строите ее по требованию, вам не нужно держать эту meory, но требуется больше времени для переключения.

также вы можете попробовать использовать вместо вкладки JTabbedPane (его даже легче, чем CardLayout, потому что он обрабатывает показа/скрытия automitically)


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


у меня была точно такая же проблема!! Невероятно!! Решение, которое я нашел, было:

  1. добавление всех компонентов (JPanels) в контейнер;
  2. используя метод setVisible (false) для всех из них;
  3. на действие пользователя, установка setVisible (true) на панель, которую я хотел показывать.
// Hiding all components (JPanels) added to a container (ex: another JPanel)
for (Component component : this.container.getComponents()) {
      component.setVisible(false);
}
// Showing only the selected JPanel, the one user wants to see
panel.setVisible(true);

нет подтверждения(), нет подтверждения(), нет необходимости CardLayout.


макет.replace () ответ существует/работает только в менеджере GroupLayout.

другие LayoutManagers( CardLayout, BoxLayout и т. д.) не поддерживают эту функцию, но требуют, чтобы вы сначала RemoveLayoutComponent( а затем AddLayoutComponent (снова. :- ) [Просто установив рекорд прямо]


Я предлагаю вам добавить обе панели при создании кадра, а затем изменить видимую панель, вызвав setVisible (true/false) на обоих. При вызове setVisible родитель будет уведомлен и попросил перекрасить себя.


class Frame1 extends javax.swing.JFrame {

    remove(previouspanel); //or getContentPane().removeAll();

    add(newpanel); //or setContentPane(newpanel);

    invalidate(); validate(); // or ((JComponent) getContentPane()).revalidate();

    repaint(); //DO NOT FORGET REPAINT

}

иногда вы можете сделать работу без использования revalidation и иногда без использования перекраски.Мой совет использовать оба.



просто вызвать метод pack () после установки ContentPane, (java 1.7, может быть, старше) вот так:

JFrame frame = new JFrame();  
JPanel panel1 = new JPanel(); 
JPanel panel2 = new JPanel(); 
....
frame.setContentPane(panel1);
frame.pack();
...

frame.setContentPane(panel2);
frame.pack();
...

вы можете использовать метод replace макета:

layout.replace(existingComponent, newComponent)

существующие и новые компоненты также могут быть JPanels.