Бібліотека Swing багатошарові панелі та вкладені вікна, Світ ПК, Видавництво «Відкриті системи»

Багатовіконний (багатодокументний) інтерфейс знайомий переважній більшості користувачів Windows. За допомогою MDI (Multi Document Interface) виконані такі відомі програми, як Microsoft Word, CorelDRAW!, Adobe Photoshop, та ще мало що! Технологія

Багатовіконний (багатодокументний) інтерфейс знайомий переважній більшості користувачів Windows. За допомогою MDI (Multi Document Interface) виконані такі відомі програми, як Microsoft Word, CorelDRAW!, Adobe Photoshop, та ще мало що!

Технологія Java спочатку цуралася застосування MDI, але тільки до тих пір, поки не з'явилася бібліотека Swing, де цей інтерфейс представляють три класи: JLayeredPane, JDesktopPane та JInternalFrame. Однак у розробках найчастіше використовуються лише два останні. Справа в тому, що JDesktopPane є нащадком JLayeredPane, тому всі поля та методи цього класу успадковуються, а його власні розширюють функціональний потенціал. Наприклад, JDesktopPane зафарбовує фон, а JLayeredPane – ні.

Усередині класу JInternalFrame є ще один клас JdesktopIcon, що представляє на екрані дочірні вікна, згорнуті в піктограму. Цей клас викликає значний інтерес, але розробники Swing поки що рекомендують не використовувати його, оскільки найближчим часом він буде радикально змінений. Втім, для того, щоб освоїти створення MDI-програм на базі Swing, достатньо розібратися з JDesktopPane і JInternalFrame.

Клас JDesktopPane можна умовно представити як вікно, набране з багатьох "скляних" шарів. Чудово, що можна розташовувати дочірні вікна між "склом", причому шарами, та ще так, як заманеться: все на одному шарі, все на різних шарах або по кілька вікон накожному шарі. Якщо розташувати дочірні вікна одному шарі, то кожне вікно, у якому користувач клацає мишею, буде переноситися передній план. Вікна, розміщені на різних шарах, ніколи не перетасовуються. Шар, що лежить на ближньому до користувача, він завжди буде найближчим до користувача, і єдиний спосіб побачити його на віддаленому шарі - зрушити на ділянку екрана, не зайнятий верхнім вікном. Ця чудова властивість класів JDesktopPane і JLayeredPane дозволяє з легкістю створювати "плаваючі" вікна-палітри інструментів (які ніколи не перекриваються робочими вікнами), модифіковані панелі повідомлень та інші вікна, якщо за родом своєї діяльності вони не повинні бути нічим перекриті.

Додаючи дочірнє вікно в екземпляр JDesktopPane (або JLayeredPane), програміст задає номер шару. Найдальший шар має номер 0. Чим більший номер шару, тим ближче він до користувача. При цьому не обов'язково нумерувати шари починаючи з 0 і не обов'язково надавати їм номери, що йдуть поспіль. Достатньо знати, що вікно на шарі з великим номером перекриє вікно, розміщене на шарі з нижчим номером.

Інший спосіб завдання номера шару - використання методу setLayer, перший аргумент якого є посиланням на об'єкт, що міститься, а другий - це номер шару. Маленьке зауваження: застосовуючи метод setLayer, задавайте номер шару число типу int.

Багатошарові панелі мають кілька визначених констант, що задають номери часто використовуваних у розробці шарів:

DEFAULT_LAYER— звичайні компоненти та вікна повинні додаватися на цей шар;

PALETTE_LAYER— на цьому шарі доцільно розміщувати вікна палітр інструментів тощо;

MODAL_LAYER— цей шар виділено для показу модальних діалогових панелей;

POPUP_LAYER— різні спливаючі вікна (списоки, що розкриваються, підказки тощо) краще всього поміщати сюди;

DRAG_LAYER— верхній шар відданий для елементів, що перетягуються з місця на місце.

Якщо ви використовуєте метод add, номер шару має бути переданий як екземпляр об'єкта Integer. Це з тим, що з базового класу Container необхідний нам метод має такий прототип:

Якщо ж припуститися помилки і передати аргумент не Integer, а int, то вийде виклик зовсім іншого методу:

В результаті замість номера шару ви встановите порядковий номер вікна всередині свого шару. Це, до речі, теж може бути зроблено, якщо ви застосуєте для додавання до панелі класу JDesktopPane дочірнього вікна метод add з трьома аргументами. Перші два ви вже знаєте, а третій аргумент методу – порядковий номер в ієрархії вікон на тому шарі, до якого додається вікно. Привласнюючи номери позицій компонентам, що додаються, легко заплутатися. Хоча документація має корисну інформацію, ви не знайдете там правильного способу нумерації позицій. Так, більший порядковий номер компонента, на відміну від номера шару, позначає більш віддалений від глядача компонент, тобто компонент з номером 1 розташовується далі компонента з номером 0. Наприклад, ви додаєте чотири вікна і хочете, щоб вони йшли у зворотному порядку. Тому ви задаєте номери 3, 2, 1, 0. За ідеєю, все має бути коректно. А ось і ні! Всі вікна, що додаються, перемішуються. Виявляється, нумерацію кожного нового вікна необхідно проводити з урахуванням вже доданих компонентів. Тому коректною буде послідовність 0, 0, 0, 0 (згадайте, що 0 - це найближча до глядача позиція). І так далі.

Можна скористатися ще парою корисних методів: moveToFront та moveToBack. Перший з нихпереносить обраний компонент у найближчу до користувача позицію з його шарі, а другий, навпаки, ховає обраний компонент іншими компонентами з його шарі.

Спочатку потрібно імпортувати необхідні класи:

Тепер ми створюємо клас-нащадок від JInternalFrame. Це необхідно тому, що нам потрібно змінити наповнення дочірніх вікон за промовчанням, а це можна зробити, створивши свій власний клас ChildWindow:

Відмінною особливістю класу ChildWindow є його конструктор, який крім параметра title, що задає заголовок вікна, має параметр type, що визначає тип вікна і параметр iconFileName, за допомогою якого можна задати піктограму для рамки вікна, що створюється:

Як очевидно з цього фрагмента, у конструкторі створюється нове дочірнє вікно й у залежність від прапорів, переданих у параметрі type, викликаються такі стандартні методи класу JInternalFrame: setResizable, setMaximizable, setIconifiable і setClosable. Ці методи, отримавши в єдиному параметрі значення true, дозволяють вікну змінювати розмір, максимізуватися, мінімізуватися, перетворюючись на піктограму, або закриватися відповідно. При цьому в заголовку вікна з'являться кнопки. Зауважте, що якщо параметр конструктора iconFileName не порожній, то на рамці вікна з'явиться задана користувачем піктограма, для чого задіюється метод setFrameIcon класу JInternalFrame.

Наступне, що робить конструктор, - задає випадковий колір фону для вікна, користуючись для цього функцією random математичного апарату Java:

Для зручності завдання прапорів у параметрі type конструктора ChildWindow у цьому класі визначено кілька корисних констант, що добре читаються:

А ось і сама програма. Крім опису класу Sample мистворюємо змінну-екземпляр класу багатошарової панелі JdesktopPane:

Після чого створюються та ініціалізуються елементи меню. Для коректного відтворення українськомовних написів усі вони виконані у кодуванні Unicode:

У конструкторі класу програми насамперед ініціалізуються слухачі подій меню та віконних операцій. Останній необхідний для стеження за моментом, коли користувач хоче закрити вікно:

Для краси задамо зовнішній вигляд програми відповідно до налаштування MetalLookAndFeel. І не забудемо додати багатошарову панель у вікно:

На завершення роботи конструктор збирає меню з окремих елементів та підключає його до головного вікна програми:

Більшість роботи нашого прикладу падає на "плечі" методу створенняChildWindows. Його єдиний параметр служить семафором, визначальним, як створювати дочірні вікна: одному шарі чи різних. Якщо параметр дорівнює true, то генерується масив layers об'єктів Integer зі значеннями 0. Якщо параметр дорівнює false, то масив будуть завантажені об'єкти Integer з різними значеннями:

Значення цього масиву надалі визначатимуть, на який шар додати нове дочірнє вікно. Зверніть увагу, що під час використання різних шарів номера генеруються виразом i * 2. Це ілюструє твердження, що номери шарів можуть бути непослідовними.

Тепер створюємо вікно, для якого дозволяємо операцію зміни розміру, йому буде присвоєно власну піктограму із зображенням червоного світлодіода. Ім'я файлу береться довільно. Головне, щоб файл піктограми, ім'я якого ви додасте у вихідний текст, існувало:

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

Наступне дочірнє вікно може бути закрите за командою користувача і лягає ближче до спостерігача, ніж попереднє:

Третє вікно може згортатися в піктограму і займає позицію 1 (тобто наступне після попереднього):

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

Ось так хитро доводиться розміщувати вікна, щоб домогтися їхнього коректного розташування. У результаті вікна стануть у такому порядку: 4, 2, 3, 1 (якщо починати з найближчого до користувача):

Звичайно, це розміщення по позиціях має сенс, тільки якщо вікна розташовуються на тому самому шарі. Інакше значення позиції просто ігноруються.

Для видалення всіх вікон із багатошарової панелі є наступний метод:

Спочатку він викликає стандартний метод removeAll, що видаляє всі компоненти на всіх шарах панелі, після чого робить перемальовку.

Два внутрішні класи-слухачі відповідають за закриття головного вікна програми та роботу меню:

Цей фрагмент листингу показує розбір вибраного пункту меню та запуск методу createChildWindows зі значенням параметра false або true. Вибір - створювати вікна на одному шарі або на різних - відбувається так:

І нарешті, ви бачите стандартну точку входу main, що задає розмір вікна та його заголовок.

Напевно, ви помітили, що головне вікно нашого прикладу успадковується від JFrame. У цьому полягає невелика хитрість. Справа в тому, що вікна-спадкоємці класу JFrame вже мають у своєму складі екземпляр класу JLayeredPane! Це означає, що вам немає потреби створюватиоб'єкт класу багатошарової панелі. Можна отримати посилання вже наявне, викликавши метод getLayeredPane. Ми ж пішли більш складним шляхом, щоб показати техніку програмування MDI-додатків.

Меню програми показано на рис. 1.
багатошарові
Якщо скористатися меню, то програма, що працює, буде виглядати приблизно так, як показано на рис. 2.
Спробуйте мінімізувати вікно та подивіться, як виглядає піктограма для дочірнього вікна (рис. 3).

Щоб розібратися в багатошаровій системі, поекспериментуйте з нашим прикладом, клацаючи на вікнах та їх піктограмах. І природно, у вас є неосяжне поле для створення своїх MDI-прикладів, може навіть набагато краще за те, що ми з вами створили.