Ohjeita käyttöliittymien tekoon

Java Tutorial:ssa on kohdassa 'Creating a GUI with JFC/Swing' kuvailtu aika laajalti käyttöliittymien tekoa, mutta tässä pientä yhteenvetoa.

Lomakkeiden ulkonäkö

Lomakkeiden luonnissa ideana on, että kullekin ikkunalla on oma LayoutManager, joka vastaa käyttöliittymäelementtien sijoittelusta. Niille siis ei tarvitse asettaa koordinaatteja. Vaihtoehtoisia LayoutManager:eja on lukuisia. Tässä hieman esimerkkikoodia niistä monipuolisimmalle ja monimutkaisimmalle eli GridBagLayout:lle.

public class SalasanaIkkuna extends JFrame {

....

    Container pane = getContentPane();

    pane.setLayout(new GridBagLayout());
    GridBagConstraints c = new GridBagConstraints();
    c.anchor = GridBagConstraints.WEST; 
    // Ankkuroidaan elementit länteen, 

    c.fill = GridBagConstraints.HORIZONTAL;
    // Jos komponentille on enemmän tilaa käytössään kuin se vaatisi, 
    // niin kasvatetaan komponenttia leveyssuunnassa.

    c.insets = new Insets(2,2,2,2);
    // Jätetään kaikkien elementtien ympärille 2 pikseliä tyhjää tilaa.
    c.gridx = 0; 
    c.gridy = 0; 

    jlYritys = new JLabel("Yritys:");
    pane.add(jlYritys,c);
    c.gridy++;

    jtfYritys = new JTextField(14);
    pane.add(jtfYritys,c);
    c.gridy++;

    etc.
     
    c.gridx = 0;
    c.gridwidth = GridBagConstraints.REMAINDER;
    // Jos halutaan, että joku elementti vie leveyssuunnassa useampia soluja, 
    // niin sille voidaan asetaa gridwidth:ksi käytettävien solujen määrä 
    // tai sitten ylläoleva, jolloin elementti vie koko loppurivin.
    jlLAFLabel = new JLabel("Available Look And Feels:");
    pane.add(jlLAFLabel, c);

    etc.

Eri LayoutManager:eja voi asetella sisäkkäin asettamalla johonkin sopivaan kohtaan JPanel -komponentin. JPanel ei näy käyttäjälle, periytyy Container:sta, sille voi asettaa LayoutManager:in ja sille voi antaa .add -käskyjä.


Lomakkeiden toiminnallisuus

Tehtäessä toiminnallisuutta käyttöliittymään, ideana on, että kullekin komponentille voidaan asettaa kuuntelijaluokkia, joiden metodeja komponentti kutsuu, kun tulee sopiva tapahtuma. Tällaisia tapahtumia ovat esim. hiiren liikkeet, napin painallukset, focuksen siirtyminen kenttään / pois kentästä, näppäin painallukset, ikkunan avautuminen ja sulkeutuminen, etc.

Näitä listener:ejä voi asettaa kolmella eri tavalla, jotka on esitelty alempana.

Tapa 1 : toteuta rajapinta

Käyttöliittymä -luokka voi toteuttaa sopivan rajapinnan, esim.:

public class LahetysIkkuna extends JFrame implements ActionListener {

....

    JButton jb = new JButton("Lähetä");
    jb.addActionListener( this );
    jb.setActionCommand("Laheta");
    pane.add( jb, c );
  

....

  public void actionPerformed( ActionEvent e) {

    if (e.getActionCommand().equals("Laheta")) {
       ...
    }
  }

Tapa 2 : oma luokka

Listener:ksi voidaan asetaa myös kokonaan oma luokkansa, esim.:


    .....

    jtfKentta.addFocusListener( new LookupKuuntelija( jtfKentta, this, ... ) );

    .....

public class LookupKuuntelija implements FocusListener {

  public LookupKuuntelija( JTextField p_jtfKentta, JFrame p_jframe, ... ) {

    ...

  }

  // ---------------- f o c u s G a i n e d --------------
  /**
   * Merkitään vanha arvo talteen.
   */
  // bTemporary == true jos kursori siirtyy ikkunan ulkopuolelle.
  public void focusGained( FocusEvent e) {
    if (!bTemporary) {

      ....

    }
  }

  // -------------------- f o c u s L o s t --------------------
  /**
   * Suoritetaan, kun focus on menetetty. Tarkistetaan lookup. Suoritetaan
   * mahdollisesti lookup-ei-löytynyt.
   */
  public void focusLost( FocusEvent e) {
    if (e.isTemporary())
      bTemporary = true;
    else {
      bTemporary = false;

      ......

    }
  }
}

Tapa 3 : miniluokka

Listener:ksi voidaan asettaa myös oma 'miniluokka', joka periytyy sopivasta yläluokasta ja jonka toteutus seuraa välittömästi perässä. Vaikkakin tämä tapa on nopea, niin laajalti käytettynä se tuottaa melko sotkuista koodia. Niinpä sitä kannattaa käyttää lähinnä tapauksissa, joissa käsitellään vain muutamia eventtejä.


    ....

    setTitle( "Otsikko" );

    addWindowListener( new WindowAdapter() {
	public void windowActivated( WindowEvent e) {
          System.out.println("windowActivated");
	}
	public void windowDeactivated( WindowEvent e) {
          System.out.println("windowDeactivated");
	}
	public void windowOpened( WindowEvent e) {
          System.out.println("windowOpened");
	}
	public void windowClosing( WindowEvent e) {
          System.out.println("windowClosing");
	}
	public void windowClosed( WindowEvent e) {
          System.out.println("windowClosing");
	}
	
      });

    JPanel jpHakuehdot = new JPanel( new GridBagLayout() );
    GridBagConstraints c = new GridBagConstraints();
    c.gridx = 0;
    c.gridy = 0;
    c.anchor = GridBagConstraints.WEST;

    .....

Ylläoleva koodi on tavallaan lyhennysmerkintä, jossa määritellään uusi luokka, joka periytyy WindowAdapter -luokasta, ja samalla luodaan tälle luokalle uusi instanssi.