Poglavlje 3 GUI aplikacije i JavaBeans
3.10 Aplet i aplikacija istovremeno
Iz prethodnih primera smo videli da je raspoređivanje komponenti na aplet i rukovanje događajima identično kao i kod klasičnih grafičkih aplikacija. Zato i nema puno tehničkih razlika u razvoju aplikacije i apleta. Sada sledi primer kako napisati program koji može biti i aplet i aplikacija, zavisno od toga na koji način se pokrene. Pošto je u pitanju aplet, osnovna klasa mora naslediti klasu JApplet. A kako je u pitanju i aplikacija, dodaćemo ovoj klasi metodu main.
import java.awt.*;
import java.awt.event.*; import javax.swing.*;
public void init() {
getContentPane().add(new JLabel("Aplet i aplikacija!")); }
static class WL extends WindowAdapter { AppletApplicationTest a;
public WL(AppletApplicationTest a) { super();
this.a = a; }
public void windowClosing(WindowEvent e) { a.stop();
a.destroy(); System.exit(0); }
}
public static void main(String args[]) {
JFrame f = new JFrame("AppletApplicationTest");
AppletApplicationTest a = new AppletApplicationTest(); f.addWindowListener(new WL(a)); a.init(); a.start(); f.getContentPane().add(a, BorderLayout.CENTER); f.setSize(300, 200); f.setVisible(true); } }
Posmatrajmo metodu main ove aplikacije. Vidimo da se u njoj kreira novi prozor klase JFrame, a zatim se kreira i aplet objekat. Prozoru se doda osluškivač događaja za zatvaranje prozora klikom na dugme close ( ). Dalje, poziva se metoda init apleta, a zatim i metoda start. Na ovaj način aplikacija simulira Web čitač prilikom pokretanja apleta. Aplet se postavlja na sredinu prozora, postave se dimenzije prozora i on se prikaže.
Suština se ovde nalazi u inicijalizaciji apleta (metode init i start) i njegovom postavljanju na prozor aplikacije. Na taj način aplet će biti prikazan u osnovnom prozoru aplikacije i imaćemo utisak da je u pitanju klasična GUI aplikacija.
Klikom na dugme close zatvara se aplikacija. Pri tome, potrebno je ponovo simulirati Web čitač: pozivaju se metode stop i destroy našeg apleta.
Dakle, možemo da zaključimo da je omogućavanje apletu da se koristi i kao aplikacija vrlo jednostavno: svodi se na dodavanje metode main i jednog osluškivača događaja. Sadržaj metode main i obrada događaja se praktično i ne menjaju, tako da se ovaj segment koda može kopirati u sve slične programe.
3.11 Korisnički definisane komponente
Swing biblioteka obezbeđuje relativno bogat izbor komponenti za izgradnju korisničkog interfejsa, ali to ponekad ne mora biti dovoljno. Pisanje novih komponenti koje će imati neke specifične mogućnosti je relativno jednostavno. Potrebno je naslediti neku od postojećih komponenti i redefinisati potrebne
metode. Ako nijedna konkretna komponenta nije adekvatna za ovakvo nasleđivanje, može se koristiti generiška komponenta JComponent. Sledi primer jedne korisnički definisane komponente koja predstavlja labelu uokvirenu tankom linijom.
import java.awt.*; import javax.swing.*;
public class UserDefined extends JComponent { public UserDefined(String text) {
this.text = text; }
/** Iscrtava komponentu */
public void paint(Graphics g) { Dimension s = getSize(); g.setColor(Color.black);
g.drawRect(0, 0, s.width - 1, s.height - 1); g.drawString(text, 0, 10);
}
/** Vraća poželjnu veličinu komponente */
public Dimension getPreferredSize() { int width = 70, height = 20;
Graphics g = getGraphics(); FontMetrics fm = null; if (g != null) fm = g.getFontMetrics(); if (fm != null) { width = fm.stringWidth(text); height = fm.getHeight(); }
return new Dimension(width, height); }
/** Tekst koji se ispisuje */
private String text; }
U primeru se vidi da je nasleđena klasa JComponent. Redefinisane su dve njene metode, paint i getPreferredSize. Metoda paint se poziva kad god je potrebno da se komponenta iscrta (prilikom pomeranja prozora, preklapanja prozora, itd). Nju poziva Swing okruženje. Samo crtanje se obavlja koristeći objekat klase Graphics, koji ima veliki broj metoda za crtanje. U okviru ove metode postavlja se crna boja kao tekuća boja za crtanje, iscrtava se pravougaonik oko ivica komponente, i unutar njega se ispisuje dati tekst.
Metoda getPreferredSize vraća dimenzije “koje bi komponenta volela da ima”. Ovu metodu će pozivati layout manager prilikom raspoređivanja komponenti na prozor. Izračunavanje ove “poželjne veličine” komponente se vrši na osnovu dimenzija koje će zauzimati tekst kada se ispiše.
Ovako definisana komponenta koristi se u aplikacijama i apletima na isti način kao i komponente iz biblioteke. Sledi primer apleta koji koristi prethodno
definisanu komponentu. Komponente se konstruišu i dodaju na površinu apleta na uobičajen način.
import java.awt.*; import javax.swing.*;
public class UserDefinedTest extends JApplet { public void init() {
Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(u1);
cp.add(u2); }
UserDefined u1 = new UserDefined("Tekst u komponenti br. 1"); UserDefined u2 = new UserDefined("Tekst u komponenti br. 2");
}
Slika 3.6 prikazuje izgled ovog apleta.
Slika 3.6. Aplet sa korisnički definisanim komponentama
3.12 JavaBeans
JavaBeans je standard za kreiranje softverskih komponenti koje imaju svoje osobine i ponašanje i koje se mogu koristiti u okviru RAD (Rapid Application Development) alata kao što su Borland JBuilder, Symantec Visual Café, itd.
Svaka JavaBean komponenta ima svoja svojstva (properties) i reaguje na neke događaje (events). Formalno posmatrano, JavaBean je svaka Java klasa za koju važi:
1. Ima podrazumevani konstruktor (konstruktor bez parametara koji je public).
2. Za svako svojstvo koji se zove xxx moraju da postoje public metode
setXxx i getXxx (obratite pažnju na odnos velikih i malih slova!). Atribut klase koji bi sadržao vrednost tog svojstva nije obavezan!
3. Za svaki događaj predstavljen klasom xxxEvent na koji komponenta može da reaguje, moraju da postoje metode addxxxListener(XxxListener) i removeXxxListener(XxxListener).
Modifikujmo sada komponentu UserDefined prikazanu u prethodnom primeru tako da postane JavaBean. Umesto konstruktora koji prima parametre, ovde je sada podrazumevani konstruktor. Tekst koji se ispisuje u okviru komponente opisaćemo property-jem text. To znači da klasa mora posedovati metode setText i getText. Kako naša komponenta već nasleđuje JComponent, nasleđuje više addXxxListener/removeXxxListener metoda. Među njima su i addMouseListener i removeXxxListener koje omogućavaju reagovanje na događaje izazvane mišem.
import java.awt.*;
import java.awt.event.*; import javax.swing.*;
public class UserDefinedBean extends JComponent { public UserDefinedBean() {
}
public void setText(String s) { text = s;
}
public String getText() { return text;
}
public void paint(Graphics g) { Dimension s = getSize(); g.setColor(Color.black);
g.drawRect(0, 0, s.width - 1, s.height - 1); g.drawString(text, 2, 12);
}
public Dimension getPreferredSize() { int width = 70, height = 20;
Graphics g = getGraphics(); FontMetrics fm = null; if (g != null) fm = g.getFontMetrics(); if (fm != null) { width = fm.stringWidth(text); height = fm.getHeight(); }
return new Dimension(width + 5, height + 2); }
/** Ovde čuvamo property text */
private String text = "text1"; }
Kada ovakvu komponentu upotrebimo u okviru RAD alata kakav je JBuilder, on će biti u stanju da prepozna svojstva i događaje ove komponente i omogući nam da ih podešavamo u vreme pisanja aplikacije (design-time). Slika 3.6a prikazuje svojstva koja je JBuilder “otkrio” u našoj komponenti, a slika 3.6b prikazuje događaje na koje naša komponenta može da reaguje. Vidimo da se u spisuku property-ja nalazi i text, koji smo sami definisali. Ostali property-ji su nasleđeni iz klase JComponent.
a) b)
Slika 3.6. Svojstva i događaji komponente u JBuilder-u
JBuilder ne koristi nikakve posebne tehnike za “otkrivanje” svojstava i događaja u komponentama. Naprotiv, koristi standardan introspection mehanizam da otkrije te informacije. Ovaj mehanizam implementiran je u klasi java.beans. Introspector. Na kraju, sam JBuilder je pisan u Javi, tako da je prirodno da koristi mehanizme koje obezbeđuje Java platforma!