Image
27.6.2016 0 Comments

Programujeme v Jave /4.časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

Po predchádzajúcej – verím, že dosť náročnej – časti, venovanej správcovi umiestnenia GridBagLayout, tentoraz dokončíme rozprávanie o umiestňovaní komponentov na plochu kontajnera.

Absolútne umiestnenie komponentov

Pokiaľ poznáte vývojárske prostredia, ako napr. Delphi od firmy Borland, tak určite viete, že je tam možné umiestňovať komponenty na plochu formulára pomocou absolútnych súradníc a veľkostí. Pri umiestnení komponentu na formulár sú teda komponentu priradené súradnice vzdialenosti jeho ľavého horného rohu od počiatku súradníc kontajnera (vychádzajme z toho, že počiatok súradníc kontajnera je reprezentovaný jeho ľavým horným rohom, pričom x-ová súradnica rastie smerom doprava a y-ová smerom dole) a takisto je mu priradená nejaká veľkosť. Takýmto spôsobom sú komponenty napevno „prilepené“ na formulár (resp. rodičovský kontajner) a zostávajú na určenom mieste a v určenej veľkosti aj po zväčšení dostupnej plochy. Takýto postup je síce oveľa jednoduchší ako používanie nejakých správcov umiestnenia, na druhej strane však ich použitie umožňuje oveľa flexibilnejší návrh GUI aplikácií a efektívnejšie využívanie dostupného miesta kontajnera. Java však umožňuje aj použitie absolútneho umiestnenia a veľkosti komponentov, a preto vám tieto možnosti teraz opíšem.

Prístupové metódy na umiestnenie a veľkosť komponentov sú obsiahnuté v triede Component, a pretože komponenty knižníc AWT aj Swing sú buď priami, alebo nepriami potomkovia tejto triedy, obsahujú ich všetky tieto komponenty. Na určenie umiestnenia komponentu slúži metóda setLocation(), ktorá existuje v dvoch preťažených verziách. Buď jej ako argument odovzdáte inštanciu triedy Point určujúcu absolútne umiestnenie ľavého horného bodu komponentu od počiatku súradníc kontajnera, alebo jej odovzdáte priamo hodnoty x-ovej a y-ovej súradnice tohto bodu. Získať informáciu o momentálnom umiestnení komponentu môžete pomocou metódy getLocation(), ktorá vracia objekt typu Point, prípadne pokiaľ vás zaujíma len x-ová alebo y-ová informácia, môžete použiť metódu getX(), resp. getY().

Ďalej je potrebné špecifikovať veľkosť komponentu. To umožňuje metóda setSize(), ktorá existuje opäť v dvoch preťažených verziách. Ako argument jej môžete odovzdať inštanciu triedy Dimension alebo celočíselné hodnoty vyjadrujúce šírku a výšku komponentu. Na získanie informácií o veľkosti komponentu slúži metóda getSize(), ktorá vracia objekt typu Dimension, alebo môžete použiť metódy getWidth() a getHeight() na zistenie šírky, resp. výšky komponentu.

Na získanie oboch informácií (aj umiestnenia, aj veľkosti) naraz slúži metóda getBounds() vracajúca objekt typu Rectangle opisujúci umiestnenie komponentu v kontajneri. Takisto je možné použiť metódu setBounds() na nastavenie všetkých týchto vlastností. Môžete použiť verziu tejto metódy prijímajúcu ako jediný parameter objekt typu Rectangle alebo verziu so štyrmi celočíselnými parametrami, postupne x-ová súradnica, y-ová súradnica, šírka a výška komponentu. Táto metóda sa zvykne používať najčastejšie.

Ešte niečo o správcoch

Vráťme sa ešte na chvíľu k správcom umiestnenia. Pokúsim sa trošku naznačiť, akým spôsobom správcovia umiestnenia pracujú. Každý správca umiestnenia musí implementovať rozhranie LayoutManager, prípadne LayoutManager2 (ktoré je odvodené od LayoutManager). Tieto rozhrania deklarujú niekoľko dôležitých metód, ktoré správcovia umiestnenia musia implementovať. Vždy, keď pri pridávaní komponentu voláte metódu add() nejakého kontajnera, postará sa tento kontajner o volanie metódy addLayoutComponent() príslušného správcu umiestnenia, aby ten mohol pridať komponent do svojho zoznamu komponentov, s ktorým pracuje. Metóda layoutContainer() je volaná vždy, keď je potrebné nanovo rozložiť komponenty na ploche kontajnera. Správcovia umiestnenia používajú práve spomínanú metódu setBounds() na umiestnenie komponentu v kontajneri, stačí si pozrieť zdrojový kód napr. správcu umiestnenia GridBagLayout (konkrétne chránenú metódu ArrangeGrid()). Pokiaľ si preštudujete zdrojové kódy nejakého správcu umiestnenia podrobne, zistíte, že funkčnosť správcu umiestnenia spočíva v podstate v tom, že vykoná nejaké viac alebo menej zložité výpočty a na základe nich určí lokáciu a veľkosť komponentov a tieto vlastnosti následne aplikuje metódou setBounds().

Použitie absolútnej veľkosti a lokácie

Pokiaľ sa teda chcete zahrať na správcu umiestnenia (ale bez toho, aby ste implementovali vlastného správcu umiestnenia, čo je, samozrejme, tiež možné) a umiestňovať svoje komponenty do kontajnera na určitú pozíciu a určovať ich veľkosť absolútne, musíte zabezpečiť, aby kontajner nepoužil žiadneho správcu umiestnenia (väčšina kontajnerov má definovaných implicitných správcov, pokiaľ žiadneho neurčíte explicitne). To zabezpečíte tým, že zavoláte metódu setLayout() s parametrom null. Tak zostane určovanie veľkosti i umiestnenia úplne vo vašej réžii. Ukážeme si všetko, o čom som doteraz písal, na nasledujúcom príklade:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class BoundsTest extends JApplet {
  public void init() {
    this.setSize(new Dimension(300, 50));
    Container kontajner = getContentPane();
    kontajner.setLayout(null);
    JButton button = new JButton("Prilepene tlacidlo");
    button.setBounds(10, 10, 150, 20);
    kontajner.add(button);
  }
}

Pokiaľ skúsite zväčšovať a zmenšovať okno appletu, zistíte, že s tlačidlom sa vôbec nič nedeje a je stále umiestnené na svojej pozícii – 10 pixelov od ľavého aj horného okraja kontajnera – a má stále rovnakú veľkosť, ktorú sme definovali na 150 × 20 pixelov.

Týmto by som zakončil rozprávanie o správcoch umiestnenia a prešiel na ďalšiu tému, ktorej ťažiskom budú jednotlivé komponenty a udalostný model.

Udalostný model v Jave

Väčšina moderných a objektovo orientovaných jazykov využíva udalostný model na notifikáciu poslucháčov o nejakej udalosti. Udalosťou môže byť čokoľvek, v prípade GUI aplikácií to môže byť napr. kliknutie myšou alebo stlačenie klávesu, ale aj obyčajné pohnutie myšou. Poslucháčom je objekt, ktorý slúži na spracovanie vzniknutého druhu udalosti. Každý objekt generujúci udalosti by mal poskytovať možnosť registrovať poslucháča udalosti. Počet poslucháčov jednej udalosti môže byť ľubovoľný. Keď na chvíľu opustíme teoretickú rovinu, dá sa povedať, že objektom generujúcim udalosti môže byť napr. tlačidlo, inštancia triedy JButton. Toto tlačidlo umožňuje okrem iného registrovať poslucháčov – objekty typu ActionListener. Tlačidlo si udržiava zoznam všetkých poslucháčov, a pokiaľ dôjde k danej udalosti (v tomto prípade k stlačeniu tlačidla kliknutím alebo stlačením príslušného klávesu), informuje všetkých registrovaných poslucháčov o vzniknutej udalosti. Informovanie prebieha prostredníctvom vopred stanovených pravidiel, teda napr. poslucháč, ktorý implementuje rozhranie ActionListener, musí implementovať metódu actionPerformed() a práve táto metóda je volaná pri vzniku opisovanej udalosti. Preto musíte do tejto metódy pridať obslužný kód na spracovanie tejto udalosti, teda napr. na vykonanie akcie spojenej s kliknutím na tlačidlo.

Triedy poslucháčov (resp. rozhrania) majú obyčajne názvy v tvare XxxListener, teda napr. ActionListener, MouseListener alebo AdjustmentListener. Rozhranie EventListener predstavuje rodičovské rozhranie pre všetkých týchto poslucháčov. Keď sa rozhodnete použiť niektorého z poslucháčov, musíte implementovať všetky jeho metódy, ktorých signatúry nájdete v dokumentácii JDK. V prípade, že takéto rozhranie obsahuje veľmi veľa metód (obsluhovaných udalostí) a vy potrebujete implementovať iba jednu z nich, budete musieť implementovať aj ostatné, hoci ako prázdne. V takýchto prípadoch vám pomôžu tzv. adaptéry, o ktorých si povieme v jednotlivých konkrétnych príkladoch.

Jednotlivé komponenty vždy ponúkajú registráciu poslucháčov prostredníctvom príslušnej metódy v tvare addXxxListener(), teda napr. addActionListener() alebo addMouseListener(). Samozrejme, neplatí to len pre komponenty GUI, ale keďže sa zaoberáme práve nimi, obmedzíme sa vo výklade na ne. Už bázová trieda Component pre všetky komponenty od nej odvodené definuje niekoľko registračných metód pre poslucháčov. Pokiaľ chcete vedieť ktoré, znova vám odporúčam dokumentáciu k JDK.

Pokiaľ vytvoríte inštanciu poslucháča a zaregistrujete ho pri komponente na „odber“ správ, bude volaná príslušná metóda vždy, keď dôjde k danej udalosti. Metódy patriace k rozhraniam poslucháčov obyčajne akceptujú ako parameter objekt opisujúci udalosť, ktorá sa stala. Triedy týchto udalostí majú názvy v tvare xxxEvent, napr. ActionEvent alebo MouseEvent. Tie sú odvodené od triedy AWTEvent alebo priamo od EventObject. Všetky triedy udalostí teda poskytujú základnú metódu getSource(), ktorá vráti odkaz na objekt, ktorý udalosť vyvolal. Tie odvodené od triedy AWTEvent poskytujú navyše na lepšiu identifikáciu udalosti, ktorá nastala, metódu getID(). O tej si tiež povieme v jednotlivých prípadoch, keď jej použitie bude mať opodstatnenie.

Tlačidlo JButton

Prvým komponentom, ktorému sa budeme venovať, bude tlačidlo JButton. Trieda JButton je potomkom triedy AbstractButton a od nej získava možnosť registrovať poslucháčov typu ItemListener, ChangeListener a hlavne ActionListener, ktorého budete v spojení s tlačidlom JButton používať asi najčastejšie. Pomocou metódy addActionListener() môžete zaregistrovať poslucháča implementujúceho rozhranie ActionListener. Toto rozhranie obsahuje jedinú metódu, ktorou je actionPerformed(). Táto metóda bude vykonaná práve vtedy, keď sa používateľ pokúsi vykonať nejakú akciu v súvislosti s pridruženým komponentom, v našom prípade s tlačidlom JButton. To znamená, že k akcii dôjde pri kliknutí na tlačidlo alebo pri stlačení klávesu, ktorý ho aktivuje. Metóde je odovzdaná inštancia triedy ActionEvent, ktorú je možné použiť na zistenie ďalších informácií o vzniknutej udalosti a aj vďaka tomu je možné použiť jedného poslucháča v spojení s viacerými komponentmi. Dosť bolo teórie, v nasledujúcom príklade si vytvoríme vlastného poslucháča ActionListener a pridružíme ho k dvom tlačidlám. Informácie o stlačení niektorého z tlačidiel budeme vypisovať na konzolu:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class ListenerTest extends JApplet {
 
  public void init() {
    this.setSize(new Dimension(300, 40));
    Container kontajner = getContentPane();
    kontajner.setLayout(new FlowLayout());
    ActionListener listener = new MyListener();
 
    JButton button = new JButton("Tlacidlo 1");
    button.addActionListener(listener);
    kontajner.add(button);
 
    button = new JButton("Tlacidlo 2");
    button.addActionListener(listener);
    kontajner.add(button);
  }
 
  class MyListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      System.out.println("Bolo stlacene tlacidlo " + ((JButton) e.getSource()).getText());
    }
  }
 
}

Skompilujte a spustite tento program z príkazového riadka pomocou AppletVieweru a skúste klikať na zobrazené tlačidlá. Na príkazovom riadku uvidíte výpisy o tom, ktoré tlačidlo bolo stlačené. Výpisy sa budú zobrazovať správne pre každé tlačidlo aj napriek tomu, že sme použili iba jednu inštanciu poslucháča MyListener. Trieda MyListener je definovaná ako vnútorná, čo býva bežným postupom v takýchto prípadoch. Všimnite si využitie získanej inštancie ActionEvent v metóde actionPerformed(). Pomocou jej metódy getSource() získame odkaz na objekt, ktorý udalosť vygeneroval. Týmto objektom je vždy jedno z dvoch tlačidiel. Keďže chceme získať text tlačidla, aby sme ho mohli identifikovať, a metóda getSource() vracia objekt typu Object, musíme tento vrátený objekt pretypovať na JButton, aby sme mohli zavolať jeho metódu getText(). Samozrejme, bolo by vhodné overiť, či naozaj ide o inštanciu triedy JButton, ale v našom prípade je to zbytočné, keďže nepoužívame žiadne iné komponenty.

Pokiaľ vopred viete, že daného poslucháča chcete použiť iba raz a pre jeden komponent, môžete ho implementovať ako vnútornú anonymnú triedu:

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
 
public class ListenerTest extends JApplet {
  public void init() {
    this.setSize(new Dimension(300, 40));
    Container kontajner = getContentPane();
    kontajner.setLayout(new FlowLayout());
 
    JButton button = new JButton("Tlacidlo");
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("Bolo stlacene tlacidlo");
      }
    });
    kontajner.add(button);
  }
}

Často sa takisto môžete stretnúť s použitím vnútornej anonymnej triedy a delegovaním udalosti na metódy definované mimo tejto triedy. Tento postup často využívajú vývojové prostredia RAD, ako napr. JBuilder od firmy Borland:

    JButton button = new JButton("Tlacidlo");
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        tlacidloClicked(e);
      }
    });
 
//...
  public void tlacidloClicked(ActionEvent e) {
    System.out.println("Bolo stlacene tlacidlo");
  }

Ako vidíte, vo vnútornej anonymnej triede je len minimum toho najnutnejšieho kódu na to, aby mohla byť udalosť delegovaná na metódu tlacidloClicked(), ktorá sa už postará o ďalšie jej spracovanie. Názov metódy zvykne byť zložený od názvu premennej odkazujúcej na daný komponent a udalosti, ktorú chceme spracovať. Takýmto spôsobom je možné napísať oveľa prehľadnejší kód, ako keby ste vložili celú implementáciu obsluhy udalosti do tela metódy v anonymnej triede, čo prispieva k jednoduchšej údržbe celej aplikácie.

Ako som už spomenul, trieda JButton je potomkom triedy AbstractButton, a tak o nej získava možnosť registrovať ďalšie typy poslucháčov, ako napr. ItemListener. Tento postup však bude asi zbytočný, pretože v súvislosti s triedou JButton má význam použitie iba poslucháča ActionListener. Použitie ostatných poslucháčov si ukážeme na príkladoch iných komponentov odvodených od triedy AbstractButton. Jediným z týchto ďalších poslucháčov, ktorého tu ešte spomeniem, bude ChangeListener. Tento poslucháč slúži na spracovanie udalostí, ktoré nastanú pri zmenách stavu tlačidla (či už ide o Jbutton, alebo hocijaké iné tlačidlo). V prípade výskytu takejto udalosti je volaná metóda stateChanged(), ktorej je odovzdaná inštancia triedy ChangeEvent opisujúca udalosť.

 

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
 
public class ListenerTest extends JApplet {
 
  public void init() {
    this.setSize(new Dimension(300, 40));
    Container kontajner = getContentPane();
    kontajner.setLayout(new FlowLayout());
 
    JButton button = new JButton("Tlacidlo");
    button.addChangeListener(new ChangeListener() {
      public void stateChanged(ChangeEvent e) {
        ButtonModel bm = ((JButton) e.getSource()).getModel();
        System.out.println("armed - " + bm.isArmed());
        System.out.println("enabled - " + bm.isEnabled());
        System.out.println("pressed - " + bm.isPressed());
        System.out.println("rollover - " + bm.isRollover());
        System.out.println("selected - " + bm.isSelected());
      }
    });
    kontajner.add(button);
  }
 
}

Tento kód vypisuje informácie o stave tlačidla. Pri volaní metódy stateChanged() je najprv získaný model tlačidla, ktoré vyvolalo udalosť. Následne sú z neho získané informácie o stave tlačidla. Metóda isArmed() vracia true, pokiaľ je tlačidlo „nabité“, čo v praxi znamená, že po uvoľnení ľavého tlačidla myši bude vykonaná akcia spojená so stlačením klávesu. Táto hodnota je true teda vtedy, keď kliknete na tlačidlo a stále držíte tlačidlo myši. Pokiaľ myšou pohnete mimo plochy tlačidla, metóda isArmed() vráti hodnotu false, pretože ak teraz tlačidlo uvoľníte, nedôjde k pridruženej akcii.

Metóda isEnabled() vracia true vtedy, keď tlačidlo prijíma používateľský vstup, teda vtedy, keď je tlačidlo sprístupnené na kliknutie.

Metóda isPressed() vracia true vtedy, keď je tlačidlo stlačené. Na rozdiel od vlastnosti Armed vracia true aj vtedy, keď používateľ pohne myšou mimo plochy tlačidla, pokiaľ drží stále stlačené tlačidlo myši.

Vlastnosť Rollover nadobúda hodnotu true vtedy, keď je tlačidlo schopné meniť svoj vzhľad pri pohybe myši nad plochou tlačidla a mimo nej.

Posledná vlastnosť Selected je využiteľná, iba ak ide o tlačidlo, ktoré môže nadobúdať dve logické hodnoty, ako napr. zaškrtávacie tlačidlo. V našom prípade je jej použitie úplne zbytočné, pretože JButton túto hodnotu neuchováva.

Skompilujte a spustite tento program a najlepšie pochopíte fungovanie modelu tlačidla. Všimnite si takisto, že napr. pri stlačení tlačidla nastanú až dve udalosti – najprv je tlačidlo „nabité“ (armed) a následne stlačené (pressed).

Záver

Teraz už viete o správcoch umiestnenia dosť na to, aby ste ich mohli bez problémov používať, a aj na to, aby ste sa dokázali zaobísť i bez nich. Takisto sme sa začali venovať komponentom (prvým a zatiaľ jediným je JButton) a udalostnému modelu. V nasledujúcej časti sa budeme venovať ďalším komponentom a udalostiam súvisiacim s nimi.

 

 

Autor: Andrej Chu

Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

ITPro

Linux súkromne i pracovne v2.0 (14. časť): Small Business Server

09.11.2016 14:57

Pojem Small Business Server (malý firemný server) začala používať spoločnosť Microsoft ešte v roku 2000 na označenie servera, ktorý ­dokázal plniť úlohy niekoľkých samostatných serverov. Aplikačná vrs ...

ITPro

Industry 4.0: Fikcia alebo už realita?

09.11.2016 14:52

Štvrtá priemyselná revolúcia je pomenovanie rozsiahlych zmien prudko vstupujúcich do súčasného priemyslu. Nositeľom týchto zmien je digitalizácia výroby a optimalizácia všetkých podnikových procesov v ...

ITPro

Vývoj aplikácií UWP pre Xbox One II.

09.11.2016 14:47

V predošlej časti sme ukázali postup, ako si ­vytvoriť vývojársky účet a aktivovať vývojársky režim na hernej konzole Xbox One, aby ste mohli testovať svoje aplikácie. Výhodou hernej konzoly Xbox je v ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá