Статьи

Взятие на GUI Builders с помощью Swing JavaBuilder

Как автор библиотеки Swing JavaBuilder , я хотел бы представить вам простое руководство, объясняющее, что оно может сделать для вас и как оно может максимизировать производительность разработки на Swing.

Поскольку ручное программирование Swing чрезвычайно обременительно, большинству разработчиков приходится прибегать к использованию специфичных для IDE сборщиков графического интерфейса, таких как Matisse NetBeans или WindowBuilder Eclipse. Несмотря на то, что это прекрасные решения, все разработчики визуальных графических интерфейсов имеют ряд общих недостатков:

  • они специфичны для IDE. Как только вы начнете использовать Matisse или WindowBuilder, ваши шансы легко переместить ваш код из одной IDE в другую почти равны нулю.
  • они часто генерируют крайне не поддерживаемый код (особенно если используется GroupLayout, такой как Matisse, который в принципе невозможно для человека, чтобы кодировать вручную)
  • их часто трудно поддерживать, если необходимо внести серьезные изменения в экран (у меня было несколько боев с Матиссом, когда казалось, что проще кодировать панель с нуля, чем переделывать существующую).

Библиотека Swing JavaBuilder направлена ​​на исправление всего этого (а затем и некоторых), позволяя декларативно кодировать весь пользовательский интерфейс в файле YAML (расширенный набор JSON, популярный в Ruby on Rails, который использует отступы для пробелов в иерархических структурах, что-то вроде Python). ).

Помимо радикального сокращения объема кода, необходимого для создания ваших элементов управления, JavaBuilder Swing предлагает также инновационный DSL для управления компоновкой, который работает поверх MigLayout (менеджера компоновки, который фактически сделал весь JDK-менеджер компоновки устаревшим), что по сути является чем-то вроде GUI Builder, но в чистом тексте.

Все это подробно описано в нашей PDF-книге , поэтому, если этот урок вам интересен, пожалуйста, обратитесь к нему для более подробных объяснений. В отличие от многих проектов с открытым исходным кодом, Swing JavaBuilder поставляется с подробной документацией для разработчиков. Вы не должны удивляться через лабиринт отключенных страниц Javadoc или исходных файлов Java, чтобы понять, как это работает.

С этим введением давайте продолжим и создадим простое, типичное бизнес-приложение для ввода данных человека.

В отличие от JavaFX с ним потоковое видео и блестящие кнопки, которые «пинг!» когда вы нажимаете на них (как эта машина в «Смысле жизни» Монти Пайтона), JavaBuilder Swing нацелен прямо на эти скучные бизнес-приложения с десятками полей, правилами проверки и долго выполняющимися процессами сохранения. Короче говоря, тип приложений, которые, вероятно, 95% из нас пишут для повседневной жизни.

Единственное, что вам нужно для начала, — это загрузить версию 0.3.FINAL Swing JavaBuilder и плагин редактора YAML для Eclipse . В NetBeans редактор YAML является частью базовой установки, а в IDEA я считаю, что он является частью плагина Ruby on Rails.

Давайте создадим новый Java-проект под названием «PersonApp». Добавьте к нему jar Swing JavaBuilder и все его зависимости (находится в папке «/ lib»). Создайте новый пакет «person.app». В нем давайте создадим наш модельный класс, который представляет человека:

package person.app;

import java.text.MessageFormat;

public class Person {

    private String firstName;
    private String lastName;
    private String emailAddress;
   
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmailAddress() {
        return emailAddress;
    }
    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
    @Override
    public String toString() {
        return MessageFormat.format("{0} {1} : {2}", getFirstName(), getLastName(), getEmailAddress());
    }
}

Далее, давайте создадим наш файл ресурсов «PersonApp.properties» в пакете по умолчанию (я упоминал, что Swing JavaBuilder поставляется с интегрированной поддержкой пакетов ресурсов из коробки?):

button.save=Save
button.cancel=Cancel

label.firstName=First Name
label.lastName=Last Name
label.email=Email

frame.title=Enter Person Data

Если в main () вы добавите комплект ресурсов в основную конфигурацию Swing JavaBuilder, он автоматически переключится в режим интернационализации и с этого момента все будут ожидать, что все соответствующие текстовые свойства будут иметь форму ключей ресурсов, а не фактических текст. Следовательно, вам не нужно вручную извлекать ресурсы из пакетов, все это делается автоматически.

Далее давайте создадим реальное приложение, а именно PersonApp:

package person.app;

 
import javax.swing.JFrame;

import javax.swing.JOptionPane;

import javax.swing.SwingUtilities;

import javax.swing.UIManager;

 
import org.javabuilders.BuildResult;

import org.javabuilders.annotations.DoInBackground;

import org.javabuilders.event.BackgroundEvent;

import org.javabuilders.event.CancelStatus;

import org.javabuilders.swing.SwingJavaBuilder;

 
public class PersonApp extends JFrame {

 
    private Person person;

 
    private BuildResult result;

     
    public PersonApp() {

        person = new Person();

        person.setFirstName("John");

        person.setLastName("Smith");

        result = SwingJavaBuilder.build(this);

    }

 
    public Person getPerson() {

        return person;

    }

     
    private void cancel() {

        setVisible(false);

    }

     
    @DoInBackground(cancelable=true, indeterminateProgress=false, progressStart=1, progressEnd=100)

    private void save(BackgroundEvent evt) {

        //simulate a long running save to a database

        for(int i = 0; i < 100; i++) {

            //progress indicator

            evt.setProgressValue(i + 1);

            evt.setProgressMessage("" + i + "% done...");

             
            //check if cancel was requested

            if (evt.getCancelStatus() != CancelStatus.REQUESTED) {

                 
                //sleep

                try {

                    Thread.sleep(100);

                } catch (InterruptedException e) {}

                 
            } else {

                //cancel requested, let's abort

                evt.setCancelStatus(CancelStatus.COMPLETED);

                break;

            }

        }

    }

     
    //runs after successful save

    private void done() {

        JOptionPane.showMessageDialog(this, "Person data: " + person.toString());

    }

     
    /**

     * @param args

     */

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                //activate internationalization

                SwingJavaBuilder.getConfig().addResourceBundle("PersonApp");

                try {

                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

                    new PersonApp().setVisible(true);

                } catch (Exception e) {

                    e.printStackTrace();

                }

            }

        });

    }

}

 

Давайте разберемся с этим и сосредоточимся на некоторых ключевых строках кода:

В основном()

 //activate internationalization

SwingJavaBuilder.getConfig().addResourceBundle("PersonApp");

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

В конструкторе мы имеем

    private BuildResult result;




public PersonApp() {


...

result = SwingJavaBuilder.build(this);


}

Эта строка кода выполняет весь фактический синтаксический анализ и чтение файла YAML (который должен находиться в том же пакете и иметь то же имя, что и класс Java, но с расширением «.yaml», соглашение о конфигурации, которое мы используем ущипнул от Apache Wicket, очевидно).

Это все, что вам нужно для создания этого JFrame из файла YAML. Все остальные методы Java в классе являются чисто бизнес-логическими методами:

private void cancel() {...}
 @DoInBackground(cancelable=true, indeterminateProgress=false, progressStart=1, progressEnd=100)

private void save(BackgroundEvent evt) {

...}

Аннотация @DoInBackground помечает этот метод как длительный бизнес-процесс, который должен автоматически запускаться в фоновом потоке с помощью SwingWorker.

Теперь, когда мы увидели простую и понятную логику Java, давайте перейдем к нашей части сопротивления, то есть к самому файлу YAML.

Как я упоминал ранее, создайте файл «PersonApp.yaml» в том же пакете person.app, где находится файл PersonApp.java:

JFrame(name=frame, title=frame.title, size=packed, defaultCloseOperation=exitOnClose):
    - JLabel(name=fNameLbl, text=label.firstName)
    - JLabel(name=lNameLbl, text=label.lastName)
    - JLabel(name=emailLbl, text=label.email)
    - JTextField(name=fName)
    - JTextField(name=lName)
    - JTextField(name=email)
    - JButton(name=save, text=button.save, onAction=($validate,save,done))
    - JButton(name=cancel, text=button.cancel, onAction=($confirm,cancel))
    - MigLayout: |
        [pref]     [grow,100]   [pref]     [grow,100]
        fNameLbl   fName        lNameLbl   lName
        emailLbl   email+*
        >save+*=1,cancel=1
bind:
    - fName.text: this.person.firstName
    - lName.text: this.person.lastName
    - email.text: this.person.emailAddress
validate:
    - fName.text: {mandatory: true, label: label.firstName}
    - lName.text: {mandatory: true, label: label.lastName}
    - email.text: {mandatory: true, emailAddress: true, label: label.email}

[в случае, если программное обеспечение форума JL не отображает интервал в этом файле правильно, просто проверьте исходный исходный код прямо в нашем репозитории GitHub: ttp: //github.com/jacek99/javabuilders/blob/44cb39db6ec0a80c0cf060716082816dbbb2epps/Person человек / приложение / PersonApp.yaml]
 

Некоторые вещи очевидны при взгляде на это. Отступы на основе пробелов дают нам иерархию «родитель-потомок» всех элементов управления. Как видите, все свойства «text» указывают на ключ ресурса, поэтому фактический текст будет выбираться во время выполнения из пакета ресурсов, который мы выбрали в начале.

Раздел MigLayout содержит наш DSL для управления макетом. Он в основном выравнивает имена элементов управления в строках и столбцах и позволяет использовать все обычные ключевые слова MigLayout, а также несколько простых символов для управления выравниванием / охватом ячеек и строк и т. Д. Полные спецификации DSL приведены в нашей книге PDF, поэтому, пожалуйста, найдите их в полный список опций.

Раздел «bind» определяет привязку данных между свойствами элемента управления и свойствами на стороне Java. Для реализации этого используется обычная библиотека Beans Binding (JSR 295). В отличие от других решений, логика привязки данных не смешивается с определениями элементов управления, а находится в отдельном узле. Это обеспечивает лучшую ясность и разделение проблем.

Узел validate определяет базовую проверку входных данных. Под капотом это реализовано с использованием Apache Commons Validators (нам не нравится изобретать велосипед).

Теперь давайте возьмем этого малыша и посмотрим, что он действительно может сделать.

 Запустив приложение, мы должны увидеть:

 

 Обратите внимание, что макет создан, все элементы управления выровнены по базовой линии (благодаря чудесам MigLayout), и даже кнопки «Сохранить» и «Отмена» имеют одинаковый размер (все это возможно для нашего MigLayout DSL). Кроме того, благодаря привязке данных имя и фамилия человека были автоматически переведены из класса Java в пользовательский интерфейс.

Давайте введем неверный адрес электронной почты и нажмите Сохранить:

Интегрированная валидация проверила входные данные по валидатору электронной почты в Apache Commons Validators и автоматически показала пользователю сообщение об ошибке.

Давайте введем действительный адрес электронной почты:

и нажмите Сохранить. Кнопка «Сохранить» была предназначена для выполнения метода save () на стороне Java (через раздел «onAction = save» в YAML), который, в свою очередь, был помечен как метод, который должен выполняться в фоновом режиме с помощью Swing Worker (с встроенный индикатор выполнения и возможность отмены, если требуется):

 После успешного сохранения вызывается метод done (), который отображает данные человека. Обратите внимание, что адрес электронной почты, который мы ввели в пользовательском интерфейсе, автоматически передается компоненту Person с помощью привязки данных:

Теперь мы можем нажать Отмена. Поскольку файл YAML запросил подтверждение с помощью глобальной команды «$ verify» перед выполнением метода cancel:

- JButton(name=cancel, text=button.cancel, onAction=($confirm,cancel))

мы получаем стандартное подтверждение:

В целом у нас есть прекрасно работающее типичное бизнес-приложение с интегрированным управлением созданием элементов управления / связыванием данных / привязкой данных / поддержкой Swing Worker / проверкой входных данных менее чем в 25 строках YAML и с классом Java, где большая часть логики связана исключительно с бизнесом и имеет мало, если таковые имеются, специфичный для пользовательского интерфейса код в нем.

Я надеюсь, что этот маленький пример заинтересует вас Swing JavaBuilder. Для поклонников других инструментариев GUI у нас есть несколько ранних сборок SWT JavaBuilder, и в настоящее время проводится работа над GTK + JavaBuilder (который будет использовать привязки Java-GNOME для создания собственных приложений Linux с Java).

Ура и счастливого (продуктивного) строительства!

PS Вы можете просмотреть исходные файлы для этого учебного приложения в нашем репозитории GitHub:

http://github.com/jacek99/javabuilders/blob/44cb39db6ec0a80c0cf060716082816dbbb2e963/PersonApp/src/person/app/Person.java

http://github.com/jacek99/javabuilders/blob/44cb39db6ec0a80c0cf060716082816dbbb2e963/PersonApp/src/person/app/PersonApp.java

http://github.com/jacek99/javabuilders/blob/44cb39db6ec0a80c0cf060716082816dbbb2e963/PersonApp/src/person/app/PersonApp.yaml