Obsługa zdarzeń w JAVIE ((ActionListener,ItemListener,TableModelListener)) "Sercem" działania aplikacji z graficznymi interfejsami użytkownika jest obsluga zdarzeń. JMenu, JToolBar, JButton
Reguły ogólne i mechanizm obsługi zdarzeń Interakcja użytkownika z GUI naszej aplikacji polega na wywoływaniu zdarzeń (np. kliknięcie w przycisk, wciśnięcie klawisza na klawiaturze etc). Zdarzenia są obiektami odpowiednich klas, określających rodzaj zdarzeń. Słuchacze są obiektami klas implementujących interfejsy nasłuchu. Interfejsy nasłuchu określają zestaw metod obsługi danego rodzaju zdarzeń. W klasach słuchaczy definiuje się metody odpowiedniego interfejsu nasłuchu zdarzeń, które określają co ma się dziać w wyniku zajścia określonego zdarzenia (metody obsługi odpowiednich zdarzeń)
Reguły ogólne i mechanizm obsługi zdarzeń Zdarzenie (obiekt odpowiedniej klasy zdarzeniowej) jest przekazywane do obsługi obiektowi-słuchaczowi tylko wtedy gdy Słuchacz ten jest przyłączony do Źródła zdarzenia. (przyłączenie za pomocą odwołania z.addnnnlistener(h),, gdzie: z Źródło zdarzenia, NNN - rodzaj zdarzenia, h Słuchacz danego rodzaju zdarzenia) Przekazanie zdarzenia do obsługi polega na wywołaniu odpowiedniej dla danego zdarzenia metody obsługi zdarzenia (zdefiniowanej w klasiesłuchacza) z argumentem obiekt-zdarzenie. Argument (obiekt klasy zdarzeniowej) zawiera informacje o okolicznościach zajścia zdarzenia (np. komu się przytrafiło? kiedy? jakie ma inne właściwości?). Jako parametr w metodzie obsługi może być odpytany o te informacje.
Reguły ogólne i mechanizm obsługi zdarzeń W Javie standardowo zdefiniowano bardzo dużo różnych rodzajów zdarzeń i interfejsów ich nasłuchu. Rozpatrzmy ogólne zasady na przykładzie zdarzenia "akcja" (obiekt klasy ActionEvent), które powstaje: po kliknięciu w przycisk lub naciśnięciu spacji, gdy przycisk ma fokus (zdolność do przyjmowania zdarzeń z klawiatury), po naciśnięciu ENTER w polu edycyjnym, po wyborze opcji menu, po podwójnym kliknięciu / ENTER na liście AWT (ale tylko AWT, nie dotyczy to listy Swingu - JList) lub na liście rozwijalnej Swingu - JComboBox w innych okolicznościach, ew. zdefiniowanych przez program
Interfejsy nasłuchujące oraz ich metody Listener Interface Adapter Class Listener Methods ActionListener none actionperformed(actionevent) ItemListener none itemstatechanged(itemevent) TableModelListener none tablechanged(tablemodelevent)
Wątek koordynujący zdarzenia Ze względu na efektywność klasy Swing nie są zabezpieczone na wypadek posługiwania się nimi przez kilka wątków [1] naraz. Za obsługę zdarzeń generowanych przez interfejs i rysowanie komponentów odpowiada specjalny wątek wątek koordynujący zdarzenia (ang. event-dispatching thread). Dzięki temu dwa zdarzenia nigdy nie są obsługiwane jednocześnie oraz rysowanie nie jest przerywane przez obsługę zdarzenia. Dla bezpieczeństwa cały kod modyfikujący GUI powinien być wykonywany przez wątek koordynujący zdarzenia. Jak się niedługo przekonasz większość operacji na komponentach GUI i tak wykonywanych jest podczas obsługi zdarzeń (przez wątek koordynujący). W pozostałych przypadkach pracę należy zlecić wątkowi koordynującemu przy pomocy metody SwingUtilities.invokeLater(), bądź SwingUtilities.invokeAndWait(). SwingUtilities.invokeLater(new Runnable() { public void run() { utwórzgui(); );
Zgodnie z podanymi wcześniej regułami musimy zatem stworzyć obiekt słuchacza akcji i przyłączyć go do przycisku. Klasa słuchacza akcji musi implementować interfejs nasłuchu akcji (ActionListener), i w konsekwencji - zdefiniować jego jedyną metodę actionperformed. W tej metodzie zawrzemy kod, który zostanie uruchomiony po kliknięciu w przycisk, np. wyprowadzenie na konsolę napisu "Wystąpiło zdarzenie!".
import java.awt.event.*; // konieczny dla obłsugi zdarzeń import javax.swing.*; class Handler implements ActionListener { //tworzymy słuchacza akcji public void actionperformed(actionevent e) { //jedyna metoda actionperformed System.out.println("Wystąpiło zdarzenie!"); public class GUI extends JFrame { public GUI() { JButton b = new Jbutton("Przycisk reagujący na wciśnięcie"); Handler h = new Handler(); b.addactionlistener(h); add(b); setdefaultcloseoperation(dispose_on_close); pack(); setvisible(true); public static void main(string[] a) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GUI(); );
Możemy zatem implementować interfejs ActionListener w klasie, definiującej okno naszej aplikacji:
class GUI extends JFrame implements ActionListener { GUI() { JButton b = new JButton("Przycisk"); b.addactionlistener(this); // TEN obiekt będzie słuchaczem akcji add(b); pack(); show(); public void actionperformed(actionevent e) { System.out.println("Wystąpiło zdarzenie!"); public static void main(string[] a) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GUI(); );
Anonimowe klasy wewnętrzne dla obsługi zdarzeń Nic nie stoi na przeszkodzie, by implementację interfejsu nasłuchu umieścić w anonimowej klasie wewnętrznej.
Jest to rozwiązanie podobne do implementacji interfejsu w klasie GUI (bo mamy jeden obiekt, obsługujący zdarzenia, który możemy przyłączyć do różnych źródeł). W przypadku interfejsów z jedną metodą obsługi zdarzeń jest w zasadzie kwestią gustu wybór jednego z tych dwóch rozwiązań (dodajmy jednak, że anonimowa klasa wewnętrzna tworzy dodatkowy plik klasowy; implementując interfejs nasłuchu w nazwanej klasie możemy używać jej obiektów do obsługi zdarzeń w innych klasach, co nie jest możliwe przy anonimowej klasie wewnętrznej; za to kod anonimowej klasy wewnętrznej może być lepiej zlokalizowany pod względem czytelności dużego programu
Jeżeli chcemy mieć tylko jeden obiekt klasy słuchacza do obsługi jednego źródła zdarzenia to dobrym rozwiązaniem będzie lokalna anonimowa klasa wewnętrzna:
// konieczne importy: javax.swing.*; i java.awt.event.*; class GUI extends JFrame { GUI() { JButton b = new JButton("Przycisk"); b.addactionlistener( new ActionListener() { public void actionperformed(actionevent e) { System.out.println("Wystąpiło zdarzenie!"); ); add(b); pack(); show(); public static void main(string[] a) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GUI(); );
Tutaj będą stworzone dwie anonimowe klasy wewnętrzne (do obsługi przycisków open i close) oraz po jednym obiekcie-słuchaczu tych klas.
Przykładowo dwa przyciski służące do wczytywania i zapisywania pliku. class GUIfile extends JFrame { GUIfile() { setlayout(new FlowLayout()); //menadżer położenia JButton open = new JButton("Open file"); open.addactionlistener( new ActionListener() { public void actionperformed(actionevent e) { readfile(); ); JButton save = new JButton("Save file"); save.addactionlistener( new ActionListener() { public void actionperformed(actionevent e) { savefile(); ); add(open); add(close); pack(); show();... void readfile() { //... void savefile() { //...... public static void main(string[] a) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GUIFile(); );
Uzyskiwanie informacji o zdarzeniach Nieprzypadkowo metodom obsługi przekazywany jest argument referencja do obiektu-zdarzenia. Dzięki temu możemy uzsyskać rozliczne informacje o zdarzeniu, które mamy obsłużyć. Klasy zdarzeniowe AWT (w tym ActionEvent) pochodzą od klasy AWTEvent, a ta od EventObject. java.lang.object +----java.util.eventobject +----java.awt.awtevent +----java.awt.event.actionevent Wszystkie klasy zdarzeniowe dziedziczą metodę: Object getsource() z klasy EventObject, która zwraca referencję do obiektu źródła zdarzenia.
Dodatkowo, w każdej klasie zdarzeniowej znajdują się metody do uzyskiwania specyficznej dla danego zdarzenia informacji. Np. w klasie ActionEvent mamy metodę: String getactioncommand() która zwraca napis, skojarzony ze zdarzeniem akcji (swoiste polecenie ). Domyślnie jest to: * dla przycisku i elementu menu - etykieta (napis na przycisku) * dla pola edycyjnego tekst zawarty w tym polu * dla listy rozwijalnej (JComboBox) wybrany element listy Domyślne ustawienie możemy zmienić, za pomocą użycia metody: void setactioncommand(string) z klas definiujących wszystkie komponenty mogące generować akcje (Button, AbstractButton, MenuItem, TextField, JTextField, List, JComboBox).
ItemListener Interfejs ten pozwala na obługę tylko jednego zdarzenia itemstatechanged, za pomocą metody itemstatechanged(itemevent evt). Zdarzenie to polega na zmianie stanu jakiegoś spisu, pola opcji, czy pola wyboru. Zarejestrowanie obiektu jako zainteresowanego obsługą tego zdarzenia nastepuje za pomocą metody rejestrującej additemlistener(itemlistener)
Jbutton akcja import java.awt.borderlayout; import java.awt.event.*; import javax.swing.*; public class button{ public static void main(string[] args) { final JTextArea output; JScrollPane scrollpane; JFrame frame = new JFrame("Button actionlistener"); frame.setdefaultcloseoperation(jframe.exit_on_close); JButton dodaj = new JButton("Ok"); JPanel contentpane = new JPanel(new BorderLayout()); contentpane.add(dodaj, BorderLayout.NORTH); output = new JTextArea(5, 30); scrollpane = new JScrollPane(output); contentpane.add(scrollpane, BorderLayout.CENTER); dodaj.addactionlistener(new ActionListener() { public void actionperformed (ActionEvent e) { String zrodlo = e.getactioncommand(); String s = "Zdarzenie akcji: "+ zrodlo+"\n"; System.out.println("Wciśnięto OK"); output.append(s); ); frame.setsize(450, 260); frame.setvisible(true); frame.setcontentpane(contentpane);
public class Menu implements ActionListener, ItemListener { public JMenuBar createmenubar() { JMenuBar menubar = new JmenuBar(); Menu,1 JMenu menu = new JMenu("A Menu"); menubar.add(menu); menuitem = new JMenuItem("Pierwszy"); menuitem.addactionlistener(this); menu.add(menuitem); menu.addseparator(); cbmenuitem = new JCheckBoxMenuItem("A check box menu item"); cbmenuitem.additemlistener(this); menu.add(cbmenuitem); menu.addseparator(); JMenu submenu = new Jmenu("Submenu"); //submenu menuitem = new JmenuItem("Element submenu"); menuitem.addactionlistener(this); submenu.add(menuitem); menu.add(submenu); return menubar; public void actionperformed(actionevent e) { String zrodlo = e.getactioncommand(); System.out.print("Źródlo akcji: "+zrodlo+"\n"); public void itemstatechanged(itemevent e) { // nastepny slajd private static void createandshowgui(){ JFrame frame = new Jframe("Moje menu"); Menu mojemenu = new Menu(); frame.setjmenubar(mojemenu.createmenubar()); //frame nasza ramka
Menu,2 (itemstatechanged) public void itemstatechanged(itemevent e) { JMenuItem source = (JMenuItem)(e.getSource()); String s = "Item event detected." + " Event source: " + source.gettext() + " New state: " + ((e.getstatechange() == ItemEvent.SELECTED)? "selected":"unselected"); System.out.println(s);
import java.awt.*; import javax.swing.*; public class projekt extends JFrame implements ActionListener { projekt () { Container cp2 = getcontentpane( ); //Returns the contentpane object for this frame. JPanel jpanel = new JPanel(new GridLayout(1,0)); cp2.add(jpanel_0,borderlayout.north); JToolBar toolbar = new JToolBar("Toolbar", JtoolBar.HORIZONTAL); JButton openbutton = new JButton("Open",new ImageIcon("open.gif")); openbutton.addactionlistener(this); toolbar.add(openbutton); jpanel.add(toolbar); public void actionperformed(actionevent e) { System.out.println("Wybrano : " + e.getactioncommand()); String ac = e.getactioncommand(); if(ac.equals("new")) newfile(); else if(ac.equals("open")) openfile(); else if(ac.equals("quit")) System.exit(0); public void openfile() { System.out.println("Akcja dla openfile metody..."); Toolbar
TableModelListener import javax.swing.event.*; import javax.swing.table.defaulttablemodel; DefaultTableModel model; JTable table; model = new DefaultTableModel(Dane WIERSZY,Nagłówki KOLUMNY); table = new JTable(model); model.addtablemodellistener( new TableModelListener() { public void tablechanged(tablemodelevent tme) { if (tme.gettype() == TableModelEvent.UPDATE) { System.out.println("Komórkę " + tme.getfirstrow() + ", " + tme.getcolumn() + " zmieniono." + " Nowa wartość komórki " + model.getvalueat(tme.getfirstrow(), tme.getcolumn())); );
Dodawanie/odejmowanie wierszy... //dodawanie przycisków do wykonania akcji... dodaj.addactionlistener(new ActionListener() { public void actionperformed (ActionEvent e) { Object[] tmp = { "", "", "", new Date() ; //Inicjuje/definiuje zmienna typu object i wypenia jej tablice tm.addrow(tmp); //dodaje wiersze ); usun.addactionlistener(new ActionListener() { public void actionperformed (ActionEvent e) { tm.removerow(tm.getrowcount() - 1); //odejmuje wiersze );
Źródło http://edu.pjwstk.edu.pl/wyklady/poj/scb /ObsZda1/ObsZda1.html