Проблемы с динамическим изменением JPanels на JFrame

У меня есть JFrame, который использует BorderLayout в качестве менеджера компоновки. У меня также есть 2 панели на этом JFrame, в ЦЕНТРЕ и ЮЖНОЙ позиции. Панель в ЦЕНТРАЛЬНОМ положении изменяется динамически при некоторых действиях пользователя (на самом деле, когда пользователь нажимает «Далее» на нижней панели).

Код следующий:

private void switchToPanelByState(State state)
{
    this.getContentPane().removeAll();

    JPanel panel = _panels.get(state);
    this.getContentPane().add(panel, BorderLayout.CENTER);

    this.getContentPane().add(_controlPanel, BorderLayout.SOUTH);
    this.pack();
}

Проблема в том, что после нескольких смен панелей на текущей панели появляются части "старых" (из предыдущих панелей) элементов GUI.

А также время от времени я вижу следующее исключение:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at javax.swing.JComponent._paintImmediately(Unknown Source)
        at javax.swing.JComponent.paintImmediately(Unknown Source)
        at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
        at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
        at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
        at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
        at java.awt.event.InvocationEvent.dispatch(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at javax.swing.JComponent._paintImmediately(Unknown Source)
        at javax.swing.JComponent.paintImmediately(Unknown Source)
        at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
        at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
        at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
        at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
        at java.awt.event.InvocationEvent.dispatch(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)

Однако я не знаю, связано ли это с проблемой.

Каков возможный источник этих проблем? Любые советы будут оценены.


person ovk    schedule 20.11.2011    source источник
comment
Всякий раз, когда я вижу метод removeAll(), первая мысль, которая приходит на ум, это то, что вы должны использовать Макет карточки.   -  person camickr    schedule 21.11.2011


Ответы (1)


BorderLayout принимает только один JComponent в центральном Area, тогда нет причин для remove()/ removeAll(); ваш код должен быть таким, как показано ниже:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class MyFrame extends JFrame {

    private static final long serialVersionUID = 1L;

    public MyFrame() {
        final JPanel parentPanel = new JPanel();
        parentPanel.setLayout(new BorderLayout(10, 10));
        JButton myButton = new JButton("Add Component ");
        myButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                JPanel childPanel1 = new JPanel();
                childPanel1.setBackground(Color.red);
                childPanel1.setPreferredSize(new Dimension(300, 40));
                JPanel childPanel2 = new JPanel();
                childPanel2.setBackground(Color.blue);
                childPanel2.setPreferredSize(new Dimension(800, 600));

                parentPanel.add(childPanel1, BorderLayout.NORTH);
                parentPanel.add(childPanel2, BorderLayout.CENTER);

                parentPanel.revalidate();
                parentPanel.repaint();

                pack();
            }
        });
        setTitle("My Empty Frame");
        setLocation(10, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        parentPanel.add(myButton, BorderLayout.SOUTH);
        add(parentPanel);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                MyFrame myFrame = new MyFrame();
            }
        });
    }
}

или напрямую

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class MyFrame extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPanel childPanel1 = new JPanel();
    private JPanel childPanel2 = new JPanel();
    private JPanel childPanel3 = new JPanel();

    public MyFrame() {
        childPanel1.setBackground(Color.red);
        childPanel1.setPreferredSize(new Dimension(300, 40));
        childPanel2.setBackground(Color.blue);
        childPanel2.setPreferredSize(new Dimension(300, 40));
        childPanel3.setBackground(Color.yellow);
        childPanel3.setPreferredSize(new Dimension(300, 40));

        JButton myButton = new JButton("Add Component ");
        myButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                add(childPanel2, BorderLayout.CENTER);
                pack();
            }
        });

        setTitle("My Empty Frame");
        setLocation(10, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(childPanel3, BorderLayout.CENTER);
        add(myButton, BorderLayout.SOUTH);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                MyFrame myFrame = new MyFrame();
            }
        });
    }
} 

2) никто не знает, что такое параметр state, тогда без SSCCE невозможно ответить на этот вопрос

3) Я могу имитировать исключения RepaintManager, когда CustomPainting быстрее, чем задержка из Native OS.

person mKorbel    schedule 20.11.2011
comment
mKorbel, спасибо за ответ. К сожалению, ни JFrame (это), ни Container (панель содержимого) не имеют перепроверки метода. Что касается State - это просто ключ для поиска нужной панели в HashMap. - person ovk; 20.11.2011
comment
этот ответ неверен - правильно то, что BorderLayout показывает только последний добавленный компонент (в каждой из его областей ограничений), но: все ранее добавленные компоненты с одним и тем же ограничением по-прежнему являются дочерними элементами контейнера. Кстати: пожалуйста, не засоряйте ответ вариантами, которые лишь незначительно отличаются - выберите один (самый минимум, здесь это будет второй только с двумя полями childPanel) и придерживайтесь его. - person kleopatra; 21.11.2011