Apleturi Java (II)

Asa cum am promis in episodul trecut al serialului "Apleturi Java", in acest episod, ca si in cele ce vor urma, voi prezenta cate un exemplu. Din acest exemplu veti invata cum puteti crea interactiune cu utilizatorul folosind butoane, campuri de text etc.
Exemplul din acest episod se numeste Triunghiul lui Sierpinski. Dati un click aici pentru a vedea apletul ruland, enuntul problemei si cateva alte informatii.

Deoarece apleturile java trebuie sa fie portabile relativ la sistemul de operare, orice mediu de dezvoltare Java furnizeaza clase pentru crearea elementelor de baza necesare unei interfete grafice. Aceste elemente includ: etichete (labels), campuri de text (text fields), butoane (buttons), radio butoane (radio buttons), cutii de selectie (check boxes), meniuri de selectie (choice menus) etc. Toate aceste elemente sunt incluse, asa cum va spuneam si data trecuta, in pachetul java.awt.

Etichete
O eticheta se creeaza apeland constructorul clasei Label, in felul urmator:
Label eticheta = new Label(str, align); unde str este o variabila de tip String care defineste sirul de caractere care va fi scris pe aceasta eticheta, iar align este una dintre valorile Label.LEFT, Label.CENTER sau Label.RIGHT, reprezentand alinierea etichetei la stanga, in centru sau la dreapta.
Dupa crearea etichetei, aceasta trebuie adaugata apletului scriind in cadrul functiei init() urmatoarea linie: add(eticheta).
Dupa cum puteti vedea, eticheta creata mai sus va fi afisata pe suprafata apletului in stanga, in centru sau in dreapta. Acest lucru nu va da prea multa libertate. De exemplu, cum puteti afisa o eticheta la anumite coordonate si cum puteti seta latimea si inaltimea etichetei dupa anumite valori?
Atunci cand creati un aplet, Java creeaza si asigneaza in mod automat un layout implicit. Puteti seta layout-ul ca avand diferite
valori, printre care enumar FlowLayout, GridLayout, BorderLayout, CardLayout, GridBagLayout. Acestea sunt clase care fac parte din pachetul java.awt. Prin urmare le puteti gasi acolo. Eu nu imi propun sa explic aceste clase pentru ca personal nu le folosesc, preferand sa am libertatea de a seta coordonatele si dimensiunile. Cum se face acest lucru? Pentru aceasta, trebuie sa nu fie setat nici un layout, ceea ce se face prin comanda setLayout(null). Apelati aceasta functie prima in init() si va va permite sa dati ce coordonate si dimensiuni doriti etichetelor. Haideti incetul cu incetul sa construim apletul pe care vi l-am propus:

import java.applet.Applet;
import java.awt.*;

public class proba extends Applet {
     Label labelxa = new Label();
     Label labelya = new Label();

     public void init() {
          setLayout(null);
 
          labelxa.setText("xa");
          labelxa.reshape(52,12,24,24);
          add(labelxa);
 
          labelya.setText("ya");
          labelya.reshape(88,12,24,24);
          add(labelya);
         }
}

Am notat in acest exemplu si voi nota si de acum incolo conventional instantele claselor de tip Label, Button etc. dupa clasa din care fac parte. Astfel, un obiect de tip Label il voi nota de exemplu cu labelxa sau labelya.
Deci in exemplul de mai sus am creat mai intai doua obiecte de tip Label. Apoi, in cadrul metodei init() am setat layout-ul pe null. Dintre metodele clasei Label, mai folosite sunt:
public void setText(String str); seteaza sirul de scris pe str,
public String getText(); returneaza sirul afisat.
Clasa Label mosteneste clasa Component, deci si toate metodele implementate de aceasta.
Clasa Component reprezinta o componenta generica ce are ca atribute: coordonata x, coordonata y, latime, inaltime, culoare de foreground, culoare de background, font, vizibilitate etc.
Printre metodele implementate de clasa Component, mai folosite sunt:
public void move(int x, int y); muta obiectul la coordonatele x si y specificate;
public void resize(int latime, int inaltime); redimensioneaza latimea si inaltimea cu valorile specificate;
public synchronized void reshape(int x, int y, int latime, int inaltime); muta si redimensioneaza obiectul la valorile specificate.
Prin urmare, clasa Component ofera mai multe atribute necesare claselor de tip Label, Button etc, si chiar si clasa Applet mosteneste aceasta clasa, chiar daca nu direct (clasa Applet mosteneste clasa Panel, care mosteneste clasa Container, care mosteneste clasa Component). Daca cititi fisierele applet.java, component.java, label.java etc. va veti clarifica in ceea ce priveste mostenirile si metodele acestor clase.

Revenind la exemplul nostru, dupa ce am stabilit textul de scris si pozitia si dimensiunile obiectului, acesta trebuie adaugat apletului prin add(labelxa). Adaugati in acelasi fel etichetele labelxb, labelyb, labelxc, labelyc, labelIteratii, corespunzand celorlalte etichete pe care le-ati vazut in aplet. Apoi compilati programul si rulati-l, apelandu-l dintr-un fisier .html in care sa aveti linia:
<applet code=triunghi.class width=500 height=500></applet>

Campuri de text
Obiectele de tip TextField sunt asemanatoare cu cele de tip Label. Deci le veti crea astfel:
 TextField textFieldxa = new textField();
 TextField textFieldya = new textField();
In corpul functiei init() veti seta textul implicit, coordonatele si dimensiunile, si apoi le veti adauga apletului astfel:
  textFieldxa.setText("250");
  textFieldxa.reshape(42,36,36,24);
  add(textFieldxa);
 
  textFieldya.setText("100");
  textFieldya.reshape(88,36,36,24);
  add(textFieldya);

Creati si celelalte campuri de text, si anume textFieldxb, textFieldyb, textFieldxc, textFieldyc si textFieldIteratii

Butoane
Asa cum va asteptati, butoanele pot fi create in mod asemanator cu etichetele si campurile de text:
 Button buttonRedeseneaza = new Button();

  buttonRedeseneaza.setLabel("Redeseneaza");
  buttonRedeseneaza.reshape(356,36,96,24);
  add(buttonRedeseneaza);

Sa vedem acum partea cea mai interesanta, si anume: interactiunea cu utilizatorul: cum putem citi datele introduse in campurile de text?

Pentru a executa o actiune de gen apasarea unui buton, se apeleaza metoda action, astfel:
public boolean action(Event evt, Object arg);
Primul parametru, evt, este un obiect de tip Event, care desemneaza un eveniment primit de aplet, de exemplu poate fi vorba de un eveniment de la tastatura sau de la mouse. Campul target al unui obiect de tip Event indica tipul obiectului care a generat evenimentul. Pentru a determina acest obiect, folosim cuvantul cheie instanceof astfel:
if (evt.target instanceof Button). Daca acesta este adevarat, pentru a vedea exact care buton a fost apasat, de exemplu se scrie linia:
if (arg == "Redeseneaza") pentru a vedea daca a fost apasat butonul cu textul "Redeseneaza".
In exemplul prezentat nu avem mai multe butoane, deci nu e nevoie sa folosim aceasta din urma linie, dar o puteti exersa construind mai multe butoane si adaugand evenimente diferite pentru fiecare dintre ele.

Sa revenim la exemplul nostru. Dupa ce ati construit etichetele, campurile de text si un buton, sa citim datele introduse.
Pentru aceasta, mai declarati ca variabila a apletului variabila sir de tip String pentru a citi in ea datele introduse intr-un camp de text:
String sir;
Apoi adaugati urmatoarele doua metode apletului:

public boolean action(Event evt, Object arg) {
   if(evt.target instanceof Button) {
       sir=textFieldxa.getText();
       repaint();
       return true;
   }
   return false;
}

public void paint(Graphics g) {
    g.drawString(sir,10,100);
}
Functia action citeste sirul de caractere introdus in campul textFieldxa si il introduce in variabila sir declarata anterior. Apoi apeleaza functia repaint(). Aceasta este definita in clasa Component si executa metoda paint(Graphics g). Nu mai ramane deci decat sa apelam functia drawString (pe care am explicat-o luna trecuta) in corpul functiei paint.

Sa citim acum datele introduse in campurile corespunzatoare coordonatelor si sa desenam un triunghi.
Trebuie sa declaram mai intai 6 variabile pentru cele 6 coordonate:
int xa, ya, xb, yb, xc, yc;
In locul variabilei declarate anterior String sir; vom declara Integer temp; pentru a citi datele introduse. Metoda action va deveni:
public boolean action(Event evt, Object arg) {
    if(evt.target instanceof Button) {
        temp=Integer.valueOf(textFieldxa.getText()); 
        xa=temp.intValue();
        temp=Integer.valueOf(TextFieldya.getText());
        ya=temp.intValue();
        temp=Integer.valueOf(TextFieldxb.getText());
        xb=temp.intValue();
        temp=Integer.valueOf(TextFieldyb.getText());
        yb=temp.intValue();
        temp=Integer.valueOf(TextFieldxc.getText());
        xc=temp.intValue();
        temp=Integer.valueOf(TextFieldyc.getText());
        yc=temp.intValue();
        repaint();
        return true;
    }
    return false;
}
Am citit in temp valoarea introdusa in campul textFieldxa ca Integer, si apoi am trecut-o in variabila xa transformand-o mai intai in int. Transformand textul introdus intr-o valoare de tip int, realizam si o validare, in sensul ca daca introduceti in vreunul din campuri o alta valoare decat un numar intreg, ea va fi ignorata.

Functia paint devine:
public void paint(Graphics g) {
    g.drawLine(xa,ya,xb,yb);
    g.drawLine(xb,yb,xc,yc);
    g.drawLine(xc,yc,xa,ya);
}

Rulati tot ce am facut pana acum si veti vedea ca apasand butonul veti desena un triunghi, iar daca modificati valorile campurilor si apasati din nou butonul, triunghiul va avea alta forma.
Pentru ca triunghiul sa apara de prima data (pentru niste valori initiale ale coordonatelor), fara sa mai fie nevoie sa mai apasati o data butonul, trebuie sa copiati liniile de tipul:
   temp=Integer.valueOf(textFieldxa.getText()); 
   xa=temp.intValue();
astfel incat ele sa apara si la sfarsitul functiei init(). Initial valorile xa, ya, xb, yb, xc, yc au valoarea zero ( la declararea unei variabile, acesteia i se asigneaza valoarea 0 sau null) si de aceea nu apare nimic desenat.

In sfarsit, sa desenam figura care reprezinta Triunghiul lui Sierpinski. Pentru aceasta trebuie definita o functie recursiva care imparte triunghiul initial in 4 triunghiuri egale si care se autoapeleaza pentru toate triunghiurile, exceptand triunghiul din mijloc. Aceasta este o problema de programare si nu mi-am propus sa o explic in acest exemplu. Daca nu ati mai facut functii recursive sau nu sunteti obisnuiti cu programarea, luati-o ca atare si incercati sa o intelegeti. Nu este atat de grea precum pare.
Aceasta functie este data mai jos:

void Sierpinski(Graphics g,int xa,int ya,int xb,int yb,int xc,int yc,int iteratii) {
    int x1,y1,x2,y2,x3,y3; //coordonatele mijloacelor laturilor
    x1=(xa+xb)/2;
    y1=(ya+yb)/2;
    x2=(xb+xc)/2;
    y2=(yb+yc)/2;
    x3=(xa+xc)/2;
    y3=(ya+yc)/2;
    g.drawLine(x1,y1,x2,y2);
    g.drawLine(x1,y1,x3,y3);
    g.drawLine(x2,y2,x3,y3);
    int xpoly[]={x1,x2,x3};
    int ypoly[]={y1,y2,y3};
    g.fillPolygon(xpoly,ypoly,3); //umplerea triunghiurilor exterioare cu culoarea implicita
    if(iteratii>1) {
        Sierpinski(g,xa,ya,x1,y1,x3,y3,iteratii-1);
        Sierpinski(g,x1,y1,xb,yb,x2,y2,iteratii-1);
        Sierpinski(g,x3,y3,x2,y2,xc,yc,iteratii-1);
    }
}

Aceasta functie trebuie apelata din functia paint, astfel incat ea devine:
public void paint(Graphics g) {
    g.drawLine(xa,ya,xb,yb);
    g.drawLine(xb,yb,xc,yc);
    g.drawLine(xc,yc,xa,ya);
    Sierpinski(g,xa,ya,xb,yb,xc,yc,nr_iteratii);
}
unde nr_iteratii este o variabila de tip int a carei valoare o citim la fel ca si xa, ya etc.

Alte clase inrudite cu cele prezentate mai sunt: CheckBox, Choice, TextArea, Menu, MenuItem, MenuBar, ScrollBar etc. Unele sunt foarte mult asemanatoare cu cele din acest episod, altele au elemente specifice.  Voi prezenta detalii asupra lor atunci cand le vom folosi in alte exemple.

Pentru a ne cunoaste mai bine, pentru ca voi sa fiti stimulati sa invatati Java si pentru ca eu sa obtin un raspuns la ceea ce va explic, va propun ca de acum incolo sa realizati unul sau mai multe apleturi in care sa folositi ce ati invatat in fiecare episod (si eventual alte elemente, cu cat mai multe si mai interesante, cu atat mai bine) si sa le trimiteti la adresa mirela.andronescu@er.dntis.ro. Cele mai interesante apleturi vor fi apoi puse pe Internet la adresa revistei noastre http://er.dntis.ro.
De asemenea orice intrebari, neclaritati, propuneri, critici pe teme de Java sunt binevenite la aceeasi adresa de e-mail.
Va astept sa imi scrieti, Sarbatori Fericite, si sa ne auzim cu bine in anul 2000!
 
  1