Проблема рисования / разметки дочерних компонентов JPanel - PullRequest
4 голосов
/ 10 марта 2010

У меня проблема с тем, что когда отображается мой фрейм (после диалога входа в систему), кнопки не находятся в правильном положении, а через несколько миллисекунд они переходят в правильное положение (центр панели с расположением границ).

- обновление

На моей машине этот SSCCE показывает проблему размещения в 2 из 10 раз, когда я запускаю его:

import java.awt.BorderLayout;
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TEST {

    public static void main(String[] args) throws Exception {

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {

        System.out.println("Debug test...");

        JPanel btnPnl = new JPanel();
        btnPnl.add(new JButton("TEST"));

        JFrame f = new JFrame("TEST");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(btnPnl);
        f.setPreferredSize(new Dimension(800, 600));
        f.pack();
        f.setExtendedState(JFrame.MAXIMIZED_BOTH);
        f.setVisible(true);

        System.out.println("End debug test!");

        }
    });

    }

}

Кнопка сначала появляется вверху слева, а затем переходит в центр. Это ошибка Java?

- обновление

Похоже, что SSCCE не показывает проблему для всех, кто пытается. Может быть, это проблема с производительностью моего компьютера. Я все еще думаю, что Java Swing создает новые потоки для создания макета за кулисами. Но я не уверен.

- обновление

Проблема возникает только с f.setExtendedState (JFrame.MAXIMIZED_BOTH);

Ответы [ 8 ]

2 голосов
/ 22 апреля 2010

Ваша проблема заинтриговала меня. После некоторого исследования, я думаю, я подтвердил кое-что, что я помню о настройке состояния окна (развернуто, восстановлено и т. Д.), А именно то, что установка состояния является запросом к операционной системе и оставлена ​​на усмотрение ОС для обработки запроса. Это означает, что он асинхронный или, по крайней мере, будет сделан позже, после того, как вы его установите. Я подтвердил использование регистрации и добавления слушателей изменения размера, где вы можете видеть, что размер кадра изменяется после выхода вашего блока кода. Из-за этого pack () будет размещать компоненты в соответствии с их предпочтительным размером. Итак, представьте, что рамка имеет размеры 800x600 и компоненты расположены как таковые (кнопка расположена горизонтально по центру около 400). Затем ОС изменяет размер кадра на полный экран (например, 1024x768) - на мгновение вы увидите, что кнопка все еще имеет значение 400. Затем кадр обрабатывает новый размер, перекладывает компоненты и центрирует кнопку. около 512. Таким образом, вы увидите мерцание, когда оно переходит во время этого процесса. Возможно, решение состоит в том, чтобы НЕ упаковывать () - он останется равным нулю, и пользователь увидит минимальное мерцание.

Попробуйте сначала изменить это:

// pack()

Если это выглядит хорошо, у вас может быть следующая проблема ... если пользователь нажимает кнопку восстановления, весь кадр сжимается в черную дыру. Поэтому попробуйте вызвать пакет ПОСЛЕ того, как размер кадра будет предсказуемо изменен из-за максимизации. Как то так:

f.addComponentListener( new ComponentAdapter( ComponentEvent e ) {
  public void componentResized( Component) {
     if( f.getSize().getWidth() > 0 ) {
        e.getComponent().removeComponentListener( this );
        ((JFrame)e.getComponent()).pack();
     }
  }
}

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

- Обновление

ОК, последняя попытка. Хотя я думаю, что в моем описании проблемы есть доля правды, предложенные решения ничего не сделали. Вот последняя попытка Удалите pack () и setPreferredSize () и замените его настройкой размера экрана. Кажется, это сильно уменьшает мерцание в моей системе. Это потому, что не должно быть никакой разницы между исходным макетом и максимизированным макетом, сделанным позже. Вы можете увидеть это, если вы переключаетесь между восстановлением и максимальным. Несмотря на то, что при переключении этих двух устройств я все еще вижу очень слабое мерцание, по крайней мере, при первом отображении оно выглядит лучше.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TEST {

    public static void main(String[] args) throws Exception {

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {

        System.out.println("Debug test...");

        JPanel btnPnl = new JPanel();
        btnPnl.add(new JButton("TEST"));

        JFrame f = new JFrame("TEST");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(btnPnl);
//        f.setPreferredSize(new Dimension(800, 600));
//        f.pack();
        f.setSize( Toolkit.getDefaultToolkit().getScreenSize() );
        f.setExtendedState(JFrame.MAXIMIZED_BOTH);
        f.setVisible(true);

        System.out.println("End debug test!");

        }
    });

-Mike

1 голос
/ 22 апреля 2010

Часть "затем через несколько миллисекунд" звучит для меня так, как будто вам нужно вызвать validate() в вашем кадре. Кроме того, если вы используете f.pack(), вашей панели нужен предпочтительный размер, потому что pack() присваивает родительским компонентам их предпочтительные размеры и изменяет размеры на их основе.

1 голос
/ 10 марта 2010

Ну, если он работает с SSCCE, то вы доказали, что проблема не в базовой логике. Должно быть что-то другое между SSCCE и вашим реальным кодом. Поскольку у нас нет доступа к вашему реальному коду, вам нужно выполнить отладку самостоятельно, чтобы увидеть, в чем разница.

Однако в этом случае лучшим решением будет использование CardLayout, который позволяет легко менять панели. Прочитайте учебник Swing для рабочего примера.

Или другой подход - использовать «диалог входа в систему». После успешного входа в систему вы отображаете свой основной фрейм вместе с панелью для вашего приложения.

1 голос
/ 10 марта 2010

Может быть, вам не хватает frameThatContainsCentralPanel.pack()?

1 голос
/ 10 марта 2010

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

Если вы вызываете вышеуказанный код , а не в потоке событий, тогда у вас есть условие гонки и все ставки отключены - вы можете манипулировать GUI только из EDT (поток рассылки событий).


РЕДАКТИРОВАТЬ: Я попробовал ваш SSCCE в моей системе, и он не демонстрирует поведение, которое вы видите. Я пробовал это примерно 50 раз, а также пытался создать 10 окон, зацикливая ваш код. Я использую 1.6.0_18 в Windows XP SP3.

0 голосов
/ 29 июля 2010

Похоже на ошибку Java. Я сообщил об этом (но по какой-то причине он все еще не отображается в отчетах об ошибках).

0 голосов
/ 22 апреля 2010

Я бы ожидал, что кадр будет увеличен до того, как он будет показан, но после проверки я уверен, что в linux кадр будет развернут после того, как он был отображен. Вы можете сделать размер кадра равным размеру экрана перед вызовом setVisible, или вы можете сделать компоненты невидимыми, пока не узнаете, что они получили предпочтительный начальный размер. Вот модифицированный пример, который показывает элементы после активации кадра (на linux активированное событие приходит достаточно поздно, чтобы не показывать «прыгающую кнопку»):

import javax.swing.JButton;

import javax.swing.JFrame; импорт javax.swing.JPanel; import javax.swing.SwingUtilities;

открытый класс TEST {

public static void main(String[] args) throws Exception {

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {

            final JPanel btnPnl = new JPanel();
            btnPnl.add(new JButton("TEST"));

            final JFrame f = new JFrame("TEST");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setContentPane(btnPnl);
            // calculate preferred size for TEST frame
            // f.isDisplayable() will become true
            f.pack();

            // extended state, if can be applied, needs to be called after f.isDisplayable()
            WindowListener maxBoth = new WindowAdapter() {

                @Override
                public void windowOpened(WindowEvent e) {
                    f.setExtendedState(Frame.MAXIMIZED_BOTH);
                }
            };

            // after windows has been opened - maximize both
            f.addWindowListener(maxBoth);

            // initially hide the elements
            // after maximized state has been applied show them
            f.getContentPane().setVisible(false);
            f.addWindowListener(new WindowAdapter() {

                @Override
                public void windowActivated(WindowEvent e) {
                    f.getContentPane().setVisible(true);
                    // remove this listener
                    f.removeWindowStateListener(this);
                }
            });

            // set the frame visible
            f.setVisible(true);
        }
    });
}

}

0 голосов
/ 18 апреля 2010

Если я скопировал ваш код, у меня возникла та же проблема, но не такая тяжелая.

Я решил это, установив предпочтительный размер для вашей рамки перед упаковкой. Итак:

import java.awt.Dimension;

System.out.println("Debug test...");

JPanel btnPnl = new JPanel();
btnPnl.add(new JButton("TEST"));

JFrame f = new JFrame("TEST");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(btnPnl);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setVisible(true);
System.out.println("End debug test!");

Я работаю в Linux.

Это действительно странно ... Я уверен, что это что-то вроде размера всех контейнеров в дереве свинга.

...