Бібліотека Swing

Бібліотека Swing

      Сучасні програми потребують графічного інтерфейсу користувача (GUI). Користувачі відвикли працювати через консоль: вони керують програмою і вводять вхідні дані за допомогою так званих елементів управління (в програмуванні їх також називають візуальними компонентами), до яких відносяться кнопки, текстові поля, списки, що випадають і т.д.

      Кожна із сучасних мов програмування надає безліч бібліотек для роботи зі стандартним набором елементів управління. Нагадаємо, що під бібліотекою в програмуванні набір готових класів та інтерфейсів, призначених для вирішення певного кола завдань.

      У Java є декілька бібліотек візуальних компонентів для створення графічного інтерфейсу користувача. Найбільш рання з них називається AWT. Вважається, що при її проектуванні був допущений ряд недоліків, внаслідок яких з нею досить складно працювати. Бібліотека Swing розроблена на базі AWT і замінює більшість її компонентів своїми, спроектованими більш ретельно і зручно. Найновіша бібліотека, називається JavaFX.

      Кожна бібліотека надає набір класів для роботи з кнопками, списками, вікнами, меню і т.д., але ці класи спроектовані по-різному: вони мають різний набір методів з різними параметрами, тому «перевести» програму з однієї бібліотеки в іншу (наприклад , з метою збільшення швидкодії) не так-то просто. Це майже як перейти з однієї мови програмування на іншу: всі мови вміють робити одне і те ж, але у кожного з них свій синтаксис, своя програмна структура і свої численні хитрощі.

      Ми з вами почнемо створення графічного інтерфейсу з бібліотеки Swing. Повноцінний графічний інтерфейс може бути розроблений з її допомогою.


Вікно JFrame

      Кожна GUI-програма працює у вікні і по ходу роботи може відкривати кілька додаткових вікон.
      У бібліотеці Swing є описаний клас JFrame, що представляє собою вікно з рамкою і рядком заголовка (з кнопками «Згорнути», «На весь екран» і «Закрити»). Воно може змінювати розміри і переміщатися по екрану.

      У Swing є ще кілька класів вікон. Наприклад, JWindow- найпростіше вікно, без рамки і без рядка заголовка. Зазвичай з його допомогою робиться заставка до програми, яка перед запуском повинна виконати кілька тривалих дій (наприклад, завантажити інформацію з БД).

      Конструктор JFrame()без параметрів створює порожнє вікно. 

      Конструктор JFrame(String title)створює порожнє вікно з заголовком title.
Щоб написати просту програму, що виводить на екран порожнє вікно, нам потрібно ще три методи:


setSize(int width, int height) - Встановлює розміри вікна. Якщо не поставити розміри, вікно буде мати нульову висоту незалежно від того, що в ньому знаходиться і користувачеві після запуску доведеться розтягувати вікно вручну. Розміри вікна включають не тільки «робочу» область, а й межі і рядок заголовка.


setDefaultCloseOperation(int operation) - Дозволяє вказати дію, яку потрібно зробити, коли користувач закриває вікно натисканням на хрестик. Зазвичай в програмі є одне або кілька вікон при закритті яких програма припиняє роботу. Для того, щоб запрограмувати це поведінка, слід як параметр operation передати константу EXIT_ON_CLOSE, описану в класі JFrame.


setVisible(boolean visible) - Коли вікно створюється, воно за замовчуванням невидимо. Щоб відобразити вікно на екрані, викликається даний метод з параметром true . Якщо викликати його з параметром false, вікно знову стане невидимим.

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

package swingMy_Class;

import javax.swing.*;

public class Program {

       public static void main(String[] args) {
            JFrame myWindow = new JFrame("Пробне вікно");
            myWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myWindow.setSize(400, 300);
            myWindow.setVisible(true);
       }
}

      Зверніть увагу, для роботи з більшістю класів бібліотеки Swing знадобиться імпортувати пакет javaх.swing. *
      Як правило, перед відображенням вікна, необхідно зробити набагато більше дій, ніж в цій простій програмі. Необхідно створити безліч елементів управління, налаштувати їх зовнішній вигляд, розмістити в потрібних місцях вікна. Крім того, в програмі може бути багато вікон і налаштовувати їх всі в методі main() незручно і неправильно, оскільки порушується принцип інкапсуляції: тримати разом дані і команди, які їх обробляють. Логічніше було б, щоб кожне вікно займалося своїми розмірами і вмістом самостійно. Тому класична структура програми з вікнами виглядає наступним чином:

У файлі SimpleWindow.java:
package swingMy_Class;

import javax.swing.*;

public class SimpleWindow extends JFrame{
            SimpleWindow(){
                  super("Пробне вікно");
                  setDefaultCloseOperation(EXIT_ON_CLOSE);
                  setSize(250, 100);
            }           
}

У файлі Program.java:
package swingMy_Class;

import javax.swing.*;

public class Program {

       public static void main(String[] args) {
              JFrame myWindow = new SimpleWindow();
              myWindow.setVisible(true);
       }
}

      З прикладу видно, що вікно описується в окремому класі, що є спадкоємцем JFrame і налаштовує свій зовнішній вигляд і поведінку в конструкторі (першою командою викликається конструктор суперкласу). Метод main() міститься в іншому класі, відповідальному за управління ходом програми. Кожен з цих класів дуже простий, кожен займається своєю справою, тому в них легко розбиратися і легко супроводжувати (тобто удосконалювати при необхідності).
      Зверніть увагу, що метод setVisible()не викликається в класі SimpleWindow, що цілком логічно: за тим, де яка кнопка розташована і які розміри вона повинна мати, стежить саме вікно, а от приймати рішення про те, яке вікно в який момент виводиться на екран - прерогатива керівника класу програми.


_______________________________________________________________________________

_______________________________________________________________________________

Панель вмісту ContentPane

      Фрейм - це контейнер в який поміщають всі інші компоненти (меню, кнопки, прапорці та інші елементи графічного інтерфейсу). Сам клас JFrame, складається з чотирьох областей (pane), що накладаються одна на одну: коренева область (root pane), область шару (layered pane), прозора область (glass pane) та область вмісту (content pane). Перші три застосовують для створення та обслуговування меню.

Для роботи з графічними елементами застосовується область вмісту, в яку і додають компоненти.

ContentPane займає весь простір вікна . Звернутися до цієї панелі можна методом
getContentPane() класу JFrame. За допомогою методу add(Component component) можна додати на неї будь-який елемент управління.

Панель вмісту ContentPane сама собою не представляє нічого особливого. Необхідно лише пам'ятати, що компоненти додаються саме в неї.

Наприклад, додамо кнопку. Кнопка описується класом JButton і створюється конструктором з параметром типу String - написом.

Додамо кнопку в панель вмісту нашого вікна командами:


JButton newButton = new JButton();
getContentPane().add(newButton);

В результаті отримаємо вікно з кнопкою. Кнопка займає всю доступну площу вікна. Такий ефект корисний не у всіх програмах, тому необхідно вивчити різні способи розташування елементів на панелі.


Клас Container (контейнер)


Елементи, які містять інші елементи, називаються контейнерами. Всі вони є нащадками класу Container і успадковують від нього ряд корисних методів:

add(Component component) - Додає в контейнер елемент component;
remove(Component component) - Видаляє з контейнера елемент component;
removeAll() - Видаляє всі елементи контейнера;
getComponentCount() - Повертає число елементів контейнера.

Крім перерахованих в класі Container визначено близько двох десятків методів для управління набором компонентів, що містяться в контейнері. Як видно, вони схожі на методи класу-колекції. Це не дивно, адже по суті контейнер і є колекцією, але колекцією особливого роду - візуальної. Крім зберігання елементів контейнер займається їх просторовим розташуванням і промальовуванням. Зокрема, він має метод getComponentAt(int x, int y), який повертає компонент, в який потрапляє точка з заданими координатами (координати відраховуються від лівого верхнього кута компонента) і ряд інших. Ми не будемо детально розглядати абстрактний контейнер, а відразу перейдемо до його найбільш часто використовуваного нащадку - класу JPanel.



Клас JPanel (панель)


Панель JPanel - це елемент управління, що являє собою прямокутний простір, на якому можна розміщувати інші елементи. Елементи додаються і видаляються методами, успадкованими від класу Container.

У прикладі з кнопкою ми спостерігали, як додана на панель вмісту кнопка зайняла весь її простір. Це відбувається не завжди. Насправді у кожної панелі є так званий менеджер розміщення , який визначає стратегію взаємного розташування елементів, що додаються на панель. Його можна змінити методом
setLayout(LayoutManager manager). Але щоб передати в цей метод потрібний параметр, необхідно знати, якими бувають менеджери.


Менеджер послідовного розміщення FlowLayout


Найпростіший менеджер розміщення - FlowLayout. Він розміщує компоненти на панелі строго по черзі, рядок за рядком, в залежності від розмірів панелі. Як тільки черговий елемент не поміщається в поточному рядку, він переноситься на наступний. Найкраще спостерігати це на прикладі. 
Змінимо конструктор класу SimpleWindow наступним чином:

Менеджери розташування описані в пакеті java.awt.*, тому потрібно його імпортувати


public class SimpleWindow extends JFrame{
     SimpleWindow(){
           super("Пробне вікно");
           setDefaultCloseOperation(EXIT_ON_CLOSE);
           JPanel panel = new JPanel();
           panel.setLayout(new FlowLayout());
           panel.add(new JButton("Кнопка"));
           panel.add(new JButton("+"));
           panel.add(new JButton("-"));
           panel.add(new JButton("Кнопка з довгим написом"));
           setContentPane(panel);
           setSize(300, 150);
     }
}

Поспостерігайте за поведінкою вікна, що з'являється після запуску програми. 
Чотири кнопки в ньому розташовані як слова в текстовому редакторі (при вирівнюванні по центру). Ефект буде краще помітний, якщо змінювати розміри вікна під час роботи програми.
Проаналізуємо текст прикладу. Новий менеджер розташування FlowLayout створюється конструктором без параметрів. Зверніть увагу, в програмі не використовується проміжна змінна. Тобто замість двох команд:
FlowLayout newLayout = new FlowLayout();
panel.setLayout(newLayout);
Ми використовуємо одну:
panel.setLayout(new FlowLayout());

Це цілком допустимо в тих випадках, коли в подальшому нам не потрібно буде звертатися до створюваного об'єкту (що справедливо для даного прикладу). Ми створюємо менеджер розташування, тут же прив'язуємо його до панелі - і все.Тепер панель і менеджер самі знайдуть один з одним спільну мову.
Панель зберігає посилання на свого менеджера, та сама звертається до нього кожен раз, коли потрібно розрахувати координати елементів (це відбувається при їх додаванні, видаленні, зміні розмірів, а також при зміні розмірів вікна). В принципі, ми можемо навіть отримати цього менеджера методом getLayout() класу JPanel, але, як правило, в цьому взагалі немає необхідності.
До речі, клас JPanel крім конструктора без параметрів, має конструктор, в якому в якості параметра задається менеджер розташування. Тому замість команд
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());

можна написати:
JPanel panel = new JPanel(new FlowLayout());
Більш того, за замовчуванням нова панель має саме менеджер розташування FlowLayout. Тому в наведеному вище прикладі ми встановлюємо менеджера швидше для наочності, взагалі ж, робити це не обов'язково.
Точно так ми додаємо на панель нові кнопки. Ми ніде більше не намагаємося звернутися до цих кнопок в програмі, тому заводити під них змінні немає сенсу.
Метод setContentPane(JPanel panel)дозволяє замінити панель вмісту вікна.

Менеджер граничного розміщення BorderLayout


Менеджер розміщення BorderLayout розділяє панель на п'ять областей: центральну, верхню, нижню, праву і ліву. У кожну з цих областей можна додати рівно по одному компоненту, причому компонент буде займати всю відведену для нього область. Компоненти, додані в верхню і нижню області, будуть розтягнуті по ширині, додані в праву і ліву - по висоті, а компонент, для якого створено центр, буде розтягнутий так, щоб повністю заповнити простір, що залишився на панелі.
При додаванні елемента на панель з менеджером розміщення BorderLayout, необхідно додатково вказувати в методі add(), яка з областей мається на увазі. Для цього служать рядки з назвами сторін світу: 
"North" - північ
"South" - південь
"East"  - схід
"West"  - захід
"Center" - центр
Але замість них рекомендується використовувати константи, визначені в класі BorderLayout
NORTH - зверху
SOUTH - знизу
EAST - праворуч
WEST - ліворуч,
CENTER - в центрі
(оскільки в рядку можна припуститися помилки і не помітити цього, а при спробі написати неправильно ім'я константи компілятор видасть попередження). Якщо ж використовувати метод add()як зазвичай, з одним параметром, елемент буде додано в центр.
Панель вмісту має саме таке розташування, саме тому кнопка і займала все вікно цілком (вона була додана в центральну область). Щоб поспостерігати ефект BorderLayout, додамо кнопки в усі п'ять областей:

SimpleWindow(){
           super("Пробне вікно");
           setDefaultCloseOperation(EXIT_ON_CLOSE);
           getContentPane().add(new JButton("Кнопка"),              
                                                BorderLayout.NORTH);
           getContentPane().add(new JButton("+"), 
                                                 BorderLayout.EAST);
           getContentPane().add(new JButton("-"), 
                                                 BorderLayout.WEST);
           getContentPane().add(new JButton("Кнопка з довгим
                                     написом"), BorderLayout.SOUTH);
           getContentPane().add(new JButton("В ЦЕНТР!"));
           setSize(300, 200);
}
Ефект буде добре спостерігатися, якщо змінювати розміри вікна.
Дане розміщення не випадково використовується в панелі вмісту за замовчуванням. Більшість програм користуються областями по краях вікна, щоб розташувати в них панелі інструментів, рядок стану і т.п. А обмеження на один компонент в центральній області абсолютно не суттєво, адже цим компонентом може бути інша панель з безліччю елементів і з будь-яким менеджером розташування.
_______________________________________________________________________________

_______________________________________________________________________________
Менеджер табличного розміщення GridLayout

GridLayout розбиває панель на осередки однакової ширини і висоти (таким чином вікно стає схожим на таблицю). Кожен елемент, що додається на панель з таким розташуванням, цілком займає одну клітинку. Осередки заповнюються елементами по черзі, починаючи з лівої верхньої.
Цей менеджер, на відміну від розглянутих раніше, створюється конструктором з параметрами (чотири цілих числа). Необхідно вказати кількість стовпців, рядків і відстань між осередками по горизонталі і по вертикалі. Виконайте наступний приклад і поспостерігайте ефект.
 SimpleWindow(){
           super("Пробне вікно");
           setDefaultCloseOperation(EXIT_ON_CLOSE);
           JPanel panel = new JPanel();
           panel.setLayout(new GridLayout(2,3,5,10));
           panel.add(new JButton("Кнопка"));
           panel.add(new JButton("+"));
           panel.add(new JButton("-"));
           panel.add(new JButton("Кнопка з довгим написом"));
           panel.add(new JButton("ще одна кнопка"));
           setContentPane(panel);
           setSize(300, 200);
}
_______________________________________________________________________________

_______________________________________________________________________________
Менеджер блочного розміщення BoxLayout і клас Box

Менеджер BoxLayout розміщує елементи на панелі в рядок або в стовпець.
Зазвичай для роботи з цим менеджером використовують допоміжний клас Box, що представляє собою панель, для якої вже налаштоване блочне розміщення. Створюється така панель не конструктором, а одним з двох статичних методів, визначених в класі BoxcreateHorizontalBox()і createVerticalBox().
Елементи, додані на панель з блоковим розміщенням, шикуються один за іншим. Відстань між елементами за замовчуванням нульове. Однак замість компонента можна додати невидиму «розпірку», єдине завдання якої - розсовувати сусідні елементи, забезпечуючи між ними задану відстань. Горизонтальна розпірка створюється статичним методом createHorizontalStrut(int width), а вертикальна - методом createVerticalStrut(int height). Обидва методи визначені в класі Box, а цілочисельний параметр в кожному з них визначає розмір розпірки.
Крім того, на таку панель можна додати ще один спеціальний елемент - своєрідну «пружину». Якщо розмір панелі буде більше, ніж необхідно для оптимального розміщення всіх елементів, ті з них, які здатні розтягуватися, будуть намагатися заповнити додатковий простір собою. Якщо ж розмістити серед елементів одну або кілька «пружин», додатковий вільний простір буде розподілятися і в ці проміжки між елементами. Горизонтальна і вертикальна пружини створюються відповідно методами createHorizontalGlue()і createVerticalGlue().
Зрозуміти особливості роботи цього менеджера краще на наочному прикладі. Ми розташуємо чотири кнопки вертикально, поставивши між двома центральними «пружину», а між іншими - розпірки в 10 пікселів.

SimpleWindow(){
           super("Пробне вікно");

           setDefaultCloseOperation(EXIT_ON_CLOSE);
           Box box = Box.createVerticalBox();
           box.add(new JButton("Кнопка"));
           box.add(Box.createVerticalStrut(10));
           box.add(new JButton("+"));
           box.add(Box.createVerticalGlue());
           box.add(new JButton("-"));
           box.add(Box.createVerticalStrut(10));
           box.add(new JButton("Кнопка з довгим написом"));
           setContentPane(box);
           setSize(250, 400);
      
}

Особливості вирівнювання елементів
У прикладі з вертикальною панеллю всі кнопки виявилися вирівняні по лівому краю. Таке вирівнювання по горизонталі прийнято за замовчуванням.
Однак при розробці вікна програми може знадобитися, щоб якісь елементи були вирівняні інакше, наприклад, по правому краю або по центру. Для того, щоб встановити вирівнювання будь-якого візуального компонента (наприклад, кнопки або панелі), використовуються методи:
setAlignmentX(float alignment) - вирівнювання по горизонталі;
setAlignmentY(float alignment) - вирівнювання по вертикалі. 
Як параметр найпростіше використовувати константи, визначені в класі JComponent.
Для вирівнювання по горизонталі служать константи:
LEFT_ALIGNMENT(по лівому краю),
RIGHT_ALIGNMENT(по правому краю),
CENTER_ALIGNMENT(по центру).
Для вирівнювання по вертикалі:
BOTTOM_ALIGNMENT(по нижньому краю),
TOP_ALIGNMENT(по верхньому краю),
CENTER_ALIGNMENT(по центру).
Однак вирівнювання працює трохи інакше, ніж очікується. Щоб це зрозуміти, змінимо попередній приклад, вирівнявши третю кнопку по правому краю.
Для цього замінимо рядок:
box.add(new JButton("-"));
На три інших:
JButton rightButton = new JButton("-"); rightButton.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
box.add(rightButton);
Нам довелося ввести змінну для звернення до цієї кнопки, оскільки тепер нам потрібно виконати з нею не одну, а дві дії: установка вирівнювання по правому краю і додавання в панель. Попередній спосіб - одночасне створення кнопки і передача її в якості параметра в метод - тут не доцільно використовувати.
Після запуску програми ми побачимо вікно, в якому кнопки розташовані не так, як, очікувалося. Ми звикли, що вирівнювання по правому краю притискає об'єкт до правого краю контейнера, але в даному випадку перебудувалися всі елементи, причому кнопка з вирівнюванням по правому краю виявилася найлівіше.
Пояснення просте. При вирівнюванні по правому краю об'єкт не притискається до правого краю компонента. Замість цього він притискається правим краєм до невидимої лінії вирівнювання. Всі інші компоненти притискаються до цієї лінії своїм лівим краєм, тому і виходить така ситуація.
Складність може виявитися в тому, що не завжди легко зрозуміти, де саме пройде ця лінія. Її положення залежить від розмірів і вирівнювання всіх елементів контейнера. Однак легко запам'ятати просте правило: якщо всі елементи в контейнері вирівняні однаково, ми отримаємо звичну поведінку (як це і було в попередньому прикладі), коли всі компоненти були вирівняні вліво і лінія вирівнювання, в результаті, притулилася до лівого краю панелі.
Параметр вирівнювання насправді являє собою дійсне число в діапазоні від 0 до 1. Він показує, яка частина компонента виявиться зліва від лінії вирівнювання, тобто в яких пропорціях компонент буде «розрізаний».
Насправді константи, що використовуються для вирівнювання мають значення:
LEFT_ALIGNMENT і TOP_ALIGNMENT рівні 0,
RIGHT_ALIGNMENT і BOTTOM_ALIGNMENT рівні 1,
CENTER_ALIGHNMENT рівне 0.5.
Можна підставляти ці числа безпосередньо (хоча використання констант значно підвищує наочність!), а можна вибрати будь-яке інше число від 0 до 1 і налаштувати абсолютно довільне вирівнювання.

Спробуйте проекспериментувати з вертикальною панеллю, задаючи різне вирівнювання для її елементів, щоб інтуїтивно зрозуміти логіку розміщення лінії вирівнювання. Змінюйте розміри вікна під час роботи програми, щоб побачити як змінюється положення цієї лінії. 
_______________________________________________________________________________

______________________________________________________________________________
Ручне розміщення елементів

Якщо в якості менеджера розміщення панелі встановити null , елементи не будуть розставлятися автоматично. Координати кожного елемента необхідно в цьому випадку вказати явно, при цьому вони ніяк не залежать від розмірів панелі і від координат інших елементів. За замовчуванням координати дорівнюють нулю (тобто елемент розташований в лівому верхньому кутку панелі). Розмір елемента також необхідно ставити явно (в іншому випадку його ширина і висота дорівнюватимуть нулю і елемент відображатися не буде).
Координати елемента можна задати одним з таких методів:
setLocation(int x, int y),
setLocation(Point point)
Ці методи працюють аналогічно, встановлюючи лівий верхній кут елемента в точку із заданими координатами. Різниця в способі завдання точки. Можна уявити точку двома цілими числами, а можна об'єктом класу Point. Клас Point по суті являє собою ту ж пару чисел, його конструктор має вигляд Point(int x, int y). Отримати доступ до окремої координати можна методами getX()і getY().
Можне виникнути питання: навіщо використовувати клас Point, якщо можна просто передати пару чисел? Але справа в тому, що є багато корисних методів які повертають результат - координати деякої точки - у вигляді об'єкта цього класу.
Наприклад, метод getLocation()- повертає координати елемента. Припустимо, нам потрібно помістити елемент b точно в те місце, яке займає елемент a. Цього легко домогтися одним рядком:
b.setLocation(a.getLocation());
Розмір елемента задається одним з двох методів:
setSize(int width, int height),
setSize(Dimension size)
Ці методи працюють однаково - різниця, як і в попередньому випадку, в способі передачі параметра. Клас Dimension, аналогічно класу Point, просто зберігає два числа, має конструктор з двома параметрами: Dimension(int width, int height)і дозволяє отримати доступ до своїх складових - ширині і висоті - за допомогою простих методів getWidth()і getHeigth(). Для того, щоб отримати поточний розмір елемента, можна скористатися методом getSize(), який повертає об'єкт класу Dimension. Елемент b можна зробити точно такого ж розміру, як елемент a, виконавши команду:
b.setSize(a.getSize());
Створимо панель, з якою не буде пов'язано ніякого менеджера розміщення і вручну розмістимо на ній дві кнопки:
SimpleWindow(){
super("Пробне вікно");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(null);
JButton button = new JButton("Кнопка");
button.setSize(80, 30);
button.setLocation(20,20);
panel.add(button);
button = new JButton("Кнопка з довгим написом");
button.setSize(120, 40);
button.setLocation(70,50);
panel.add(button);
setContentPane(panel);
setSize(250, 150);}
Ми використовуємо одну і ту ж змінну button для звернення до обох кнопок (причому, другий раз її описувати не потрібно). Справді, здійснивши всі необхідні операції з першою кнопкою і знаючи, що звертатися до неї нам більше не знадобиться, ми використовуємо «звільнену» змінну для маніпуляцій з другою кнопкою.
Автоматичне визначення розмірів компонентів

Якщо у панелі є будь-який менеджер розміщення, вона ігнорує явно задані розміри і координати всіх своїх елементів.
У цьому легко переконатися, замінивши в попередньому прикладі команду panel.setLayout(null)на panel.setLayout(new FlowLayout()).
Менеджер розміщення сам визначає координати і розміри всіх елементів.
Спосіб визначення координат елементів очевидним чином випливає з алгоритмів роботи кожного менеджера.
Ми також відзначали, що в деяких випадках компоненти намагаються заповнити весь доступний їм простір. Наприклад, всю центральну область в разі менеджера BorderLayout або весь осередок в менеджері GridLayout. А в панелі з менеджером FlowLayout, навпаки, елементи ніколи не намагаються вийти за певні межі. Розглянемо, що це за кордони.
Кожен візуальний компонент має три типи розмірів: мінімально допустимий, максимально допустимий і кращий. Дізнатися, чому дорівнюють ці розміри для даного компонента можна за допомогою відповідних методів:
getMinimumSize(),
getMaximumSize(),
getPreferredSize().
Методи повертають результат типу Dimension. Вони запрограмовані у відповідному класі. Наприклад, у кнопки мінімальний розмір - нульовий, максимальний розмір не обмежений, а кращий залежить від напису на кнопці (обчислюється як розмір тексту напису плюс розміри полів).
Менеджер FlowLayout завжди встановлює бажані розміри елементів. Менеджер BorderLayout встановлює кращу ширину правого і лівого, а також кращу висоту верхнього і нижнього. Решта розміри підганяються під доступний простір панелі.
Менеджер GridLayout намагається підігнати розміри всіх елементів під розмір осередків.
Менеджер BoxLayout орієнтується на кращі розміри.
Всіма трьома розмірами можна керувати за допомогою відповідних методів set:
setMinimumSize(Dimension size),
setPreferredSize(Dimension size),
setMaximumSize(Dimension size).
Найчастіше використовується простий прийом, коли елементу «не рекомендується» збільшуватися або зменшуватися щодо своїх бажаних розмірів.
Це легко зробити командою:
element.setMinimumSize(element.getPreferredSize());
«Упаковка» вікна

У розглянутих вище прикладах ми явно задавали розмір вікна методом setSize(). Але коли використовується будь-який менеджер розташування, що розставляє елементи і змінює їх розміри за власними правилами, важко сказати заздалегідь, які розміри вікна будуть найбільш підходящими.
Безумовно, найбільш підходящим буде варіант, при якому всі елементи вікна мають кращі розміри або близькі до них.
Якщо замість явної вказівки розмірів вікна, викликати метод pack(), вони будуть підібрані оптимальним чином з урахуванням переваг всіх елементів, розміщених в цьому вікні.
Оцініть роботу цього методу, замінивши в кожному з наведених вище прикладів команду
setSize(250, 100);
на команду
pack();
Зауважте, що коли панель не має методу розміщення, ця команда не працює (оскільки панель не має алгоритму для обчислення свого кращого розміру). 
_______________________________________________________________________________

_______________________________________________________________________________

Вправа 1.

Як уже зазначалося, елементом панелі може бути інша панель.

Створіть панель з трьома кнопками і менеджером розміщення FlowLayout і панель з двома кнопками і менеджером розміщення BoxLayout (горизонтальним). Додайте обидві панелі в головне вікно (не змінюючи менеджера розміщення у панелі вмісту): першу в центр, а другу вздовж будь-якого боку вікна.

Наприклад:



_______________________________________________________________________________

_______________________________________________________________________________

Основні візуальні компоненти Swing

Клас JComponent

Всі візуальні компоненти бібліотеки Swing успадковані від класу JComponent. Сам цей клас є абстрактними і безпосередньо не використовується, але всі візуальні компоненти успадковують його методи. Розглянемо найбільш корисні з них.
setEnabled(boolean enabled) - використовується для управління активністю компонента. При виклику цього методу з параметром false компонент переходить в неактивний стан. Для кожного спадкоємця JComponent ця «неактивність» може бути перевизначена по-різному. Наприклад, неактивна кнопка - не було натиснуто, не реагує на наведення миші і відображається монохромним сірим кольором. Метод isEnabled()повертає true , якщо елемент активний і false в іншому випадку.
setVisible(boolean visible) - управляє видимістю компонента. Ми вже використовували його для відображення вікна JFrame. Більшість елементів управління, на відміну від вікна, за замовчуванням є видимими (тому ми не викликали даний метод після створення кнопок в прикладах попередніх уроків). Метод isVisible()повертає false , якщо елемент невидимий і true в іншому випадку.
За допомогою методу setBackground(Color color)можна змінити колір заднього фону компонента. Однак ефект матиме місце лише в тому випадку, якщо компонент непрозорий (деякі компоненти, наприклад мітка JLabel за замовчуванням є прозорими). Непрозорість встановлюється методом setOpaque(boolean opaque) з параметром true . Методи getBackground() і isOpaque() повертають поточний колір заднього фону і непрозорість компонента.

Мітка JLabel

У більшості візуальних бібліотек мітка - один з найпростіших компонентів. Вона являє собою звичайний текст, який виводиться в заданому місці вікна і використовується для виведення допоміжної текстової інформації: підписи до інших елементів, інструкції та попередження для користувача. У Swing мітка дозволяє досягти більш цікавих ефектів. По-перше, крім тексту можна використовувати значок. По-друге, з її допомогою можна виводити відформатований текст.
Текст і значок мітки можна задати в її конструкторі. У неї є кілька конструкторів з різними параметрами, зокрема:
JLabel(String text) - створює мітку з написом text
JLabel(Icon image) - створює мітку із позначкою image
JLabel(String text, Icon image, int align)- створює мітку з написом text і значком image. Третій параметр задає вирівнювання тексту разом із позначкою, може бути використана одна з констант, описаних в Інтерфейсі SwingConstants: LEFT, RIGHT, CENTER.

Для прикладу створимо вікно з міткою, створеної за допомогою третього конструктора. Як і раніше, ми будемо використовувати два класи, один з яких назвемо SimpleWindow і успадкуємо його від класу вікна JFrame. У його конструкторі будуть створюватися і розміщуватися всі елементи вікна. Другий клас буде створювати це вікно і відображати його на екрані.
Напишемо в конструкторі класу SimpleWindow наступний код:
SimpleWindow(){
super("Вікно з написом");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JLabel label = new JLabel("Мітка зі значком і                написом", new ImageIcon("1.gif"), SwingConstants.RIGHT);
getContentPane().add(label);
pack();}
Щоб переконатися, що вирівнювання по правому краю працює, необхідно трохи розтягнути вікно, щоб ширина мітки стала більше оптимальної.
У бібліотеці Swing мітка (і не тільки вона) може бути налаштована для відображення відформатованого тексту в форматі HTML. Для цього необхідно, щоб рядок, що встановлюється в якості напису мітки, починався з тега <html>. Після цього можна використовувати в рядку будь-які теги мови HTML версії 3.2, і вони будуть перетворюватися у відповідні атрибути форматування. У цьому легко переконатися, змінивши в попередньому прикладі рядок з викликом конструктора на:
JLabel label = new JLabel("<html>До цієї мітки застосовано " +
                         "HTML-форматування, включаючи: "+
                         "<ul><li><i>курсив</i>,"+
                         "<li><b>напівжирний</b>" +
                         "<li><font size = +2>збільшення розміру</font>"+
                       "<li>маркований список </ul>");
Оскільки текст нашого напису досить довгий, рядок для зручності сприйняття розбивається на частини і використовується оператор +.
Перелічимо основні методи класу JLabel:
getText() - повертає поточний текст напису мітки
setText(String text) - задає новий текст напису
getIcon() - повертає значок мітки
setIcon(Icon image)- встановлює новий значок. 
Як значок зазвичай використовується об'єкт класу ImageIcon
Клас ImageIcon описує графічне зображення. Параметр його конструктора - це повне ім'я файлу, з якого може бути завантажено зображення.  
getVerticalAlignment();
setVerticalAlignment(int align);
getHorizontalAlignment();
setHorizontalAlignment(int align);
Ці чотири методи дозволяють отримати поточне або встановити нове вирівнювання (по горизонталі і вертикалі) мітки щодо її кордонів. Можливі положення описані в інтерфейсі SwingConstants.
getVerticalTextPosition();
setVerticalTextPosition(int align);
getHorizontalTextPosition();
setHorizontalTextPosition(int align);
Ці чотири методи дозволяють отримати поточне або встановити нове вирівнювання тексту щодо значка. Можливі положення описані в інтерфейсі SwingConstants.
getIconTextGap(), setIconTextGap(int gap) - Дозволяють отримати або задати відстань між текстом і значком мітки в пікселях.
_______________________________________________________________________________

_______________________________________________________________________________

Кнопка JButton

Ми використовували кнопки в попередніх уроках, хоча і не торкалися можливостей їх налаштування. 
Кнопка - це прямокутник з текстом (і / або значком), по якому користувач клацає, коли хоче виконати якусь дію (або про щось сигналізувати).
Кнопка створюється одним з п'яти конструкторів, зокрема 
JButton(), 
JButton(String text), 
JButton(Icon icon), 
JButton(String text, Icon icon) - параметри яких говорять самі за себе. 
П'ятий конструктор ми розглянемо пізніше.
Крім звичайного значка можна призначити кнопці ще кілька - для різних станів. Метод setRolloverIcon(Icon icon) дозволяє задати значок, який буде з'являтися при наведенні на кнопку миші, setPressedIcon(Icon icon) - встановлюється значок для кнопки в натиснутому стані, setDisableIcon(Icon icon) - значок для неактивної кнопки. Кожному з цих методів відповідає метод get.
Метод setMargin(Insets margin) дозволяє задати величину відступів від тексту напису на кнопці до її полів. Об'єкт класу Insets, який передається в цей метод, може бути створений конструктором з чотирма цілочисельними параметрами, які задають величину відступів: 
Insets(int top, int left, int bottom, int right). 
Метод getMargin()повертає величину поточних відступів у вигляді об'єкта того ж класу.
Всі методи класу JLabel, описані в попередньому уроці, присутні і в класі JButton. За допомогою цих методів можна змінювати значок і текст напису на кнопці, а також управляти їх взаємним розташуванням один щодо одного і щодо краю кнопки (з урахуванням відступів).
За допомогою методів:
setBorderPainted(boolean borderPainted), 
setFocusPainted(boolean focusPainted), 
setContentAreaFilled(boolean contentAreaFilled) можна відключати (параметром false ) і включати назад (параметром true ) промальовування рамки, промальовування фокуса (кнопка, на якій знаходиться фокус, виділяється пунктирним прямокутником) і зафарбовування кнопки в натиснутому стані.
Для прикладу створимо кнопку зі значком і з написом, змінимо її відступи і розташування тексту щодо значка (текст буде вирівняний ліворуч і вгору щодо значка).
SimpleWindow(){
super("Вікно з кнопкою");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton button = new JButton("Кнопка", new ImageIcon("1.gif"));
button.setMargin(new Insets(0, 10, 20, 30));
button.setVerticalTextPosition(SwingConstants.TOP);
button.setHorizontalTextPosition(SwingConstants.LEFT);
getContentPane().add(button);
pack();}

Компоненти JToggleButton, JCheckBox, JRadioButton

Компонент JToggleButton являє собою кнопку, яка може перебувати в двох станах: натиснутому і відпущеному. Коли користувач клацає мишкою по такій кнопці, вона змінює свій стан. Саме таким чином ведуть себе кнопки форматування на панелі інструментів текстового редактора. Кнопка [I] не тільки встановлює або прибирає курсивне зображення в виділеному тексті, але і сигналізує про його наявність чи відсутність.
Основний конструктор - 
JToggleButton(String text, Icon icon, boolean selected) - створює кнопку із заданими написом, значком і поточним станом. Кнопку можна перевести в потрібний стан програмним шляхом, викликавши метод 
setSelected(boolean selected). 
Метод isSelected()повертає true , якщо кнопка вибрана (тобто знаходиться в натиснутому стані) і false у протилежному випадку.
Від класу JToggleButton успадкований клас JCheckBox - прапорець. Цей клас має точно такий же набір конструкторів і методів. 
Єдина відмінність між ними - у зовнішньому вигляді: JCheckBox виглядає не як кнопка, а як невеликий квадратик, в якому можна поставити або прибрати галочку.
Аналогічним чином поводиться клас JRadioButton - перемикач або радіокнопка, зовні виглядає як порожній кружок, коли вона не виділена і кружок з точкою в виділеному стані.
Незважаючи на те, що класи JCheckBox і JRadioButton поводяться абсолютно однаково (і аналогічно їх спільного предка JToggleButton), їх прийнято використовувати в різних ситуаціях. Зокрема, JRadioButton передбачає вибір єдиної альтернативи з декількох можливих: кілька таких об'єктів об'єднуються в одну групу (найчастіше ця група візуально позначається рамкою) і при виборі одного з елементів групи попередній обраний елемент переходить в стан «не обрана».
Для того, щоб отримати таку поведінку, використовується спеціальний контейнер ButtonGroup- взаємовиключна група (створюється конструктором без параметрів). Якщо додати в один такий контейнер кілька елементів JRadioButton, то обраним завжди буде тільки один з них.
В принципі, в ButtonGroup можуть бути додані не тільки перемикачі, але і прапорці, і кнопки вибору (і навіть звичайні кнопки). Але при розробці інтерфейсу слід дотримуватися усталеному підходу, згідно з яким у взаємовиключну групу слід об'єднувати об'єкти JRadioButton (і, в деяких випадках JToggleButton), але не JCheckBox.
Метод add(AbstractButton button) - додає елемент в групу. 
Метод getElements()повертає всі її елементи у вигляді колекції Enumeration. По колекції можна пройтися ітератором і знайти виділений елемент.
Розгляньте приклад, в якому створюються дві кнопки вибору, два прапорці і два перемикача. Кнопки вибору і перемикачі об'єднані в групи ButtonGroup. Для того, щоб обвести кожну пару елементів рамкою, необхідно розташувати кожну пару елементів на окремій панелі.
SimpleWindow(){
super("Приклад з кнопками вибору, прапорцями і перемикачами");
setDefaultCloseOperation(EXIT_ON_CLOSE);
ImageIcon icon = new ImageIcon("1.gif"); // будемо використовувати один значок на всі випадки
Box mainBox = Box.createVerticalBox();
Box box1 = Box.createVerticalBox();
JToggleButton tButton1 = new JToggleButton("Кнопка вибору 1");
JToggleButton tButton2 = new JToggleButton("Кнопка вибору 2", icon);
ButtonGroup bg = new ButtonGroup(); // створюємо групу взаємного виключення
bg.add(tButton1);
bg.add(tButton2); // зробили кнопки tButton1 і tButton2 взаємовиключаючими
box1.add(tButton1);
box1.add(tButton2); // додали кнопки tButton1 і tButton2 на панель box1
box1.setBorder(new TitledBorder("Кнопки вибору"));

Box box2 = Box.createVerticalBox();
JCheckBox check1 = new JCheckBox("Прапорець 1");
JCheckBox check2 = new JCheckBox("Прапорець 2", icon);
box2.add(check1);
box2.add(check2); // додали прапорці на панель box2
box2.setBorder(new TitledBorder("Прапорці"));

Box box3 = Box.createVerticalBox();
JRadioButton rButton1 = new JRadioButton("Перемикач 1");
JRadioButton rButton2 = new JRadioButton("Перемикач 2", icon);
bg = new ButtonGroup(); // створюємо групу взаємного виключення
bg.add(rButton1);
bg.add(rButton2); // зробили радіокнопки взаємовиключаючими
box3.add(rButton1);
box3.add(rButton2); // додали радіокнопки на панель box3
box3.setBorder(new TitledBorder("Перемикачі"));

mainBox.add(box1);
mainBox.add(box2);
mainBox.add(box3);

setContentPane(mainBox);
pack();}


Запустивши приклад, ми можемо поспостерігати особливості роботи кнопок вибору, прапорців і перемикачів. Зокрема, ми бачимо, що у прапорців або перемикачів малюнок замінює елемент виділення. Але малюнок не показує, чи вибраний даний об'єкт. Необхідно встановити окремий малюнок для виділеного стану, що досягається методом setSelectedIcon(Icon icon). 
Додайте в потрібні місця команди:

check2.setSelectedIcon(new ImageIcon("2.gif"));
і
rButton2.setSelectedIcon(new ImageIcon("2.gif"));

Поспостерігайте за змінами. Не забудьте, що файл 2.gif, так само як і файл 1.gif повинні перебувати в доступному для програми місці.
_______________________________________________________________________________

_______________________________________________________________________________
Вправа 2.
Створити вікно за зразком:



Вправа 3.
Створити вікно за зразком:



Вибрані перемикачі, прапорці, кнопки


Збільшені розміри вікна

_______________________________________________________________________________

_______________________________________________________________________________

Текстове поле JTextField

Текстове поле - простий і часто використовуваний компонент, призначений для введення невеликих за обсягом (записуються в один рядок) текстових даних. Для створення текстового поля найчастіше використовуються конструктори:
JTextField(int columns)- Створює пусте текстове поле, ширина якого достатня для розміщення columns символів. При цьому користувач може вводити в текстове поле рядок будь-якої довжини: він просто буде прокручуватися.
JTextField(String text)- Створює текстове поле з початковим текстом text.
JTextField(String text, int columns) - Встановлює і ширину і початковий текст.
Занести текст в поле можна методом setText(String text). Метод getText()повертає вміст текстового поля цілком, а getText(int offset, int length)- фрагмент вмісту довжини length, починаючи з символу offset.
Частина тексту в полі може виділятися (як програмним шляхом, так і в результаті дій користувача). Метод getSelectedText()дозволяє отримати виділену частину тексту. Замінити виділений текст іншим можна за допомогою методу replaceSelection(String content).
Методи getSelectionStart()і getSelectionEnd()повертають кордон виділеної ділянки, а методи setSelectionStart(int start)
і setSelectionEnd(int end)змінюють їх.
Метод getCaretPosition()повертає позицію курсора (каретки) в текстовому полі, а метод setCaretPosition(int position)дозволяє поставити її програмно. Методом setCaretColor(Color color)можна змінити колір курсора.
За замовчуванням текст в полі притискається до лівого краю. Змінити це можна методом setHorizontalAlignment(int align), як параметр передається одна з констант вирівнювання, визначених у цьому ж класі JTextField: LEFT, CENTER, RIGHT.

Поле для введення пароля JPasswordField

JPasswordField є прямим нащадком JTextField, тому для нього справедливо все сказане вище. Відмінність полягає в тому, що весь введений в нього текст прихований від сторонніх очей: він замінюється зірочками або іншим символом, встановити який дозволяє метод setEchoChar(char echo), а отримати - getEchoChar().
Найчастіше JPasswordField застосовується для введення пароля. Метод getText()дозволяє отримати цей пароль, але користуватися ним не рекомендується (зловмисник може проаналізувати вміст оперативної пам'яті і перехопити цей пароль). Замість нього слід використовувати метод getPassword(), який повертає масив символів char[]. Після того, як введений пароль буде оброблений (наприклад, порівняний з реальним паролем) рекомендується заповнити цей масив нулями, щоб слідів в оперативній пам'яті не залишилося.

Область для введення тексту JTextArea

JTextArea також є нащадком JTextField і успадковує всі його методи. На відміну від текстового поля область для введення тексту дозволяє ввести не один рядок, а кілька. У зв'язку з цим JTextArea пропонує кілька додаткових функцій. По-перше, це здатність переносити слова на сусідній рядок цілком, якою управляє метод setWrapStyleWord(boolean wrapStyle). Якщо викликати цей метод з параметром true, то слова НЕ будуть розриватися в тому місці, де вони «натикаються» на кордон компонента, а будуть цілком перенесені на новий рядок. По-друге, це здатність переносити текст (тобто довгі рядки будуть укладатися в кілька рядків замість одного, що йде за межі компоненту.
Цією здатністю управляє метод setLineWrap(boolean lineWrap). Методи isWrapStyleWord()і isLineWrap()повертають поточний стан даних здібностей ( true - активована і false - деактивовано).
При створенні JTextArea найчастіше використовують конструктор JTextArea(int rows, int columns), який встановлює висоту (кількість рядків) і ширину (кількість символів) компонента.
Для роботи зі своїм спорядженням JTextArea додатково пропонує два зручних метода. Метод append(String text)додає рядок text в кінець вже наявного тексту, а метод insert(String text, int position) вставляє його в позицію position.


Поспостерігаємо ці три компонента на наочному прикладі. Створимо просте вікно, в якому розмістимо їх за допомогою менеджера BorderLayout.
 

_______________________________________________________________________________

_______________________________________________________________________________
Вправа 4.
Змініть вікно з вправи 3, додавши текстові поля.
Зразок:
_______________________________________________________________________________

_______________________________________________________________________________

Панель прокрутки JScrollPane

Спостерігаючи за поведінкою компонента JTextArea в попередньому прикладі, легко можна виявити проблеми, які виникають, коли тексту стає «тісно» в рамках відведеного місця. Залежно від використовуваного менеджера розташування текст або обрізається, йдучи за межі компоненту, або розсовує ці межі (але в будь-якому випадку залишається обмежений розміром вікна). У таких випадках типово використання смуг прокрутки, але в Swing смуги прокрутки самі собою не з'являються.
На щастя, додати до компоненту смуги прокрутки насправді дуже просто. Для цього служить компонент JScrollPane - панель прокрутки. Найчастіше вона просто «надівається» на необхідний об'єкт за допомогою власного конструктора, що приймає цей об'єкт як параметр. Наприклад, щоб текстова область textArea з попереднього прикладу мала смуги прокрутки, необхідно замінити команду
getContentPane().add(textArea);
на команду
getContentPane().add(new JScrollPane(textArea));
У цій команді створюється панель з смугами прокрутки, в неї поміщається об'єкт textArea, а сама панель додається в панель вмісту вікна. Тепер текст вільно прокручується. А в разі застосування менеджера FlowLayout або BoxLayout компонент JTextArea НЕ буде підлаштовуватися під свій вміст (матиме бажаний розмір, відповідний параметрам конструктора) і, при необхідності, відображатиме смуги прокрутки.
Корисними методами JScrollPane є:
setHorizontalScrollBarPolicy(int policy)- Дозволяє задати стратегію роботи з горизонтальною смугою прокрутки. Можливі значення представлені константами HORIZONTAL_SCROLLBAR_ALWAYS (відображати завжди), HORIZONTAL_SCROLLBAR_AS_NEEDED (відображати при необхідності) і HORIZONTAL_SCROLLBAR_NEVER (не відображати ніколи). Дані константи визначені в інтерфейсі ScrollPaneConstants.
setVerticalScrollBarPolicy(int policy)дозволяє задати стратегію роботи з вертикальною смугою прокрутки за допомогою констант VERTICAL_SCROLLBAR_ALWAYS,
VERTICAL_SCROLLBAR_AS_NEEDED і
VERTICAL_SCROLLBAR_NEVER.

Інструментальна панель JToolBar

Більшість програмних продуктів надають зручні інструментальні панелі, розташовані уздовж кордонів вікна програми і містять кнопки, списки, що випадають і інші елементи управління, зазвичай відповідні командам меню. У Swing для інструментальних панелей розроблений візуальний компонент JToolBar, в якому закладена дуже зручна функціональність.

На цей раз почнемо відразу з прикладу. Створимо вікно з менеджером розташування BorderLayout, розмістимо по центру область для введення тексту JTextArea, а до верхньої межі прикріпимо інструментальну панель з трьома кнопками і одним роздільником:


Запустіть приклад і проекспериментуйте з інструментальною панеллю.
Спробуйте від'єднати її від верхньої межі вікна і прикріпити до будь-якої іншої.
Від'єднайте її від кордонів вікна так, щоб панель стала самостійним вікном. При цьому панель завжди відображується над батьківським вікном, навіть якщо саме воно, а не панель є активним. Якщо закрити самостійну панель кнопкою з хрестиком, вона повернеться в своє вікно, в те місце, де вона була закріплена останній раз.
Отже, досить простий приклад продемонстрував досить вражаючі можливості інструментальної панелі.
Підіб'ємо підсумок, перерахувавши найкорисніші методи JToolBar:
Конструктор JToolBar(String title)створює горизонтальну панель із заданим заголовком. Горизонтальна панель призначена для прикріплення до верхньої або нижньої межі батьківської панелі (має розташування BorderLayout). Для створення вертикальної панелі використовується конструктор JToolBar(String title, int orientation), де параметр orientation задається константою VERTICAL з інтерфейсу SwingConstants.
Також доступні конструктори JToolBar()і JToolBar(int orientation), що створюють панель без заголовка.
setFloatable(boolean floatable)- дозволяє або забороняє (за замовчуванням дозволяє) користувачу відкріплювати панель від місця її початкового розташування. Йому відповідає метод isFloatable()повертає true , якщо відкріплювати панель дозволено.
add(Component component)- додає на інструментальну панель новий елемент управління. Взаємопов'язані групи елементів управління прийнято розділяти за допомогою лінії або порожнього простору. Метод addSeparator()додає такий роздільник.
 _________________________________________

_______________________________________________________________________________

2 коментарі: