A gomb megnyomása

Egy programban a gomb megnyomására szokott történni valami! A mi programunk meg semmit nem csinál.

Tulajdonképpen furán nem csinál semmit: hiszen úgy tűnik, hogy a programnak vége lesz, de az ablak még mindig kinn marad a képernyőn. Hogy is van ez?

Az történik, hogy az első ablak megnyitásával elindul egy eseménykezelő háttérprogram. Ez a program olyan, mint egy jó tündér: figyeli, hogy mit csinálsz az egérrel és a billentyűzettel. Minden egér elmozdítás, kattintás az egy esemény. Természetesen minden gombnyomás is egy esemény.

Ezeket az eseményeket az eseménykezelő eljuttatja a JFrame példányunknak, ami ezeket továbbítja a benne lévő többi widgetnek, ha szükséges. Például, ha a JFrame üres területére kattintasz, akkor a kattintási eseményt megkapja a frame. De ha a gomb fölött kattintasz, akkor a frame tudja, hogy az egér alatt épp egy gomb van, és a gombnak továbbadja a kattintási eseményt.

Végül az események picit megváltoztatják a widgeteket. Ha kattintasz, akkor a gomb benyomódni látszik. Eddig szuper, minden automatikusan megy, de hogy tudjuk megcsinálni, hogy erre valamit csináljon a programunk?

Az eseménykezelők

Az eseményekre eseményfeldolgozót (listener - azaz "hallgatózó") lehet írni, ilyen egyszerű! Az lenne a legjobb, ha a listener egy metódus lenne, ami "magától" meghívódik, ha valaki kattintott a gombra.

Egy a baj. A Javaban minden objektum... nem pedig metódus. Metódust nem lehet átadni paraméternek.

public class HelloGui {
    public static void main(String[] args) {
        JFrame frame=new JFrame();
        frame.setSize(200, 100);
        frame.setTitle("Elso program");
        frame.setVisible(true);
        frame.setLayout(null);

        JLabel label=new JLabel("Gipsz Jakab");
        frame.add(label);
        label.setBounds(5, 0, 100, 21);

        JButton button=new JButton("Ok");
        frame.add(button);
        button.setBounds(5, 25, 75, 21);

        // Ez nem megy
        button.addActionListener(csinald);
    }

    public static void csinald() {
        // ha megnyomjak a gombot
        System.out.println("Katt!");
    }
}

Helyette egy kicsit kacifántosabb a megoldás:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        csinald();
    }
});

Azaz... a buttonnak van egy addActionListener metódusa. Természetesen ez is egy konténer, így ha akarjuk, több listenert is köthetünk a gombra, így a kattintáskor az összes meghívódik majd. A listener maga pedig egy ActionListener osztály példánya - mert objektumot át lehet adni paraméternek. Azért hogy azt csinálja amit mi szeretnénk, override-oljuk az actionPerformed metódusát, amit akkor hív meg, amikor megnyomják a gombot. Végül ebben a metódusban már bármit csinálhatunk, például meghívhatjuk bármely más metódust. Ami azt illeti, ez az actionPerformed egy normális metódus saját maga is, így akár itt is kiírhatunk valamit a konzolra:

public class HelloGui {
    public static void main(String[] args) {
        JFrame frame=new JFrame();
        frame.setSize(200, 100);
        frame.setTitle("Elso program");
        frame.setVisible(true);
        frame.setLayout(null);

        JLabel label=new JLabel("Gipsz Jakab");
        frame.add(label);
        label.setBounds(5, 0, 100, 21);

        JButton button=new JButton("Ok");
        frame.add(button);
        button.setBounds(5, 25, 75, 21);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Katt!");
            }
        });
    }   
}

Feladat: Próbáld ki! Kattintáskor a Console View-en megjelenik, hogy Katt!

Vajon fejben kell tartani mindezt a sok takony kódot?

Nem kell.

Feladat: töröld ki az actionListeneres részt, és kezd újra. Kövesd a következő lépéseket:

és máris készen van az egész váz:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
    }
});

Ennyi volt, így már elég könnyű, igaz?

Mennyi féle listener van?

Sok fajta van! ActionListenere szinte minden widgetnek van, ez hívódik meg, ha rákattintasz. De van MouseListener is - ezzel az egér megnyomását, elengedését, a widget fölé húzását is el lehet kapni. A MouseMotionListener-rel pedig az egér mozgási eseményeit lehet elkapni.

A listenerekben általában van valami paraméter - az ActionListener esetén az ActionEvent típusú e nevű osztály. Ennek sok értelme nincs, de mondjuk a MouseMotionListener-nél a paraméter tartalmazza az egér pozíciójának koordinátáit, ami hasznos lehet.

Feladat: csináld meg, hogy amikor a gombon az egeret lenyomják, írja ki hogy "Megnyomva", amikor elengedik, akkor meg hogy "Elengedve". Tipp: MouseListener.

Feladat: csináld meg, hogy amikor az egeret mozgatják a gomb felett, akkor írja ki az egér x és y koordinátáit.

Listener Java 8 módra

Mikor ezt olvasod, a Java 8-as verziója lesz az elfogadott mindenhol. Ebben van egy érdekes nyelvi bővítés - a funkcionális programozás -, amit fel lehet használni arra, hogy a listenereket egyszerűbben írjuk meg.

// Java 8 előtt
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Katt!");
    }
});
// Java 8 
button.addActionListener(e -> {
    System.out.println("Katt");
});

Ezt az írásmódot azonban csak akkor lehet használni, ha a listener egyféle metódussal rendelkezik (pld. ActionListener). Ha a listenernek többféle metódusa lenne (mint például MouseListener), akkor ez nem megy sajnos, ezért a jövőben maradunk a régi fajta írásmódnál, okés?

Változik az ablak

Ennyi előkészület után igazán megtehetnénk, hogy ne a konzolra írjunk, hanem változtassuk meg az ablakban a label szövegét, nem?

public class HelloGui {
    public static void main(String[] args) {
        JFrame frame=new JFrame();
        frame.setSize(200, 100);
        frame.setTitle("Elso program");
        frame.setVisible(true);
        frame.setLayout(null);

        JLabel label=new JLabel("Gipsz Jakab");
        frame.add(label);
        label.setBounds(5, 0, 100, 21);

        JButton button=new JButton("Ok");
        frame.add(button);
        button.setBounds(5, 25, 75, 21);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // semmi System.out.println!
                label.setText("Katt!");
            }
        });
    }   
}

Ez igazán nem volt nehéz, ugye?

Feladat: csináld meg, hogy a gombra kattintva a gomb elugrik egy véletlen pozícióba! A button.setLocation-al tudod a helyét megváltoztani, a Random-ot meg a konténeres részben már használtad.