betacode

Программирование приложения Java Desktop с использованием SWT

  1. Введение
  2. RCP (Rich Client Platform)
  3. Необходимые настройки для начала
  4. Некоторые понятия о SWT.
  5. Создать RCP Plugin Project
  6. Первый пример
  7. Использование WindowBuilder
  8. SWT Widget
  9. SWT Layout
  10. Написать расширенные классы из widget в SWT
  11. Module становится компонентом интерфейса
  12. Обработка события
  13. JFace

1. Введение

Статья основана на:
  • Eclipse 4.6, 47 (NEON, OXYGEN)

В данной статье я ознакомлю вас с программированием приложения Desktop с SWT.

Вопросом является, есть ли в Java способ выбора технологии для программирования приложения Desktop, или какая технология помогает программировать веб приложение с интерфейсом похожим на приложение Desktop.

Смотрите так же:

2. RCP (Rich Client Platform)

RCP (Rich Client Platform) - Это платформа (Platform) разработанная на основе SWT, используется для программирования приложений Desktop и еще дальше он построил для вас платформу, позволяющую вас разрабатывать приложения Desktop вида Workbench, похоже на Eclipse IDE, или программировать Plugin которые могут интегрировать в Eclipse IDE.

Но даже если вы хотите использовать только SWT для программирования не использовать то, что предоставлено RCP-ом вам так же нужно создать приложение RCP.
SWT:
Workbench Application:
Чтобы создать приложение Desktop используя SWT. В Eclipse мы создадим RCP Plugin project. У вас есть 2 варианта.
  • Использовать только свойства SWT
  • Использовать платформу предоставленную RCP для программирования приложения RCP Workbench.
В данной статье я покажу вам как ознакомиться с программированием стандартного SWT, используя WindowBuilder чтобы перетаскивать компоненты в интерфейс.

3. Необходимые настройки для начала

Есть необходимые настройки, которые нужно сделать перед тем, как начать:

Вам нужно иметь Eclipse самой новой версии. Сейчас это Eclipse 4.7 (Код OXYGEN).
По-моему вам нужно скачать пакет: "Eclipse IDE for Java EE Developers". Пакеты отличаются колличеством Plugin, для разных целей программирования. В процессе программирования можно установить дополнительно Plugin для других целей.
Установка Plugin WindowBuilder, это 1 Plugin позволяющий вас сделать интерфейс дизайна приложения SWT с помощью перетаскивания, очень удобно.

Смотрите инструкцию установки по ссылке:

4. Некоторые понятия о SWT.

Display & Shell
Класс Display и Shell являются ключевыми компонентами приложения SWT.

- org.eclipse.swt.widgets.Shell описывает окно (Window)

- org.eclipse.swt.widgets.Display отвечает за управление цикла события (event loops), шрифт, цвет и контролирует информацию коммуникации между потоками интерфкйса пользователя (UI Thread) и другими потоками (other Thread). Display это основа для всех способностей свойств SWT.

Каждое приложение SWT требует минимум один Display и один или более объектов Shell.
Пример:
Display display = new Display();
Shell shell = new Shell(display);
shell.open();

// run the event loop as long as the window is open
while (!shell.isDisposed()) {
   // read the next OS event queue and transfer it to a SWT event
 if (!display.readAndDispatch())
  {
 // if there are currently no other OS event to process
 // sleep until the next OS event is available
   display.sleep();
  }
}

// disposes all associated windows and their components
display.dispose();
SWT Widgets
SWT widget установлены в пакетах org.eclipse.swt.widgets и org.eclipse.swt.custom. Этом widget расширен из класса Widget или Control. Некоторые widget иллюстрированы в изображении ниже.
Детали вы можете просмотреть в SWT widget homepage.

5. Создать RCP Plugin Project

В Eclipse выбрать:
  • File/New/Other...
  • Так как нет надобности создать приложение Workbench поэтому мы не выбираем (1) как в изображении ниже.
  • Выбрать Yes в области (2) чтобы Eclipse создал RCP Application (Работающий на Desktop), напротив он создаст RAP Application (Работающий на Web).
Project создан:
Добавить библиотеку swt: org.eclipse.swt

Если вы программируете приложение RAP, соответствующей библиотекой является org.eclipse.rap.rwt

6. Первый пример

Это простой пример, не используя инструмент перетаскивания WindowBuilder.
HelloSWT.java
package org.o7planning.tutorial.swt.helloswt;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class HelloSWT {

  public static void main(String[] args) {
      // Create Display
      Display display = new Display();
      // Create Shell (Window) from diplay
      Shell shell = new Shell(display);

      shell.open();

      while (!shell.isDisposed()) {
          if (!display.readAndDispatch())
              display.sleep();
      }
      display.dispose();
  }
}
Нажмите на правую кнопку мыши на HelloSWT.java и выберите Run As/Java Application.
Результаты запуска примера:

7. Использование WindowBuilder

Далее, мы создадим пример с перетаскиванием на WindowBuilder.
  • File/New/Other ..
Это окно дизайна в WindowBuilder. Оно позволяет вам легко перетаскивать Widget.
You can watch the video below:

8. SWT Widget

Обзор
Это иерарзия Widget в SWT.
Вы можете посмотреть демо (demo) про Control по ссылке ниже, это RWT Control, но на самом деле они похожи на SWT control.
Demo:
Widget могут содержать другие Widget (Container)
Control

9. SWT Layout

Что такое Layout?
Проще говоря, Layout это способ распорядка компонентов на интерфейсе.
Стандартные Layout в SWT это:
  • FillLayout – Выравнивает размер Widget в строке или столбце
  • RowLayout – Связывает Widget в строке или заполненной строке (Fill), обернутой (Wrap), и с вариантами пробела (space).
  • GridLayout – Связывает Widget в сети
Онлайн пример
Это пример онлайн, позволяющий вам увидеть действия Layout.
  • TODO Link?
FillLayout
FillLayout это простой класс layout. н расставляет виджет (Widget) в одной строке или столбце, заставляя их иметь один размер. Сначала Widget иметь высоту как самый высокий Widget, и широту как самый широкий widget. FillLayout не wrap (обертывает) Widget, и вы не можете определить грань (margin) или расстояние (space).
FillLayout fillLayout = new FillLayout();
fillLayout.type = SWT.VERTICAL;
shell.setLayout(fillLayout);
Сначала
После изменения размера
fillLayout.type = SWT.HORIZONTAL
(Mặc định)
fillLayout.type = SWT.VERTICAL
Video:
FillLayoutExample.java
package org.o7planning.tutorial.swt.layout;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class FillLayoutExample {

  public static void main(String[] args) {
      Display display = new Display();
      final Shell shell = new Shell(display);
      shell.setLayout(new FillLayout());

      //
      Composite parent = new Composite(shell, SWT.NONE);
     
      FillLayout fillLayout= new FillLayout();
      fillLayout.type= SWT.VERTICAL;
     
      parent.setLayout(fillLayout);

      Button b1 = new Button(parent, SWT.NONE);
      b1.setText("B1");

      Button b2 = new Button(parent, SWT.NONE);
      b2.setText("B2");

      Button button3 = new Button(parent, SWT.NONE);
      button3.setText("Button 3");
     
      // Windows back to natural size.
      shell.pack();
      //
      shell.open();
      while (!shell.isDisposed()) {
          if (!display.readAndDispatch())
              display.sleep();
      }
      // tear down the SWT window
      display.dispose();
  }
}
Результаты запуска примера:
RowLayout
RowLayout используются больше чем FillLayout так как он имеет способность wrap (обертывать), и потому что он поддерживает margin и spacing. RowLayout имеет некоторые поля (field) конфигурации. Помимо этого, высота и ширина каждого widget в каждом RowLayout может быть определен созданием объекта RowData для widget используя setLayoutData
The field configuration:
Wrap, Pack, Justify:
Сначала
После изменения размера
wrap = true
pack = true
justify = false

(По умолчанию)
wrap = false

(Немного отрезан если не хватает пространства)
pack = false

(Все Widget имеют одинаковый размер)
justify = true

(Widget будут распространены на готовом пространстве и поровну)
MarginLeft, MarginTop, MarginRight, MarginBottom, Spacing:
Поля (field) контролирующие пиксели (Pixel) между widget (space - расстояние) и количество пикселей между widget и стороной родительского Composite (margin - грань). По умолчанию, RowLayout использует 3 Pixel для выравнивания (margin) и интервалом (space). Смотрите детали в следующем изображении.
Video:
GridLayout
GridLayout это самый полезный и сильный в стандартных Layout, но так же являеся самым сложным. С GridLayout, дочерний Widget одного Composite будет расставлен в сетке. GridLayout имеет некоторые конфигурационные поля, и так же как RowLayout, Widget расположенные в GridLayout могут иметь связанный объект данных Layout, так же называется GridData. Сила GridLayout находится в способности конфигурации GridData для каждого widget управляемый с помощью GridLayout.
Конфигурации GridLayout:
  • NumColumns
  • MakeColumnsEqualWidth
Video GridLayout:
StackLayout
StackLayout расставляет все Widget в стопку, они будут иметь одинаковый размер и местоположение. Поле topControl определяет какой Widget находится в самом верху и видно. Пользователь должен настроить значение topControl и потом вызвать метод layout() родительского Composite.
Video StackLayout:
Комбинирование Layout
Выше мы ознакомились со стандартными Layout, и сочетаниями разных Layout, и разными контейнерами (Container) (Composite, TabFolder, SashForm,.. ) которые создают желаемый интерфейс.
  • TODO

10. Написать расширенные классы из widget в SWT

Иногда вам нжуно написать расширенный класс из класса widget готовый в SWT. Это совершенно нормально, но есть маленькое примечание, вам нужно переопределить метод checkSubclass() ничего не делая в том методе.
MyButton.java
package org.o7planning.tutorial.swt.swtsubclass;

import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;

public class MyButton extends Button {

   public MyButton(Composite parent, int style) {
       super(parent, style);
   }

   // You have to override this method.
   @Override
   protected void checkSubclass() {
       // No need to do anything.
   }

   
}

11. Module становится компонентом интерфейса

В случае, когда вам нужно сделать дизайн сложного интерфейса. Разделение дизайна это необходимо и потом соединить их снова, с дизайном на WindowBuilder это становится легче.

Посмотрим следующий интерфейс, мы попытаемся разделить его.
Предположим, вы хотите сделать дизайн похожим на изображение ниже. (Он сложный и нужно разделить дизайн, но это иллюстрация примера разделения дизайна интерфейса)
Мы можем сделать дизайн 2-х отдельных Composite и соединить их на MainComposite.
TopComposite
  • File/New/Other...
Дизайн интерфейса для TopComposite.
TopComposite.java
package org.o7planning.tutorial.swt.module;

import org.eclipse.swt.widgets.Composite;

public class TopComposite extends Composite {
  private Text text;

  /**
   * Create the composite.
   * @param parent
   * @param style
   */
  public TopComposite(Composite parent, int style) {
      super(parent, style);
      setLayout(new FillLayout(SWT.HORIZONTAL));
     
      Composite composite = new Composite(this, SWT.NONE);
      composite.setLayout(new GridLayout(1, false));
     
      Button btnPreferredSite = new Button(composite, SWT.CHECK);
      btnPreferredSite.setText("Preferred Site");
     
      Label lblColumnWidth = new Label(composite, SWT.NONE);
      lblColumnWidth.setText("Column width");
     
      text = new Text(composite, SWT.BORDER);
      text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

  }

  @Override
  protected void checkSubclass() {
  }
}
BottomComposite
Индентично создайте класс BottomComposite:
Дизайн интерфейса для BottomComposite:
BottomComposite.java
package org.o7planning.tutorial.swt.module;

import org.eclipse.swt.SWT;

public class BottomComposite extends Composite {
  private Text text;

  /**
   * Create the composite.
   * @param parent
   * @param style
   */
  public BottomComposite(Composite parent, int style) {
      super(parent, style);
      setLayout(new FillLayout(SWT.HORIZONTAL));
     
      Composite composite = new Composite(this, SWT.NONE);
      composite.setLayout(new GridLayout(1, false));
     
      Composite composite_1 = new Composite(composite, SWT.NONE);
      GridLayout gl_composite_1 = new GridLayout(3, false);
      gl_composite_1.marginHeight = 0;
      gl_composite_1.marginWidth = 0;
      composite_1.setLayout(gl_composite_1);
      composite_1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
     
      Button btnNewButton = new Button(composite_1, SWT.NONE);
      btnNewButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
      btnNewButton.setText("Add");
     
      Button btnNewButton_1 = new Button(composite_1, SWT.NONE);
      btnNewButton_1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
      btnNewButton_1.setText("Delete");
     
      Button btnNewButton_2 = new Button(composite_1, SWT.NONE);
      btnNewButton_2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
      btnNewButton_2.setText("Clear");
     
      text = new Text(composite, SWT.BORDER | SWT.MULTI);
      text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

  }

  @Override
  protected void checkSubclass() {
      // Disable the check that prevents subclassing of SWT components
  }

}
MainComposite
Индентично создайте класс MainComposite:
Регистрация TopComposite & BottomComposite на Palette
Нажмите на правую кнопку мыши на Palette и выберите Add category...
Назовите Pallete Category:
  • My Composite
Нажмите на правую кнопку мыши на "My Composite" чтобы добавить TopComposite & BottomComposite.
Индентично добавьте BottomComposite в каталог My Composite.
Теперь TopComposite & BottomComposite могут быть легко перетащены в другие Composite.
MainComposite.java
package org.o7planning.tutorial.swt.module;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;

public class MainComposite extends Composite {

  /**
   * Create the composite.
   * @param parent
   * @param style
   */
  public MainComposite(Composite parent, int style) {
      super(parent, style);
      setLayout(new FillLayout(SWT.HORIZONTAL));
     
      Composite composite = new Composite(this, SWT.NONE);
      composite.setLayout(new GridLayout(1, false));
     
      TopComposite topComposite = new TopComposite(composite, SWT.BORDER);
      topComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
     
      BottomComposite bottomComposite = new BottomComposite(composite, SWT.BORDER);
      bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

  }

  @Override
  protected void checkSubclass() {
  }

}

12. Обработка события

No ADS
Обработка события SWT является очень простой с поддержкой WindowBuilder.
Иллюстрированные примеры:
  • File/New/Other...
  • Package: org.o7planning.tutorial.swt.event1
  • Name: ButtonEventDemo
Кликнете правой кнопкой мыши на Button, выберите "Add event handler", отобразится серия событий соответствующие с Button.
WindowBuilder автоматически создает для вас code:
btnClickToMe.addSelectionListener(new SelectionAdapter() {
    @Override
    public void widgetSelected(SelectionEvent e) {
        // Sử lý sự kiện Button được chọn tại đây.
        System.out.println("Button selected!");
    }
});
ButtonEventDemo.java
package org.o7planning.tutorial.swt.event1;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class ButtonEventDemo {

  protected Shell shlButtonEventDemo;

  public static void main(String[] args) {
      try {
          ButtonEventDemo window = new ButtonEventDemo();
          window.open();
      } catch (Exception e) {
          e.printStackTrace();
      }
  }

  public void open() {
      Display display = Display.getDefault();
      createContents();
      shlButtonEventDemo.open();
      shlButtonEventDemo.layout();
      while (!shlButtonEventDemo.isDisposed()) {
          if (!display.readAndDispatch()) {
              display.sleep();
          }
      }
  }

  protected void createContents() {
      shlButtonEventDemo = new Shell();
      shlButtonEventDemo.setSize(296, 205);
      shlButtonEventDemo.setText("Button Event Demo");
      shlButtonEventDemo.setLayout(new RowLayout(SWT.HORIZONTAL));

      Button btnClickToMe = new Button(shlButtonEventDemo, SWT.NONE);
      btnClickToMe.addSelectionListener(new SelectionAdapter() {
          @Override
          public void widgetSelected(SelectionEvent e) {
              // Handle Button selected here!
              System.out.println("Button selected!");
          }
      });
      btnClickToMe.setText("Click to me");

  }

}

13. JFace

Вы можете посмотреть больше про JFace, дополнительный API для SWT:
No ADS

Технология Eclipse

Show More

Руководства Java SWT

Show More