Graafisen käyttöliittymän laadintatekniikkaa

Swingin komponentteja

Paneeli - JPanel
Toimii säiliönä muille graafisille komponenteille, sekä määrää sen miten komponentit asetellaan näkymään käyttäjälle. Paneeliin voidaan asettaa valittu layoutManager, jolla saadaan halutunlainen asettelu sille miten komponentit näkyvät paneelissa.

Esim. Luodaan paneeli ja asetetaan siihen GridLayout tyyppinen asemointi.

    buttonPanel = new JPanel();
    buttonPanel.setLayout(new GridLayout(4,4) );
Paneeliin voidaan lisätä komponentteja add() -metodilla
    buttonPanel.add(button);
Myös toisten paneeleiden lisääminen paneeliin on mahdollista
public CalculatorPanel() {
    setLayout(new BorderLayout() );
    ...
    // Lisätään painikepaneeli laskimen paneeliin.
    add(buttonPanel,"Center");
Tekstikenttä - JTextField
Tässä komponentissa voidaan editoida yhtä rivillistä tekstiä. On kätevä mm. lomakkeisiin jossa pyydetään käyttäjältä jotain tietoja (nimi, osoite,..) tai vaikka näyttämään laskimen tekemät laskutoimitukset.

Esim. Luodaan laskimen näyttöä varten tekstiruutu ja asetetaan se sellaiseksi, että kentän sisältöä ei voida muuttaa:
    screen = new JTextField("");
    screen.setEditable(false);
Kentän sisältö voidaan hakea String-olioon getText -metodilla ja kenttään voidaan viedä teksti setText -metodilla.
screen.setText(screen.getText() + s);
Tekstialue - JTextArea
Erona tekstikenttään, tähän komponenttiin voidaan sijoittaa vapaasti useita rivejä tekstiä. Toimii mm. tekstieditorin kenttänä johon teksti syötetään tai ikkunana johon voidaan tulostaa jokin raportti. Teksti alue voidaan sijoittaa scrollPanel-paneelin sisään, jolloin tekstialueen sisältöä voidaan vierittää, jos se ei mahdu näkyvän alueen sisälle.

JTextArea-komponentin muodostimessa annetaan parametreina tekstialueen rivien ja sarakkeiden määrä
textArea = new TextArea(5,30); // 5 riviä ja 30 saraketta
Otsikot - JLabel
Erilaisten komponenttien tai lomakkeiden otsikot. Käyttäjä ei pysty editoimalla muuttamaan tekstiä. Tekstin sisällön voi hakea tai sitä voidaan muuttaa getText() ja setText() -metodeilla.

Valintaruutu - JCheckBox

Jos halutaan vastaus "kyllä" tai "ei", niin kannattaa käyttää valintaruutua. Valintaruutuun kuuluu automaattisesti tehtävän valinnan kertova otsikko, mikä kirjoitetaan muodostimeen. Esim. pieni esimerkki valintapaneelin luomisesta
public MyCheckBoxFrame() {
  JPanel p = new JPanel();
  bold = addCheckBox(p,"Lihavoitu");
  italic = addCheckBox(p,"Kursivoitu");
}

JCheckBox addCheckBox(JPanel p, String name) {
  JCheckBox c = new JCheckBox(name);
  c.addActionListener(this);
  p.add(c);
  return c;
}
Valintanappi - JRadioButton
Valinta- tai radionapin käyttö on siinä tapauksessa hyvä jos pitää valita useasta vaihtoehdosta tismalleen yksi. Valintanapit toteutetaan Javassa muodostamalla ensin ButtonGroup komponentti johon voidaan sitten lisätä JRadioButton -komponentteja.

Esim. Lisätään kaksi painiketta painikeryhmään
smallButton = new JRadioButton("Pieni", false);
mediumButton = new JRadioButton("Keskikoko", true);
ButtonGroup group = new ButtonGroup();
group.add(smallButton);
group.add(mediumButton);
Luettelot - JList Jos pitää valita tietty vaihtoehto lukuisista valinnoista, niin valintanappeja parempia vaihtoehtoja ovat luettelot. JList -komponentti muodostaa luettelon josta voidaan valita tietty alkio. Ja JComboBox -komponentti muodostaa pudotusvalikon, josta voidaan valita tietty alkio.

Esim. Muodostetaan luettelo tekemällä ensin merkkijonotaulukko, joka välitetään muodostimelle parametrina
String[] words = {"1", "2", "3", "4", "5"};
JList wordlist = new JList(words);

Piirtäminen Javassa

Paneeliin piirrettäessä täytyy määritellä luokka, joka laajentaa JPanel -luokkaa. Ja syrjäyttää tässä luokassa paintComponent -metodi. PaintComponent-metodi on oikeastaan JComponent eli kantaluokka kaikille swingin osille.

PaintComponent -metodia ei tarvitse kutsua itse, vaan sitä kutsutaan aina automaattisesti esim. ikkunaa suurentaessa. Tätä prosessia ei pitäisi edes häiritä. Kun ikkuna avataan ensimmäisen kerran, sen pitää tietenkin suorittaa lauseet, jotka määräävät miten ikkunan osat piirretään.

Eroja AWT:n:
AWT:ssa piirrettiin Component-luokan aliluokkaan Canvas, joka oli tarkoitettu vain piirtämiseen. Swingissä voidaan piirtää mihin tahansa Swing-komponenttiin.

Enää ei käytetä paint -metodia. Tämän metodin syrjäyttämisen jälkeen ohjelma ei edes toimisi oikein, koska siinä häirittäisiin JComponent.paint -metodia.

Swingin komponentit käyttävät puskurointia poistaakseen välkkymisen.

2D Kuviot
Java.awt.Graphics-pakkauksen metodeilla drawLine, drawArc, drawPolyLine ja drawPolygon piirretään grafiikkaolioon suoria ja käyriä viivoja.

Esim. Helpoin tapa piirtää Javalla monikulmio on luoda monikulmio-olio, lisätä siihen pisteitä ja kutsua drawPolygon -metodia:
Polygon p = new Polygon();
p.addPoint(10,10);
p.addPoint(10,30);
p.addPoint(20,20);
p.drawPolygon(p);
Metodeilla drawRect, drawRoundrect, draw3DRect ja drawOval piirretään suorakulmioita ja soikioita.

Esim. Piirretään ympyrä, jonka vasen yläkulma on pisteessä (0,0). Ympyrä saadaan erikoistapauksena ovaalista asettamalla leveys ja korkeys (kaksi viimeistä parametria) samanpituisiksi.
g.drawOval(0,0,40,40);
Värit
Värin vaihtaminen onnistuu Java.awt.Graphics -luokan metodilla setColor. Kun halutaan piirtää komponentti/kuvio tietyllä värillä, niin ensin valitaan väri jota käytetään piirtokäskyissä.

Esim. Piirretään kaksi eri väristä tekstiä.
g.setColor(Color.blue);
g.drawString("Hello", 70, 100);
g.setColor(Color.green);
g.drawString("World", 75, 125);

Layout -managerit

FlowLayout:
Virtaava asettelutapa. Virtaavassa asettelutavassa komponentit sijoitetaan vierekkäin ja kun tila ei enää riitä, niin aloitetaan uusi rivi. Komponenttien koko ei muutu ikkunan kokoa muutettaessa. Tapa millä komponentit sijoitetaan riville voidaan valita joko keskittämällä ne säiliöön tai tasaamalla säiliön vasempaan tai oikeaan reunaan. Tämä on paneelin oletusasettelu.

Border Layout:
Reuna-asettelu. Tässä asettelussa voidaan määrätä yksittäisen komponentin sijoittuminen säiliöön, joko keskelle, yläreunaan, alareunaan, vasemmalle tai oikealle.

Grid Layout:
Ruudukko-asettelu. Komponentit sijoitetaan ruudukkoon, jossa rivien ja sarakkeiden määrä on ennalta määrätty. Jos ikkunan kokoa muutetaan, niin solujen keskinäiset suhteet säilyvät jolloin komponenttien koko muuttuu.

Box Layout:
Toimii kuten virtaava-asettelu, mutta komponentteja voidaan sijoittaa myös pystysuunnassa, jolloin komponentit pinotaan päällekkäin.

The Grid Bag Layout: Ehkä paras Javan oma asettelumanageri. kuten ruudukko-asettelussa, niin myös tässä komponentit jaetaan ruudukkoihin, mutta solujen ei tarvitse olla keskenään samankokoiset.

Layout-managerit, kuten FlowLayout eivät vaikuta komponenttien koon muuttumiseen ikkunan kokoa vaihdettaessa, koska komponentteja asetellaan sitä mukaa, kun niitä ruudulle mahtuu.

Layout-managerit, kuten GridLayout aiheuttavat komponenttien koon muuttamisen ikkunan kokoa muuttamalla, koska näissä komponenttien määrä riveillä ja sarakkeilla on vakio.

Käyttöliittymä, jossa jotkut komponentit vaihtavat kokoa ja toiset eivät, onnistuu toteuttaa luonnollisesti käyttämällä useita paneeleita, joissa käytetty layout-managereita sen mukaan aiheuttavatko ne komponenttien koon muuttumisen ikkunan kokoa muuttamalla, vai eivät.

Javan tapahtumankäsittely

Javan tapahtumankäsittely toimii karkeasti siten, että tapahtumien lähteisiin (painikkeet, vierityspalkit,..) kohdistuu jokin tapahtuma ja nämä lähteet lähettävät ilmoituksen niille kuuntelijaolioille, jotka on rekisteröity kyseistä tapahtumaa varten. Tapahtuman tieto löytyy tapahtumaoliosta, kaikki nämä oliot on johdettu luokasta Java.util.EventObject. Kuuntelijaoliot päättelevät tapahtumaolion sisällöstä siitä miten tapahtumaan reagoidaan.

AWT:n tapahtumatyypit:
Kuuntelijaolion tulee toteuttaa jokin ???Listener -rajapinta. Rajapinnassa on esitelty ne metodit, mitkä sen toteuttavan luokan tulee sisältää.

AWT:n kuuntelija-rajapinnat:
Esim. ActionListener -rajapinta, johon kuuluu pakollisena actionPerformed -metodi, missä toteutetaan kuuntelijaolion reagointi tapahtuman tiedon perusteella.
public class tapahtumanKuuntelijaOlio implement ActionListener {
  ...
  public void actionPerformed(ActionEvent evt) { ...
Monesti ei tarvita kaikkia metodeja, jotka rajapinta sisältää ja tällöin olisi turhaa kirjoittaa kaikkiin metodeihin sisältö. Javassa tällainen ongelma on kierretty sovitinluokilla. Tällainen luokka on toteutettu AWT:n jokaisesta kuuntelija-rajapinnasta, jossa on enemmän kuin yksi metodi. Sovitinluokassa on yksinkertaisesti jokainen metodi kirjoitettu ilman sisältöä. Laajentamalla sovitinluokkaa voidaan toteuttaa sisältö vaan niille metodeille joita tarvitaan.

Esim. toteutetaan WindowListener -rajapinnasta ainoastaan tapahtuma ikkunan sulkemiselle käyttämällä WindowAdapter -sovitinluokkaa (Core Java 2).
public class Terminator extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0);
  }
}
Kuuntelijaolio rekisteröidään lähdeolioon seuraavalla lauseella:
tapahtumaLähdeolio.addTapahtumaListener(tapahtumanKuuntelijaOlio);
Tapahtuman aiheuttaja voidaan saada selville kahdella tavalla: Esim. Lisätään paneeliin kaksi painiketta, jotka molemmat ovat tapahtumien lähteitä ja rekisteröidään paneeli kuuntelemaan painikkeiden tapahtumia.
public class ActionListenerPanel extends JPanel implement ActionListener {
  JButton b1;
  JButton b2;

  public ActionListenerPanel() {
    b1 = new JButton("painike1");
    b2 = new JButton("painike2");
    add(b1);
    add(b2);
    b1.addActionListener(this);
    b2.addActionListener(this);
  }

  public void actionPerformed(ActionEvent evt) {
    Object source = evt.getSource();
    String command = evt.getActionCommand();
    if (source == b1)
      System.out.println("Painiketta 1 painettiin);
    else if (command.equals("painike2"))
      System.out.println("Painiketta 2 painettiin);
  }


Viitteet:

[1] Core Java 2 - Horstmann, Cornell