CATEGORII DOCUMENTE |
Componente Swing cu model
Arhitectura MVC
Un obiect vizual folosita intr-o interfata grafica indeplineste mai multe functii:
- Prezinta pe ecran date intr-o imagine specifica (controlabila prin program).
- Preia actiunile transmise prin mouse sau prin tastatura obiectului respectiv.
- Permite modificarea datelor ca raspuns la actiuni ale operatorului uman sau la cereri exprimate prin program si actualizeaza imaginea de pe ecran a acestor date.
Arhitectura MVC (Model-View-Controller) separa in cadrul unei componente vizuale cele trei functii esentiale ale unui program sau ale unui fragment de program: intrari (controller), prelucrari (model), iesiri (View).
Partea numita "model" reprezinta datele si functionalitatea componentei, deci defineste starea si logica componentei. Imaginea ("view") reda intr-o forma vizuala modelul, iar partea de comanda ("controller") interpreteaza gesturile utilizatorului si actioneaza asupra modelului (defineste "comportarea" componentei). Poate exista si o legatura directa intre partea de control si partea de redare, in afara legaturilor dintre model si celelalte doua parti (imagine si comanda).
Comunicarea dintre cele trei parti ale modelului MVC se face fie prin evenimente, fie prin apeluri de metode. Modelul semnaleaza partii de prezentare orice modificare in starea sa, prin evenimente, iar partea de imagine poate interoga modelul, prin apeluri de metode. Partea de comanda este notificata prin evenimente de actiunile ("gesturile") operatorului uman si modifica starea modelului prin apeluri de metode ale obiectului cu rol de "model"; in plus poate apela direct si metode ale obiectului de redare (pentru modificarea imaginii afisate).
In figura urmatoare linia continua reprezinta un apel de metoda iar linia intrerupta reprezinta un eveniment transmis intre obiecte.
Model
View Controller
De exemplu, un buton simplu este modelat printr-o variabila booleana cu doua stari (apasat sau ridicat), este comandat printr-un clic de mouse (si, eventual, printr-o tasta) si are pe ecran imaginea unei ferestre dreptunghiulare (rotunde), cu sau fara contur, cu text sau cu imagine afisata pe buton. Imaginea butonului reflecta de obicei starea sa, prin crearea impresiei de buton in pozitie ridicata sau coborata. Este posibil ca imaginea butonului sa fie modificata direct de actiunea operatorului (de controler) si nu indirect prin intermediul modelului. Legatura de la imagine la controler consta in aceea ca trebuie mai intai pozitionat mouse-ul pe suprafata butonului si apoi apasat butonul de la mouse.
Clasele JFC folosesc o varianta a modelului MVC cu numai doi participanti: o clasa model si o clasa "delegat" care reuneste functiile de redare si de control pentru a usura sarcina proiectantului, deoarece comunicarea dintre controler si imagine poate fi destul de complexa.
Componentele JList, JTable, JTree, JMenuBar includ intotdeauna un obiect model care poate fi extras printr-o metoda "getModel" pentru a se opera asupra lui. Obiectul model poate fi creat automat in constructor, pe baza altor date, sau poate fi creat de programator si transmis clasei care asigura prezentarea datelor din model. Exista clase predefinite pentru obiecte model (DefaultListModel, DefaultTreeModel) dupa cum se pot defini si alte clase model care sa respecte interfete impuse.
Un model este o structura de date care poate genera evenimente la modificarea datelor si contine metode pentru adaugarea si eliminarea de "ascultatori" la evenimentele generate de model. Clasele JList si JTable sunt ascultatori la evenimente generate de clasele model respective, deci modificarea datelor din model va modifica automat si afisarea pe ecran (se vor afisa noile date din obiectul model).
Datele prezentate intr-un obiect JList, JTable, JTree se pot modifica in cursul executiei programului, iar afisarea trebuie sa reflecte aceste modificari. De exemplu, se afiseaza date din directoare sau din fisiere al caror nume se introduce sau se modifica de catre operator dupa afisarea interfetei grafice (prin introducere in campuri text, de exemplu).
Ca tehnica generala, nu se construiesc alte obiecte vizuale (JList, JTable, JTree) cu noile date dupa afisarea interfetei grafice si nici nu se apeleaza metode de reafisare ("repaint" sau altele), ci se apeleaza metode care transmit la obiectul vizual un alt model ("setModel"), sau se apeleaza metode de modificare a obiectului model.
Modificarile asupra obiectului model sunt redate automat pe ecran deoarece obiectul vizual este ascultator la evenimente generate de model, dar nu toate obiectele model au metode de modificare a datelor (interfetele claselor model nu impun astfel de metode).
O alta posibilitate este sa se creeze un nou model (de exemplu dupa reordonarea unui vector sau unui tabel) si sa se retransmita noul model la acelasi obiect vizual din interfata grafica.
Deoarece clasele JList,JTable si JTree au fost proiectate de echipe diferite exista diferente de conceptie intre aceste clase in ceea ce priveste metodele si constructorii clasei model si metodele clasei vizuale legate de model. De exemplu, nu exista o clasa abstracta model de arbore, dar exista clase abstracte model de lista si de tabel, care implementeaza interfetele ce trebuie respectate de orice obiect model.
Utilizarea unui model de lista
Componenta vizuala JList afiseaza pe ecran o lista de valori, sub forma unei coloane, si permite selectarea uneia dintre valorile afisate, fie prin mouse, fie prin tastele cu sageti. Datele afisate pot fi de orice tip clasa si sunt memorate intr-un obiect colectie (Vector) sau model; o referinta la acest obiect este memorata in obiectul JList de constructorul obiectului JList. Exista mai multi constructori, cu parametri de tipuri diferite: vector intrinsec, obiect Vector sau obiect ListModel. Exemple:
String v =;
JList list1 = new JList (v); // vector intrinsec
Vector vec = new Vector ( Arrays.asList(v));
JList list2 = new JList(vec); // obiect de tip Vector
JList list3 = new JList (new DefaultListModel());
Clasa model de lista predefinita DefaultListModel are un singur constructor, fara argumente, dar are metode de adaugare si de modificare a obiectelor din vectorul folosit de obiectul model.
Un obiect JList cu continut variabil se poate folosi si fara obiect model creat explicit, prin retransmiterea unui vector cu date la obiectul JList (metoda "setListData"). Exemplu de afisare intr-un obiect JList a numelor fisierelor dintr-un director al carui nume se introduce (sau se modifica) intr-un camp text:
class FileList extends JFrame implements ActionListener
public void actionPerformed (ActionEvent ev)
}
Modificarea datelor din vector (vector intrinsec sau obiect Vector) nu are efect asupra datelor afisate in obiectul JList decat daca se apeleaza la fiecare modificare si metoda "setListData", care apeleaza metoda "setModel" din JList.
Modificarea datelor din model (prin metode ca "addElement", "setElementAt", "removeElementAt") se reflecta automat pe ecran, deoarece obiectul JList este ascultator la evenimentele generate de model. Exemplu:
class FileList extends JFrame implements ActionListener
public void actionPerformed (ActionEvent ev)
}
Putem sa nu folosim obiect model separat atunci cand lista este folosita doar pentru selectia unor elemente din lista iar continutul listei afisate nu se mai modifica. Trebuie observat ca orice constructor creeaza un obiect model simplu, daca nu primeste unul ca argument, iar metoda "getModel" poate fi apelata indiferent cum a fost creata lista. Modelul creat implicit are numai metode de acces la date ("getElementAt", "getSize") si nu permite modificarea datelor din acest model (date transmise printr-un vector).
De obicei lista este afisata intr-un panou cu derulare (JScrollPanel) pentru a permite aducerea pe ecran a elementelor unei liste oricat de mari (care nu este limitata la numarul de linii din panoul de afisare). Ca aspect, o lista JList seamana cu o zona text JTextArea, dar permite selectarea unuia sau mai multor elemente din lista. Un obiect JList genereaza evenimente cauzate de selectarea unui element din lista si evenimente cauzate de modificarea continutului listei.
Selectarea unui element dintr-o lista produce un eveniment ListSelectionEvent, tratat de metoda "valueChanged" (din interfata ListSelectionListener). Putem obtine fie obiectul selectat (metoda "getSelectedValue"), fie pozitia in vector (sau in model) a elementului selectat (metoda "getSelectedIndex").
Exemplul urmator arata cum se poate programa o lista de selectie; elementul selectat este afisat intr-un camp text, pentru verificarea selectiei corecte.
class ListSel extends JFrame implements ListSelectionListener
public void valueChanged(ListSelectionEvent e)
public static void main(String s[ ]) ; // date afisate in lista
ListSel ls = new ListSel(a);
}
}
Programul anterior nu permite modificarea continutului listei de selectie deoarece interfata ListModel nu prevede metode de modificare a modelului.
Exemplul urmator arata cum se pot adauga elemente la sfarsitul unei liste de selectie, cu un buton pentru comanda de adaugare ("Add") si un camp text unde se introduce elementul ce va fi adaugat la lista.
class ListAdd extends JFrame implements ActionListener
// receptor la buton si la campul text
public void actionPerformed(ActionEvent e)
listModel.addElement(toAdd.getText()); // adauga element la model lista
toAdd.setText (''); // sterge camp text
}
}
}
Exemplul anterior poate fi extins cu un buton de stergere element selectat din lista si o clasa care implementeaza interfata ListSelectionListener, cu o metoda "valueChanged" ; un obiect al acestei clase se va inregistra ca ascultator la lista.
Modificarea vectorului din modelul de lista genereaza evenimente pentru obiecte inregistrate ca ascultatori la lista: metoda "addElement" din clasa DefaultListModel apeleaza metoda "fireIntervalAdded", metoda "removeElement" apeleaza metoda "fireIntervalRemoved" si "setElementAt" apeleaza metoda "fireContentsChanged". Lantul de apeluri care produce activarea unei metode scrise de programator ca urmare a unei modificari a colectiei de date din model este, in cazul listei, urmatorul:
DefaultListModel.addElement AbstractModel.fireIntervalAdded
(ListDataEvent) ListDataListener.intervalAdded
Un observator la modificari ale listei trebuie sa implementeaza interfata ListDataListener, deci sa defineasca metodele "intervalAdded", "intervalRemoved" si "contentsChanged", toate cu argument ListDataEvent, care contine informatii despre sursa, tipul evenimentului si pozitia din vector unde s-a produs modificarea. Putem defini o clasa adaptor cu implementari nule pentru toate cele trei metode :
class ListDataAdapter implements ListDataListener
public void intervalAdded (ListDataEvent ev)
public void contentsChanged (ListDataEvent ev)
}
Exemplul urmator arata cum putem reactiona la stergerea unui element din lista prin afisarea dimensiunii listei dupa stergere:
class LFrame extends JFrame implements ActionListener, ListSelectionListener
// ascultator la butonul de stergere
public void actionPerformed (ActionEvent ev)
// ascultator la selectie din lista
public void valueChanged(ListSelectionEvent e)
// ascultator la stergere din lista
class LDL extends ListDataAdapter
}
Utilizarea unui model de tabel
Componenta vizuala JTable afiseaza pe ecran un tabel de celule structurat in linii si coloane, permitand selectarea si editarea de celule, modificarea aspectului tabelului si obtinerea de informatii despre celulele selectate prin "mouse". Fiecare coloana poate avea un titlu. Datele din celule pot fi de tipuri diferite, dar numai tipuri clasa. Dimensiunile coloanelor sunt implicit egale si calculate in functie de numarul coloanelor, dar pot fi modificate fie prin program (metoda "setPreferredWidth"), fie prin deplasarea ("tragerea"="dragging") marginilor coloanelor folosind un mouse.
Construirea unui obiect JTable se poate face folosind un vector de titluri si o matrice de celule (de obiecte Object), sau folosind un obiect model TableModel :
JTable (Object[][] date, Object[] numecol); // cu vectori intrinseci
JTable (Vector date, Vector numecol); // cu obiecte Vector
JTable (TableModel model); // cu obiect model
Clasa DefaultTableModel are mai multi constructori cu argumente (inclusiv cu matrice de celule si vector de titluri) si are metode pentru retransmiterea datelor la model (setDataVector) si pentru modificarea directa a valorii din oricare celula din tabel: setValueAt(Object value,int row,int col).
Secventa urmatoare ilustreaza folosirea unor constructori:
// tabelul primeste direct datele
String [ ] titles = ; // nume de coloane
Object[ ][ ] cells = , , };
JTable table1 = new JTable (cells,titles);
// tabelul primeste un model al datelor
DefaultTableModel model = new DefaultTableModel (cells,titles);
JTable table2 = new JTabel (model);
Un obiect JTable include un obiect model, creat automat (implicit) sau primit de un constructor; modelul de date creat implicit este public accesibil si permite modificarea datelor din model prin metoda "setValueAt". Clasa JTable are si ea metodele "getValueAt" si "setValueAt", care apeleaza metodele cu acelasi nume ale obiectului model continut de obiectul tabel.
Crearea unui model explicit este necesara in cazuri mai speciale: cand nu vrem ca toate celulele sa fie editabile, cand vrem ca datele din celule sa fie prezentate prin imagini sau altfel decat prin metoda "toString" (folosita implicit la afisarea datelor).
In exemplele urmatoare se afiseaza intr-un obiect JTable atributele fisierelor dintr-un director al carui nume este introdus intr-un camp text (pe 4 coloane). Prima solutie modifica datele din modelul creat implicit si extras cu "getModel":
class FileTable extends JFrame implements ActionListener ; // titluri de coloane
Object[][] cells= new Object[100][4] ; // numarul de linii depinde de nr de fisiere !
public FileTable()
public void actionPerformed (ActionEvent ev)
}
}
In varianta urmatoare se creeaza un nou model de tabel pentru fiecare nou director si se transmite acest model la acelasi obiect JTable, creat initial:
class FileTable extends JFrame implements ActionListener ; // titluri de coloane
DefaultTableModel model= new DefaultTableModel() ; // un obiect model
public FileTable()
public void actionPerformed (ActionEvent ev)
model = new DefaultTableModel(a,titles); // crearea unui nou model
jtable.setModel(model); // transmis la ob. JTable
}
}
Obiectul model separat transmis la crearea unui obiect JTable poate fi:
- din clasa DefaultTableModel;
- dintr-o subclasa a clasei DefaultTableModel sau AbstractTableModel;
- dintr-o clasa noua care sa implementeze interfata TableModel.
Exemplele urmatoare arata cum se pot defini alte clase model de tabel pentru un tabel cu celule nemodificabile de catre operator (dar modificabile prin program):
// tabel nemodificabil prin extindere DefaultTableModel
class MyTableModel extends DefaultTableModel
public boolean isCellEditable (int row,int col)
}
// tabel nemodificabil prin extindere AbstractTableModel
class MyTableModel extends AbstractTableModel
public int getColumnCount() // numar de coloane
public int getRowCount() // numar de linii
public Object getValueAt (int row,int col)
public String getColumnName(int col)
}
Pentru ambele variante crearea tabelului este identica :
JTable table = new JTable(new MyTableModel(cells,titles));
Pentru operatii cu o coloana dintr-un tabel este prevazut modelul unei coloane: interfata ColumnModel si modelul implicit DefaultColumnModel.
Un tabel poate genera mai multe tipuri de evenimente :
- Evenimente produse la selectarea unei linii, unei coloane sau unei celule.
- Evenimente produse de modificarea datelor din tabel
- Evenimente produse de modificarea structurii tabelului
In mod implicit este permisa numai selectarea de linii dintr-un tabel, dar exista metode pentru activarea selectiei de coloane sau de celule individuale. In exemplul urmator se permite selectarea unei celule (prin "mouse" sau prin tasta Tab) si se afiseaza intr-un camp text coordonatele (linie-coloana) celulei selectate.
class TableSel extends JFrame implements ListSelectionListener
// actiune la selectie celula
public void valueChanged(ListSelectionEvent e)
}
Utilizarea unui model de arbore
Clasa JTree permite afisarea unor structuri arborescente (ierarhice), cu posibilitatea de a extinde orice nod intern prin subarborele sau, de a comprima un subarbore la radacina sa si de a selecta orice nod din arbore prin "mouse". O aplicatie poate trata evenimentele de selectare a unor noduri sau de modificare structura arbore sau de extindere -contractie noduri corespunzator scopului aplicatiei.
Un obiect JTree se poate construi in mai multe feluri:
- Pe baza unui vector de vectori (Object[] sau Vector);
- Pe baza unui obiect nod de arbore (radacina), subtip al interfetei TreeNode;
- Pe baza unui model de arbore, subtip al interfetei TreeModel.
Exista si un constructor JTree fara argumente, care afiseaza un arbore cu date constante fara legatura cu aplicatia (colors, sports, food) dar de obicei putem evita aceasta situatie; pentru un arbore de fisiere putem alege un director initial arbitrar (directorul curent, de exemplu).
Modificarea datelor dintr-un obiect JTree, dupa afisarea acestuia, se poate face numai prin modificarea totala (crearea altui obiect model) sau partiala a modelului folosit de obiectul JTree; din acest motiv este important sa stim cum se creeaza si cum se modifica modelul datelor dintr-un arbore.
Modelul de arbore poate fi creat explicit de utilizator inainte de a construi obiectul JTree sau este creat automat in interiorul clasei JTree pe baza unei alte colectii de date furnizate de utilizator (vector intrinsec, obiect Vector, etc.).
Modelul de arbore este fie un obiect din clasa DefaultTreeModel, fie obiect al unei clase definite de utilizator, dar care implementeaza interfata TreeModel. Acest model genereaza numai evenimente de modificarea datelor; pentru a trata evenimente legate de selectia unor noduri este folosit un model TreeSelectionModel.
Clasa model de arbore foloseste un obiect nod, ca radacina a arborelui; acesta poate fi un obiect din clasa DefaultMutableTreeNode sau obiecte din clase definite de utilizatori care implementeaza interfata TreeNode (noduri nemodificabile) sau interfata MutableTreeNode (cu operatii de modificare). Exemple de metode din interfata TreeNode:
int getChildCount(); // numar de succesori ai acestui nod
TreeNode getChildAt(int childIndex); // succesor cu indice cunoscut
int getIndex(TreeNode node); // indicele unui succesor dat
TreeNode getParent(); // nodul parinte al acestui nod
boolean isLeaf(); // daca acest nod este o frunza
Enumeration children(); // enumerator pentru succesorii acestui nod
Exemple de metode din interfata MutableTreeNode:
void remove(int index); // elimina succesor cu indice dat
void remove(MutableTreeNode node); // elimina succesor dat ca nod
void setUserObject(Object object); // modifica datele din acest nod
void removeFromParent(); // elimina acest nod
void setParent(MutableTreeNode newParent); // modifica parintele acestui nod
Clasa DefaultMutableTreeNode implementeaza interfata MutableTreeNode si foloseste cate un vector de succesori la fiecare nod (obiect de tip Vector), plus o legatura catre nodul parinte. Exemple de metode in plus fata de interfete:
public Object getUserObject (); // obtine date din acest nod (orice obiect)
public void add(MutableTreeNode newChild) ; // adauga acestui nod un fiu
public Enumeration preorderEnumeration() ; // enumerare noduri din subarbore
public Enumeration postorderEnumeration() ; // enumerare noduri din subarbore
public Enumeration breadthFirstEnumeration() ; // enumerare noduri din subarbore
Pentru afisarea in mod grafic a unui arbore de fisiere, o functie "main" minimala poate arata astfel:
class FileJTree extends JFrame
}
Modificarea dinamica a continutului unui obiect JTree se face fie prin crearea unui nou model de arbore si transmiterea lui la obiectul JTree deja afisat, fie prin crearea unui nou arbore (cu o alta radacina) si transmiterea adresei noii radacini la modelul de arbore existent. Teoretic, se poate modifica direct modelul, prin metode ale clasei DefaultTreeModel, dar este mai simplu de lucrat cu metode ale clasei nod de arbore. Pentru a ilustra aceste posibilitati de modificare dinamica a unui JTree vom relua problema afisarii continutului oricarui director (si a subdirectoarelor sale) intr-un obiect JTree (numele directorului este preluat dintr-un camp text).
class DirTree extends JFrame implements ActionListener
// constructor
public DirTree ()
. . . // functiile dirtree si main
Urmeaza varianta cu modificarea radacinii de arbore folosite de modelul existent:
class DirTree extends JFrame
}
Pentru ambele programe se afiseaza initial continutul directorului curent (atunci se creeaza modelul de arbore) deoarece constructorul fara argumente JTree afiseaza niste valori constante (care nu reprezinta nume de fisiere).
La selectarea unui nod din arbore se genereaza evenimentul TreeSelectionEvent si se apeleaza metoda "valueChanged" din ascultatorii de tip TreeSelectionListener care au fost inregistrati la obiectul JTree. Exemplu de afisare valoare nod selectat:
public void valueChanged (TreeSelectionEvent ev) catch(Exception ex)
}
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1328
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved