Java Swing: проблемы с шириной

У меня проблемы с пониманием поведения моего приложения. Я хочу создать простое окно (1000x700px), разделенное на две части (ширина 250px и 750px соответственно). Я попробовал следующий код:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example extends JFrame
{
    private static final long serialVersionUID = 1L;

    public Example()
    {
        this.setSize(1000, 700);
        this.setTitle("Example");
        this.setResizable(false);
        this.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));

        JPanel navigation_panel_wrap = new JPanel();
        JPanel content_panel_wrap = new JPanel();
        navigation_panel_wrap.setPreferredSize(new Dimension(250, 700));
        content_panel_wrap.setPreferredSize(new Dimension(750, 700));
        content_panel_wrap.setBackground(Color.green);
        navigation_panel_wrap.setBackground(Color.red);
        this.getContentPane().add(navigation_panel_wrap);
        this.getContentPane().add(content_panel_wrap);
    }

    public static void main(String[] args)
    {
        Example example = new Example();
        example.setVisible(true);
    }
}

Как видите, я вручную установил диспетчер компоновки для JFrame (FlowLayout вместо BorderLayout с нулевыми горизонтальными и вертикальными промежутками). Конечно, я могу просто использовать BorderLayout, а затем использовать метод add() с параметрами BorderLayout.EAST и BorderLayout.WEST, но я хочу понять, что не так с FlowLayout. Когда я запускаю свое приложение, я получаю следующее (без зеленого JPanel): введите здесь описание изображения

Если я уменьшу ширину, например, content_panel_wrap и сделаю ее 744px вместо 750px, все будет работать правильно. введите здесь описание изображенияТак вот вопрос - что это за странные 6 пикселей? Я не уверен, что это значение является постоянным для всех операционных систем, поэтому я хочу понять его происхождение.


person Kirill Smirnov    schedule 24.10.2012    source источник
comment
См. этот Вопросы и ответы.   -  person trashgod    schedule 24.10.2012
comment
Большое спасибо, очень полезная ссылка.   -  person Kirill Smirnov    schedule 24.10.2012


Ответы (2)


Что касается вашей проблемы с кодами (+1 к @Reimeus), то вызов pack() - это решение. согласно документам:

Изменяет размер этого окна в соответствии с предпочтительным размером и расположением его подкомпонентов. Если окно и/или его владелец еще не отображаются, они оба делаются отображаемыми перед вычислением предпочтительного размера. Окно будет проверено после вычисления предпочитаемого размера.

Советы:

  • Не расширяйте JFrame без необходимости.
  • Используйте поток отправки событий при создании и изменении компонентов пользовательского интерфейса:

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
          // create UI components etc here
        }
      });
    
  • Не вызывайте setPreferredSize(), а переопределите getPrefferedSize() компонента.
  • Не вызывайте setSize(...) на JFrame, а вызывайте JFrame#pack() перед тем, как сделать его видимым.
  • Не забудьте вызвать JFrame#defaultCloseOperation(..), иначе ваш начальный поток/поток EDT не будет завершен, когда JFrame будет закрыт.

Вот пример сочетания моего совета и вашего кода:

введите здесь описание изображения

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Example {

    private final JFrame frame;

    public Example() {
        frame = new JFrame();
        frame.setTitle("Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//app exited when frame closes
        frame.setResizable(false);
        frame.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));

        JPanel navigation_panel_wrap = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(250, 700);
            }
        };
        JPanel content_panel_wrap = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(750, 700);
            }
        };

        content_panel_wrap.setBackground(Color.green);
        navigation_panel_wrap.setBackground(Color.red);

        frame.add(navigation_panel_wrap);
        frame.add(content_panel_wrap);
        //pack frame (size components to preferred size)
        frame.pack();
        frame.setVisible(true);//make frame visible
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Example();
            }
        });
    }
}
person David Kroukamp    schedule 24.10.2012
comment
Спасибо за такой подробный ответ! Все ясно, кроме двух вещей: 1. Не расширяйте JFrame без необходимости. Мне не следует расширять все классы Swing, такие как JLabel, JPanel и т. д., или только JFrame? И по какой причине это делать? 2. В чем преимущество переопределения getPrefferedSize() по сравнению с использованием setPreferredSize()? - person Kirill Smirnov; 24.10.2012
comment
@ user1600356 +1 приятно. Что касается JFrame вопроса о расширении: 1) вы расширяете классы только для добавления функциональности 2) после расширения JFrame вы не можете расширять другой класс, который вам может ФАКТИЧЕСКИ понадобиться. Что касается вашего вопроса: В чем преимущество переопределения getPrefferedSize() по сравнению с использованием setPreferredSize()? см. эту ссылку: Должен ли я избегать использования методов set[Preferred|Maximum|Minimum]Size в Java Swing? - person David Kroukamp; 24.10.2012

В FlowLayout нет ничего плохого, но вам нужно будет вызвать pack() для изменения размера всех компонентов.

person Reimeus    schedule 24.10.2012