Pod modálnym oknom sedela…

Keď som ešte blahej pamäti programoval hračičkové aplikácie vo Visual Basicu, jednou z prvých vecí v novom megasoftvéri bolo dialógové okno About Box / O aplikácii. Vôbec neprekážalo, že sa po prvom spustení nedalo nič robiť (akurát pozerať do prázdneho okna), ale hlavné bolo menu s krásnou položkou!

About Box je tiež skvelý príklad modálneho okna (modal window). Rozdiel medzi modálnymi a klasickými oknami raz ktosi veselo popísal heslom

modálne sú tie, kde sa nedá klikať do okna za ním a keď to robím, tak Windows pípa.

A naozaj, modálne okno odpútava pozornosť používateľa od hlavného okna a presmeruje jeho pozornosť na dôležitejšiu úlohu, alebo ho upozorní na Naozaj Veľmi Dôležitý Fakt.

Modálne okná v Jave

Modálne okná v Jave sú pomerne jednoduché. Teda; povedzme. Typickému oknu v swingovej aplikácii zodpovedá trieda JFrame. Je určená pre hlavné okná, ktoré sú nezávislé od iných okien. Na Windowse ich spoznáme ľahko: dajú sa maximalizovať a minimalizovať.

Alternatívou je JDialog (sledujte názov, dialóg!). Slúži pre dočasné informácie a veľmi často je otvárané z nejakého iného okna (vlastníka). Najlepšie sa hodí pre… modálne okná! Kým JFrame neviete otvoriť ako modálne okno, s JDialogom to ide bez problémov.

Klasický dialóg v duchu About Box môžeme navrhnúť oddedením od JDialogu. Ak sme si istí, že dialóg bude vždy len modálny, stačí prekryť konštruktor JDialog(Owner).

package sk.upjs.ics.novotnyr.modal;

import java.awt.Window;

import javax.swing.*;
import javax.swing.border.*;

public class AboutDialog extends JDialog {

    public AboutDialog(Window owner) {
        super(owner, "About...", JDialog.DEFAULT_MODALITY_TYPE);

        JLabel label = new JLabel("Modal Dialog Demo v 1.0");
        add(label);
    }
}

Predovšetkým zavoláme rodičovský konštruktor, kam pošleme vlastníka (owner) a popisný reťazec, ktorý sa objaví v záhlaví okna. Tretí parameter je typ modality: preddefinovaný konštanta DEFAULT_MODALITY_TYPE zapne štandardné správanie… teda nastaví dialóg ako modálny.

<div class="note">
Typ modality je k dispozícii od JDK 1.6. Doposiaľ sa využívali konštruktory, kde modalita mohla byť len booleovsky zapnutá alebo vypnutá. Nové nastavenie modality berie do úvahy nuansy novších správcov okien. Detaily o type modality možno nájsť v <a href="http://docs.oracle.com/javase/7/docs/api/java/awt/Dialog.ModalityType.html">dokumentácii</a>.
</div>

Ak chceme našu triedu vidieť v akcii, môžeme si do nej dodať dočasný main():

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                AboutDialog aboutDialog = new AboutDialog(null);
                aboutDialog.setVisible(true);

                aboutDialog.dispose();
            }
        });
    }
}

Ak to spustíme, čo presne sa stane? Budeme vidieť škaredé mikrookno, zrejme v ľavom hornom rohu. Natiahnuť si ho budeme musieť sami. Upravíme to neskôr.

Zobrazovanie a skrývanie modálnych okien

Vytvorenie okna zodpovedá vytvoreniu inštancie. Keďže ide o biedny testovací príklad, vlastníka sme dočasne nastavili na null — dialóg je prvé okno v systéme.

setVisible(true) blokuje

Ďalej celé okno zobrazíme cez setVisible(), ale pozor! V prípade modálnych okien volanie tejto metódy s true blokuje ďalšie vykonávanie metódy dovtedy, dokiaľ sa okno nezavrie. To je prirodzené správanie. Sme uprostred nejakej operácie, vyhodíme upozornenie a pokiaľ ho používateľ nezoberie na vedomie zatvorením, tak nepokračujeme v programe.

Ak používateľ zatvorí okno cez systémové zatváradlo (na Windowse červené X), vykonávanie pokračuje.

Zatvorené okno treba odpratať

Na rozdiel od bežných objektov, ktoré v Jave nemusíme upratovať, musíme zatvorené okno upratať alebo dispose()núť. Dispose()nuté okno uvoľní systémové prostriedky súvisiace s réžiou a vykresľovaním grafiky okna a vráti ich späť systému.

aboutDialog.dispose();

Ak tento riadok vynecháme, dialóg sa síce schová, ale stále bude prítomné v aplikácii a bude žrať systémové prostriedky. Dôsledok pre používateľa je pomerne nečakaný: používateľ zo svojho pohľadu zatvorí dialóg, ale v skutočnosti ho len schová. V tomto príklade je to ešte záludnejšie: ak dialóg zavriete, aplikácia stále pobeží, ale nebudete s ňou môcť interagovať.

Šikovníkom môže napadnúť jeden trik: nastaviť dialógu správanie sa v prípade, že je okno zavreté iksnutím:

aboutDialog.setDefaultOnCloseOperation(DISPOSE_ON_CLOSE);

To znamená, že okno sa po zatvorení cez systémové X automaticky dispose()ne. Je to lákavé riešenie, a má svoju logiku (okno sa samo postará o svoje upratanie po zatvorení), ale niektorým vývojárom je pohodlnejšie spárovať upratovanie okna s vytváraním jeho inštancie práve tak, ako sa otváranie súboru páruje s jeho zatváraním. (= ku každému new patrí dispose()).

Zestetičňovanie dialógu

Zmena veľkosti

Predovšetkým zavolajme metódu pack(), ktorá prispôsobí rozmery okna rozmerom komponentov v ňom, čím predídeme stavu „vidím len hlavičku a obsah musím zobraziť ťahaním”.

Vycentrovanie

Centrovanie dialógu v strede obrazovky sa dosahuje cheatom. Metóda umožňuje relatívne umiestnenie vzhľadom k inému komponentu (popravde, z dokumentácie som správanie nepobral), ale ak uvedieme nullový komponent, dialóg sa vycentruje. Pračudesné, ale je to i v dokumentácii.

setLocationRelativeTo(Component c)

Zákaz zväčšovania

Načo zväčšovať About Box?

setResizable(false);

Viac luftu

Label s textom by si zaslúžil viac luftu na okolí. Pridajme okolo neho 15pixelový vzduch, teda okraj, teda border.

Border border = BorderFactory.createEmptyBorder(15, 15, 15, 15);
// ... //       
label.setBorder(border);

Výsledný konštruktor

public AboutDialog(Window owner) {
    super(owner, "About...", JDialog.DEFAULT_MODALITY_TYPE);

    Border border = BorderFactory.createEmptyBorder(15, 15, 15, 15);

    JLabel label = new JLabel("Modal Dialog Demo v 1.0");
    label.setBorder(border);
    add(label);

    setLocationRelativeTo(null);
    setResizable(false);

    pack();
}

Volanie modálneho okna z JFrame

Dodajme si teraz do projektu skutočné hlavné okno. Oddedíme od JFrame klasickým spôsobom, dodáme jeden JButton a celé to následne sfunčkníme a zestetičníme. Dodáme si ešte aj metódu main(), kde náš frame zobrazíme bežným spôsobom.

package sk.upjs.ics.novotnyr.modal;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class MainForm extends JFrame {
    public MainForm() {
        JButton button = new JButton("About...");
        add(button, BorderLayout.PAGE_START);

        setExtendedState(MainForm.MAXIMIZED_BOTH);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MainForm mainForm = new MainForm();
                mainForm.setVisible(true);
            }
        });
    }
}

Na tomto nie je nič výnimočné: tlačidlo pridáme na vrch okna (používa sa BorderLayout, konštanta PAGE_START je ekvivalentná starej konštantne pre sever, teda NORTH) a okno maximalizujeme (len tak pre zábavu).

Spustíme si a poobdivujeme…

…a nerobí to nič.

Dodajme k tlačidlu jeden action listener, ktorý obslúži klik.

    JButton button = new JButton("About...");
    button.addActionListener(new ActionListener() {     
        public void actionPerformed(ActionEvent e) {
            AboutDialog aboutDialog = new AboutDialog(MainForm.this);
            aboutDialog.setVisible(true);

            aboutDialog.dispose();
        }
    });

Všimnime si filozofiu prevzatú z main() vo vedľajšej triede. Rozdiel je v

AboutDialog aboutDialog = new AboutDialog(MainForm.this);

V tomto prípade potrebujeme do dialógu dodať vlastníka dialógu, teda „to okno na pozadí, do ktorého sa nebude dať klikať”. Vlastníkom je MainForm, na ktorý sa odkážeme cez this, ale… obslužný kód pre tlačidlo je vo vnútri ActionListenera, čo je vnútorná trieda v triede MainForm. Použitie this by sa odkazovalo na objekt typu ActionListener, čo je blbosť. Potrebujeme sa odkázať na „mňa”, čím sa myslí inštancia MainFormu, čo dosiahneme cez

MainForm.this

Hľa, hotovo

Poskúšajte si to. Klikajte na tlačidlo About…, otvorí sa náš známy dialóg About Box a klikať do hlavného okna nebudete až dovtedy, kým About Box nezavriete.

Toť modálne okno v akcii!

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *