Как стать автором
Обновить

Layout без layout'ов

Время на прочтение4 мин
Количество просмотров26K
Библиотека Swing появилась примерно 15 лет назад и все эти 15 лет КАЖДЫЙ кто начинает программировать на Java задаёт один и тот же вопрос:
— Почему я не могу просто добавить кнопки с полями на форму без изучения всех этих LayoutManager'ов?

В стандарной JRE содержится больше десятка классов для компоновки элементов (FlowLayout, GroupLayout и т.п.), постоянно появляются новые компоновщики типа MigLayout но ситуация не улучшается.

Рассмотрим варианты создания форм типа этой:

image


На скриншоте видно что некоторые поля меняют ширину в зависимости от размера формы, кнопки привязаны к границам окна т.е. компоновка не является тривиальным выставлением константных размеров/положения компонентов.

Использование визуального редактора типа Netbeans Matisse

Пример формы:

image

Рассмотрим код сгенерированный рисовалкой:

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, 126, Short.MAX_VALUE)
                    .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jButton1)
                    .addComponent(jPasswordField1)
                    .addComponent(jTextField2)
                    .addComponent(jTextField1))
                .addContainerGap(149, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jPasswordField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel3))
                .addGap(18, 18, 18)
                .addComponent(jButton1)
                .addContainerGap(166, Short.MAX_VALUE))
        );

        pack();


Код ужасен. Абсолютно невозможно разобраться что где находится и как что-то поменять руками. И это всего лишь несколько полей с одной кнопкой. А если нужно динамически подстраивать расположение/размер компонентов под размеры формы то количество кода увеличится в разы.

Вариант без layout'ов

Можно просто отключить компоновщик контейнера методом setLayout(null) и выставить размеры контролов вручную, через setSize и setLocation примерно как в старом добром Visual Basic'е.
Способ топорный но иногда это проще чем тратить время на полуавтоматическую компоновку.

Мой собственный вариант

Я опубликовал библиотеку для простого и наглядного рисования форм руками. Исходный код и пример использования можно скачать на https://code.google.com/p/layout-less/
Проект использует binding из библиотеки описанной в статье на http://habrahabr.ru/blogs/java/127076/

Код формы из первого скриншота выглядит так:
	layoutless = new Layoutless();
	int labelsWidth=150;
	layoutless
		.item(new ComponentBox()
		    .component(jLabel1)
		    .width(labelsWidth)
		    .height(22)
		    .x(0)
		    .y(8+25*0)
		    )
		.item(new ComponentBox()
		    .component(jTextField1)
		    .width(layoutless.width().minus(labelsWidth).minus(16).minus(50))
		    .height(22)
		    .x(labelsWidth+8)
		    .y(8+25*0)
		    )
		.item(new ComponentBox()
		    .component(jButton2)
		    .width(49)
		    .height(21)
		    .x(layoutless.width().minus(58))
		    .y(8+25*0)
		    )
		.item(new ComponentBox()
		    .component(jLabel2)
		    .width(labelsWidth)
		    .height(22)
		    .x(0)
		    .y(8+25*1)
		    )
		.item(new ComponentBox()
		    .component(jTextField2)
		    .width(layoutless.width().minus(labelsWidth).minus(16))
		    .height(22)
		    .x(labelsWidth+8)
		    .y(8+25*1)
		    )
		.item(new ComponentBox()
		    .component(jLabel3)
		    .width(labelsWidth)
		    .height(22)
		    .x(0)
		    .y(8+25*2)
		    )
		.item(new ComponentBox()
		    .component(jPasswordField1)
		    .width(layoutless.width().minus(labelsWidth).minus(16))
		    .height(22)
		    .x(labelsWidth+8)
		    .y(8+25*2)
		    )
		.item(new ComponentBox()
		    .component(jButton1)
		    .width(90)
		    .height(27)
		    .x(labelsWidth+8)
		    .y(layoutless.height().minus(40))
		    )
		.item(new ComponentBox()
		    .component(iconLabel)
		    .width(128)
		    .height(128)
		    .x(0)
		    .y(layoutless.height().minus(140))
		    )		
		;
	this.add(layoutless, BorderLayout.CENTER);


— описание компоновки полей вполне читабельно.

Дополнительно:
— размеры и положение полей могут быть динамически привязаны к границам контейнера или другим переменным
— компоненты могут накладываться друг на друга (см. картинку с ключами)
Теги:
Хабы:
Всего голосов 47: ↑31 и ↓16+15
Комментарии120

Публикации

Истории

Работа

Java разработчик
402 вакансии

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
24 сентября
Astra DevConf 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн