Programare Java - 10
MANAGEMENTUL UNEI INTERFETE GRAFICE
Aspectul de pana acum al unei interfete grafice AWT era oarecum relativ - redimensionarea ferestrei putand modifica intreaga dispunere in fereastra . Aceasta maleabilitate a interfetei grafice apare in Java datorita posibilitatii sale de a rula pe diferite platforme care trateaza diferit elementele componente ale unei interfete grafice .
Pentru a introduce un control mai strict asupra aspectului in 454g62e terfetei noastre Java ne pune la dispozitie pentru pachetul AWT un numar de 5 administratori de dispunere .
DISPUNEREA COMPONENTELOR UNEI INTERFETE
Un administrator de dispunere determina modul in care vor fi aranjate componentele unui contatiner . Administratorul implicit al Java este clasa FlowLayout . Ea permite dispunerea secventiala a obiectelor , de la stanga la dreapta si de sus in jos .
Pachetul AWT poate folosi inca patru administratori : GridLayout , BorderLayout , CardLayout si GridBagLayout .
Pentru a crea un administrator pentru un container se creaza o instanta a clasei container folosind o instructiune de tipul :
FlowLayout administrator=new FlowLayout();
Acest administrator nou creat trebuie apoi declarat ca administrator pentru container cu ajutorul metodei setLayout() . Administratorul de dispunere trebuie stabilit inainte de a adauga componente containerului . Daca nu este stabilit un administrator explicit atunci apare automat cel de dispunere secventiala ( FlowLayout ) .
In exemplul de mai jos avem inceputul unui applet care foloseste un administrator de dispunere :
public class Inceput extends java.applet.Applet
}
ADMINISTRATORUL DE DISPUNERE SECVENTIALA ( FlowLayout )
Daca foloism constructorul FlowLayout() fara argumente toate componentele de pe fiecare rand vor fi automat centrate . Daca vrem ca alinierea sa se faca explicit trebuie sa utilizam ca argument pentru constructor una dintre variabilele de clasa FlowLayout.LEFT sau FlowLayout.RIGHT sau chiar FlowLayout.CENTER .
In mod automat administratorul de dispunere secventiala lasa un spatiu de trei pixeli intre componente pe un rand si tot cate trei pixeli intre randuri . Daca vrem sa modificam aceste caracteristici putem folosi un constructor de genul :
FlowLayout(int, int, int)
Acesta primeste ca argumente :
alinierea ( o variabila de clasa )
spatiul in pixeli dintre componente pe orizontala
spatiul in pixeli pe verticala intre linii
ADMINISTRATORUL DE DISPUNERE TABELARA ( GridLayout )
Acesta aranjeaza componentele intr-un tabel de randuri si coloane . Componentele sunt adaugate incepand cu celula aflata cel mai in stanga pe primul rand si continuand spre dreapta . In continuare se va trece la urmatoarea linie si tot asa mai departe .
Clasa care implementeaza acest administrator este GridLayout . Un constructor al acestei clase primeste doua argumente : numarul de linii si numarul de coloane al tabelului in care se va imparti containerul .
Exista si posibilitatea de a utiliza inca doua argumente care sa exprime spatiul pe orizontala si pe verticala intre componente . Spatiul implicit intre componentele administrate in acest mod este 0 pixeli pe ambele directii .
Trebuie facuta neaparat o observatie legata de acest administrator de dispunere : componentele vor ocupa totdeauna intregul spatiu al celulei in care trebuie sa se integreze .
In exemplul de mai jos avem un applet ce creaza un tabel cu 9 celule spatiate la 10 pixeli atat pe verticala cat si pe orizontala :
import java.awt.*;
public class Tabelare extends java.applet.Applet
}
ADMINISTRATORUL DE DISPUNERE MARGINALA ( BorderLayout )
Administratorul BorderLayout imparte un container in 5 zone : nord , sud , est , vest si centru . In cadrul acestei dispuneri componentele din cele patru directi de baza vor ocupa spatiul pe care il necesita iar centrul va primi restul de spatiu .
Exista doua versiuni ale constructorului clasei BorderLayout : fara nici un parametru sau cu doi parametri . Prima varianta creaza o dispunere fara spatiere intre componente in timp ce a doua varianta permite specificarea distantei in pixeli pe orizontala si pe verticala dintre componente .
Dupa crearea si aplicarea administratorului de acest tip adaugarea componentelor in container se face cu o metoda de genul :
add(string, componenta);
Primul argument are valori intre : "North" , "South" , "East" , "West" si "Center" . Al doilea argument este chiar componenta care trebuie adaugata .
Programul de mai jos implementeaza un administrator de dispunere marginala :
import java.awt.*;
public class Margini extends java.applet.Applet
}
COMBINAREA ADMINISTRATORILOR DE DISPUNERE
Pentru a realiza o interfata grafica practica si utila trebuie de fapt sa folosim mai multi administratori pentru aceeasi interfata . Acest lucru se poate face adaugand mai multe containere intr-un container principal , ca fereastra applet , fiecare avand un alt administrator de dispunere .
Aceste containere mai mici se numesc panouri si sunt derivate din clasa Panel . Panourile sunt folosite pentru a grupa diferite componente . Atunci cand lucram cu ele trebuie sa retinem cateva lucruri :
panoul se umple cu componente inainte de a fi introdus in containerul mai mare
panoul are propriul administrator de dispunere
Un panou este creat simplu :
Panel panou=new Panel();
Panoului i se atribuie o metoda de dispunere cu ajutorul lui setLayout() . Acesta functioneaza identic cu setLayout() pentru containere normale . Componentele sunt adaugate intr-un panou cu metoda add() a acestuia .
ADMINISTRATORUL DE DISPUNERE IN STIVA ( CardLayout )
Dipsunerea in stiva difera de celelalte deoarece ascunde practic unele componente . O astfel de dispunere inseamna de fapt un grup de containere sau componente afisate cate unul pe rand - fiecare container din grup fiind numit un card .
In mod normal acest administrator foloseste un panou pentru fiecare card . Mai intai se introduc componentele in panouri si apoi acestea se introduc in containerul de baza pentru care s-a stabilit o dispunere in stiva .
Clasa care implementeaza acest administrator este CardLayout .
Dupa ce am stabilit un container cu administrare a dispunerii in stiva trebuie sa adaugam cardurile sale cu ajutorul unei metode add() de forma :
add(string, container);
Primul argument al metodei este un sir care reprezinta numele cardului . Al doilea argument specifica containerul sau componenta care reprezinta cardul . Daca este vorba de un container acesta trebuie sa contina deja toate componentele necesare lui .
Dupa ce am adaugat cardul in containerul principal al programului putem folosi metoda show() a administratorului de dispunere in stiva pentru a afisa o anumita cartela . Metoda show() primeste doua argumente :
primul este containerul in care au fost adaugate cardurile ; daca respectivul container este chiar fereastra principala a appletului putem folosi cuvantul cheie this ca argument
numele cardului
In cele ce urmeaza se va prezenta o modalitate bazata pe fire de executie pentru a trece de la un card la altul :
import java.awt.*;
public class Carduri extends java.applet.Applet implements Runnable
}
public void stop()
public void init()
public void run() catch (InterruptedException e)
}
}
}
ADMINISTRATORUL DE DISPUNERE TABELARA NEPROPORTIONALA (GridBagLayout )
Acesta administrator reprezinta de fapt o extensie a dispunerii tabelare . Exista cateva diferente intre aceasta dispunere si cea tabelara standard :
o componenta poate ocupa mai multe celule ale tabelului
proportiile intre diferitele randuri sau coloane nu trebuie sa fie aceeasi
componentele dispuse in cadrul celulelor pot fi aranjate in diferite moduri
Pentru a crea o astfel de dispunere se folosesc clasa GridBagLayout si clasa GridBagConstraints . Prima clasa este chiar administratorul de dispunere iar cea de a doua este folosita pentru a defini constrngerile fiecarei componente ce va fi plasata intr-o celula - pozitia , dimensiunile , alinierea , etc .
In forma generala crearea unei dispuneri tabelare neproportionale presupune cativa pasi :
crearea unui obiect GridBagLayout si stabilirea sa ca administrator de dispunere curent
crearea unei noi instante a clasei GridBagConstraints
definirea restrictiilor pentru o componenta
anuntarea componentei si a restrictiilor sale in administratorul de dispunere
introducerea componentei in container
Pentru realizarea in practica a unei astfel de dispuneri sa luam un exemplu direct :
prima etapa : proiectarea tabelului
Aceasta parte se realizeaza pe hartie , unde vom schita interfata grafica . Fiecare componenta a interfetei trebuie plasata intr-o celula separata - desi o componenta poate ocupa mai multe celule .
Schita noastra sa presupunem ca este ceva de genul :
La pasul urmator ar trebui sa tabelam deja interfata noastra etichetand celulele cu coordonatele lor x , y :
0,0 1,0
0,1 1,1
0,2 1,2
etapa a doua : crearea tabelului
In aceasta etapa ne vom axa pe dispunere doar asa ca vom folosi doar niste butoane care sa tina locul elementelor reale ce vor apare pe panou .
Pentru usurinta specificarii restrictiilor vom defini o metoda care preia mai multe valori si stabileste restrictiile pentru aceastea . Metoda definireRestrictii() preia sapte argumente : un obiect GridBagConstraints si sase intregi care reprezinta variabilele gridx, gridy, gridwidth, gridheight, weightx si weighty .
Primele doua argumente reprezinta valorile coordonatelor celulei respective - in cazul in care o componenta ocupa mai multe celule coordonatele date sunt cele ale celului aflate cea mai in stanga-sus ca pozitie .
Urmatoarele doua argumente reprezinta numarul de celule pe care le ocupa componenta : gridwidth pentru coloane si gridheight pentru linii .
Ultimele doua argumente sunt date pentru latimea si inaltimea celulei - ca proportii din totalul pe orizontala respectiv verticala .
Pentru atribuirea restrictiilor unui obiect se foloseste metoda setConstraints() ; aceasta preia doua argumente : componente si restrictiile acesteia . Dupa ce toate acestea s-au realizat componenta poate fi adaugata in panou .
Crearea de fapt a dispunerii se face in metoda init() . Aici vom defini un administrator de dispunere GridBagLayout si se creaza un obiect pentru restrictii .
La finalul acestei etape trebuie sa verificam aspectul functional al tabelului , daca are numarul corect de linii si coloane , daca unele componente care ocupa mai multe celule sunt prezentate corect si daca nu apar alte anomalii vizibile .
etapa a treia : determinarea proportiilor
Acum se vor determina proportiile liniilor si coloanelor in relatiile cu alte linii si coloane . Cel mai usor este sa luam valorile weightx si weighty ca procente din latimea si inaltimea totale ale panoului . In acest caz valorile insumate pentru toate variabilele weightx si weighty trebuie sa fie 100 .
Celulele care ocupa mai multe linii sau coloane trebuie sa aiba totdeauna valoarea o in directia in care se extind . In afara de acest lucru trebuie sa ne hotaram doar asupra celulei care va primi o valoare ; toate celelalte de pe rand sau de pe coloana trebuie sa aiba valoarea 0 .
Scopul acestei etape este de a incerca sa stabilim proportiile de baza ale liniilor si celulelor care vor apare pe ecran . In general in aceasta etapa vor apare mai multe incercari pana la obtinerea rezultatului dorit .
etapa a patra : adaugarea si aranjarea componentelor
Aici mai apar restrictiile care sa aranjeze componentele in cadrul celulelor . Avem doua astfel de restrictii : fill si anchor .
Restrictia fill determina - pentru componentele care se pot extinde in orice directie - in ce directie se face aceasta extindere ( cum ar fi casetele de text sau butoanele ) . Restrictia fill poate avea una dintre urmatoarele patru valori , definite drept variabile de clasa in GridBagConstrints :
GridBagConstraints.BOTH - determina extinderea pentru a umple celula in ambele directii
GridBagConstraints.NONE - determina afisarea la dimensiunea mimina necesara componentei
GridBagConstraints.HORIZONTAL - determina extinderea pe orizontala
GridBagConstraints.VERTICAL - determina extinderea pe verticala
Implicit restrictia fill are valoarea NONE pentru toate componentele .
A doua restrictie care afecteaza aparitia unei componente intr-o celula este anchor . Aceasta se aplica doar componentelor care nu umplu intreaga celula si indica functiilor AWT unde sa plaseze componenta in cadrul celulei . Valorile posibile pentru aceasta restrictie sunt :
GridBagConstraints.NORTH
GridBagConstraints.NORTHEAST
GridBagConstraints.SOUTH
GridBagConstraints.SOUTHWEST
GridBagConstraints.EAST
GridBagConstraints.SOUTHEAST
GridBagConstraints.WEST
GridBagConstraints.NORTHWEST
GridBagConstraints.CENTER
Valoarea prestabilita pentru ancorare este GridBagConstraints.CENTER .
etapa a cincea : ajustari
In realitate aceasta etapa apare aproape totdeauna ; pentru ca totul sa arate perfect va trebui sa mai facem anumite modificari ale unor restrictii de obicei .
In listingul de mai jos avem un exemplu complet de creare a unui applet care implementeaza o interfata grafica similara celei prezentate in etapa intai a constructiei teoretice a unei interfete ce foloseste un administrator de dispunere tabelara neproportionala :
import java.awt.*;
public class NumeParola extends java.applet.Applet
public void init()
}
In afara restrictiilor discutate anterior mai trebuie amintite inca doua restrictii ipadx si ipady . Acestea doua controleaza bordurile - spatiul care inconjoara o componenta dintr-un panou . In mod prestabilit componentele nu au nici un spatiu liber in jurul lor . Restrictia ipadx adauga un spatiu in stanga si in dreapta componentei iar ipady adauga deasupra si dedesubtul componentei .
Pentru a determina spatiul lasat in jurul unui panou putem folosi insertiile ( insets ) . Clasa Insets contine valori pentru insertiile din partea de sus , de jos , din stanga si din dreapta , valori folosite la desenarea panoului .
Insertiile determina spatiul dintre marginile panoului si comonentele acestuia .
Pentru a modifica insertiile trebuie sa suprascriem metoda insets() - in Java 1.02 , sau metoda getInsets() din Java2 - ambele metode realizand acelasi lucru .
In cadrul metodei getInsets() ( sau insets() ) se creaza un nou obiect Insets unde constructorul clasei primeste ca argumente patru valori intregi reprezentand insertiile pentru partea de sus , din stanga , de jos si din dreapta panoului . Metoda va returna apoi obiectul Insets . In continuare avem o prezentare a codului necesar pentru adaugarea insertiilor intr-o dispunere tabelara : 10 pentru partea de sus si de jos si 30 pentru laterale :
public Insets getInsets()
INTERACTIUNEA UTILIZATORULUI CU UN APPLET
Pana acum am vazut cum se poate crea o interfata grafica utilizator - problema este ca aceasta interfata nu poate face deocamdata nimic ! In cele ce urmeaza vom vedea modul in care putem determina un applet sa trateze diferite evenimente folosind tehnici din Java 1.02 - limbajul cel mai raspandit inca pentru crearea appleturilor .
O interfata functionala trebuie sa fie capabila sa raspunda la evenimente - acestea reprezentand apeluri de metode pe care sistemul de ferestre Java le face ori de cate ori se interactioneaza cu un element al interfetei grafice create de utilizator .
Un eveniment este generat ca raspuns la aproape orice actiune pe care o poate intreprinde un utilizator . In programele noastre nu trebuie tratate toate evenimentele - ne vom ocupa de fapt doar de cele la care vrem sa reactioneze programul nostru ignorand restul . Intre evenimentele care pot fi tratate amintim : clicuri demouse , miscari ale mouseului , apasari de taste sau evenimente ale interfetei grafice utilizatoru ( clicuri pe diferite componente , derulari de liste , etc ) .
METODA handleEvent()
Tratarea evenimentelor in Java 1.02 se face prin metoda handleEvent() . Aceasta este definita in clasa Component care este mostenita de java.applet.Applet - devenind astfel disponibila appleturilor .
Cand un eveniment este transmis metodei hendleEvent() aceasta apeleaza o metoda specifica de tratare a evenimentului respectiv . Pentru a trata un anumit eveniment va trebui sa suprascriem aceste metode specifice . Apoi , la aparitia evenimentului respectiv metoda noua va fi apelata si executata .
TRATAREA CLICURILOR DE MOUSE
La executarea unui clid de mouse apar de fapt doua evenimente : mouse down , la apasarea butonului si mouse up la eliberarea butonului mouseului .
Semnatura metodei pentru evenimentul mouse down este :
public boolean mouseDown(Event evt, int x, int y)
Metoda - ca si mouseUp() de altfel - primeste trei argumente : evenimentul si coordonatele x si y unde a aparut evenimentul .
Argumentul evt este o instanta a clasei Event . Toate evenimentele genereaza o instanta a clasei Event care contine informatii privind locul si perioada cand a avut loc evenimentul , tipul sau si alte informatii .
De exemplu putem crea o metoda care sa afiseze coordonatele punctului unde s-a executat un clic de mouse :
public boolean mouseDown(Event evt, int x, int y)
Trebuie metionat ca aceasta metoda returneaza o valoare booleana , spre deosebire de majoritatea metodelor folosite pana acum . Valoarea de retur true sau false a unei metode de tratare de evenimente determina daca o anumita componenta poate intercepta evenimentul sau daca trebuie sa il transmita mai departe altor componente . Regula este ca daca metoda intercepteaza si trateaza evenimentul ea trebuie sa returneze true ; daca metoda ignora evenimentul ea trebuie sa returneze false pentru ca celelalte componente ale interfetei sa aiba sansa de a trata ele evenimentul .
In listingul de mai jos vom vedea un applet care va trata apasarea mouseului - la fiecare clic de mouse in fereastra este desenata o pata , pana la totalul de maxim 15 pete :
import java.awt.*;
public class Pete extends java.applet.Applet
public boolean mouseDown(Event evt, int x, int y)
else
}
void adaugaPata(int x, int y)
public void paint(Graphics ecran)
}
}
Clasa Event din Java poseda o variabila denumita clickCount care pastreaza aceasta informatie . clickCount este o variabila intreaga care contine numarul de clicuri consecutive aparute - consecutivitatea este determinata de sistemul de operare sau de hardwareul mouseului . In cazul in care vrem sa tratam ca evenimente clicuri multiple trebuie sa testam valoarea variabilei sus amintite in corpul metodei noastre :
public boolean mouseDown(Event evt, int x, int y)
}
Metoda mouseDown() este de fapt apelata pentru fiecare clic in parte .
Ca sa fie mai clar sa luam exemplul :
public boolean mouseDown(Event evt, int x, int y)
Daca rulam un program care implementeaza aceasta metoda si executam un triplu clic rezultatul obtinut va fi :
Numar de clicuri : 1
Numar de clicuri : 2
Numar de clicuri : 3
AWT defineste doua tipuri distincte de miscari ale mouseului : tragerea sa - miscarea se face cu butonul semnificativ apasat , si miscarea simpla - cu butonul neapasat . Evenimentele de mai sus sunt interceptate si tratate cu ajutorul metodelor mouseDrag() si mouseMove() .
Metoda mouseMove() seamana mult cu metodele care tratau clicurile de mouse :
public boolean mouseMove(Event evt, int x, int y)
Meotda mouseDrag() are o semnatura similara si mai trebuie sa mentionam ca in ambele metode argumentele pentru coordonatele x si y reprezinta noua pozitie a mouseului si nu cea initiala .
Metodele mouseEnter() si mouseExit() sunt apelate cand mouseul patrunde sau paraseste un applet sau o portiune a acestuia . Ambele metode au semnaturi similare cu cele intalnite mai inainte , coordonatele x si y referindu-se aici la puctul unde mouseul a patruns sau respectiv a parasit appletul :
public boolean mouseEnter(Event evt, int x, int y)
Pentru a utiliza aceste evenimente si a le trata vom prezenta un exemplu de applet care traseaza linii drepte pe ecran prin tragerea mouseului dintr-un punct in altul . Numarul de linii va fi restrictionat la 20 :
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Point;
public class Linii extends java.applet.Applet
public boolean mouseDown(Event evt , int x , int y)
else
}
public boolean mouseUp (Event evt , int x , int y)
else return false;
}
public boolean mouseDrag (Event evt , int x , int y)
else return false;
}
void adaugareLinie(int x,int y)
public void paint(Graphics g)
g.setColor(Color.red);
if (pctCurent!=null)
g.drawLine(pctInitial.x , pctInitial.y , pctCurent.x , pctCurent.y);
}
}
Pentru a ajuta la intelegerea programului vom schita pe scurt si un comentariu al diferitelor elemente ce apar in cadrul sursei de mai sus .
Programul nostru memoreaz nu valori individuale ale coordonatelor ci obiecte Point . Tabloul inceput memoreaza punctele care reprezinta punctele de inceput ale liniilor deja desenate . Tabloul final memoreaza punctele de final ale liniilor deja desenate . Variabila pctInitial memoreaza punctul de start al liniei pe care o desenam la momentul curent iar pctCurent retine punctul final al liniei desenate curent . Variabila linieCrt memoreaza numarul de linii desenate pentru a nu se depasi valoare MAXLINII si pentru a se cunoaste linia din tablou care se va accesa in continuare .
Evenimentele tratate in applet sunt trei : mouseDown() pentru a stabili punctul de start al liniei curente , mouseDrag() pentru a desena linia curenta pe masura ce este desenata si mouseUp() pentru a se stabili punctul final al liniei desenate .
Pe masura ce mouseul este miscat pentru a trasa o noua linie appletul o deseneaza pe aceasta astfel incat noua linie se va deplasa de la punctul de pornire si pana la punctul mobil reprezentat de mouse ; evenimentul mouseDrag() memoreaza punctul curent ori de cate ori mouseul se deplaseaza asa ca aceasta metoda poate fi folosita pentru a reimprospata afisarea liniei curente dand astfel senzatia de animatie a acestei liniii .
Noua linie desenata se va adauga in tabloul de liniii doar dupa eliberarea mouseului - la interventia metodei mouseUp() care apeleaza metoda adaugareLinie() .
Aceasta metoda adaugareLinie() actualizeaza tabloul de linii - de fapt tablourile de puncte de inceput si de sfarsit ale liniilor dupa care appletul este redesenat pentru a tine cont de aparitia ultimei linii desenate . Tot in cadrul acestei metode se seteaza variabilele pctCurent si pctInitial la null deoarece linia desenata curent a fost finalizata in momentul apelarii acestei metode . Prin setarea aceasta putem apoi testa aceasta valoare null in metoda paint() pentru a vedea daca trebuie trasata o nou linie .
Desenarea appletului presupune desenarea tuturor liniilor stocate in tablourile programului precum si desenarea liniei curente ale carei capete sunt in pozitiile pctInitial si respectiv pctCurent .
Dupa cum am metionat putin mai sus in cadrul metodei paint() intai testam daca valoarea lui pctCurent este null . Daca da , atunci appletul nu se afla in timpul desenarii unei linii deci nu va avea nici un motiv sa traseze o linie care nu exista . Prin testarea variabilei pctCurent - si prin posibilitatea setarii ei la null in metoda adaugareLinie() - putem desena doar ceea ce este necesar .
|