This is an old revision of the document!


Breviar 10

Interfețe grafice - Componente avansate

1. Liste de selecție

1.1 Introducere

Un obiect de tip JList prezintă utilizatorului un grup de item-uri, afișate în una sau mai multe coloane, pentru a oferi acestuia posibilitatea de a alege.

1.2 Crearea unui model

Există trei metode prin care se poate crea un model de listă:

  • DefaultListModel - aproape totul este gestionat de model pentru a ușura munca programatorului;
  • AbstractListModel - utilizatorul gestionează datele și invocă metodele de acționare. Pentru această abordare, programatorul trebuie să subclaseze AbstractListModel și să implementeze metodele getSize() și getElementAt() moștenite din interfața ListModel;
  • ListModel - utilizatorul gestionează totul.
1.3 Inițializarea unei liste

Iată un exemplu de cod care creează și setează parametrii unei liste:

list = new JList(data); // data e de tipul Object[]
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
// ...
JScrollPane listScroller = new JScrollPane(list);
listScroller.setPreferredSize(new Dimension(250, 80));

Alți constructori pentru JList permit inițializarea listei dintr-un obiect de tip Vector sau dintr-un obiect de tipul ListModel. Dacă inițializarea se face dintr-un obiect de tip Vector sau dintr-un vector clasic intrinsec, constructorul implicit creează un model standard de listă.

Modelul standard este imutabil - nu se pot adăuga, șterge, înlocui intrări în listă. Pentru a crea o listă a cărei intrări pot fi modificate individual, se poate seta modelul listei către o instanță variabilă, cum ar fi DefaultListModel. Modelul unei liste se poate seta când se creează lista sau prin apelul metodei setModel().

Apelul setSelectionMode() specifică câte intrări din listă poate selecta utilizatorul și dacă acestea trebuie să fie sau nu continue.

Apelul setLayoutOrientation() permite afișarea datelor în mai multe coloane. Valoarea JList.HORIZONTAL_WRAP specifică faptul că lista ar trebui să își afișeze intrările de la stânga la dreapta înainte de a trece la o nouă linie. O altă valoare posibilă este JList.VERTICAL_WRAP, care specifică afișarea datelor de sus până jos (ca de obicei) înainte de a trece la o coloană nouă.

În combinație cu apelul setLayoutOrientation(), invocarea metodei setVisibleRowCount(-1) oferă listei posibilitatea de afișare a numărului maxim de intrări cuprins în spațiul disponibil pe ecran. O altă utilizare pentru setVisibleRowCount() este aceea de a indica panoului de scroll-ing asociat listei câte rânduri să afișeze.

1.4 Selectarea intrărilor dintr-o listă

O listă utilizează o instanță de ListSelectionModel pentru a-și gestiona selecția. Implicit, modelul de selecție a unei liste permite selecția oricărei combinații de intrări la un moment dat. Se poate specifica un mod diferit de selecție prin apelul metodei setSelectionMode():

  • SINGLE_SELECTION - numai o singură intrare poate fi selectată la un moment dat. Când utilizatorul selectează o intrare, orice intrare anterior aleasă este deselectată;
  • SINGLE_INTERVAL_SELECTION - intrări multiple și continue pot fi selectate. Când utilizatorul începe un nou interval de selecție, orice intrări anterior alese sunt deselectate;
  • MULTIPLE_INTERVAL_SELECTION - mod implicit.

Indiferent de modelul de selecție folosit de listă, aceasta generează evenimente de selecție de fiecare dată când selecția se schimbă. Aceste evenimente pot fi procesate prin adăugarea unui ascultător la listă folosind metoda addListSelectionListener(). Acest ascultător trebuie să implementeze o metodă: valueChanged().

public void valueChanged(ListSelectionEvent e) {
    if (e.getValueIsAdjusting() == false) {
        if (list.getSelectedIndex() == -1) {
            fireButton.setEnabled(false);
        } else {
            fireButton.setEnabled(true);
        }
    }
}

Multe evenimente de selecție pot fi generate de o singură acțiune a utilizatorului, cum ar fi un click. Metoda getValuesAdjusting() returnează true dacă utilizatorul încă manipulează selecția. Programul de mai sus este interesat doar de rezultatul final al acțiunii utilizatorului și, de aceea, metoda valueChanged() conține secvența de cod doar dacă getValuesAdjusting() întoarce false.

Deoarece lista este în modul SINGLE_SELECTION, acest cod poate utiliza metoda getSelectedIndex() pentru a obține indexul intrării tocmai selectate. JList pune la dispoziție și alte metode pentru a seta sau a obține selecția când se pot alege mai multe intrări. De asemenea, se pot asculta evenimente direct pe modelul de selecție, dacă acest lucru este dorit.

1.5 Adăugarea și ștergerea
listModel = new DefaultListModel();
listModel.addElement("Debbie Scott");
listModel.addElement("Scott Hommel");
listModel.addElement("Alan Sommerer");
list = new JList(listModel);

Programul de mai sus utilizează o instanță DefaultListModel. În ciuda numelui, lista nu deține un DefaultListModel decât dacă acest lucru este setat explicit din program.

Dacă DefaultListModel nu este potrivit nevoilor programatorului, se poate scrie un model customizat dar care trebuie să adere la interfața ListModel.

Altă metodă de adăugare, cu specificare exactă a poziției de inserare, este cea care uzitează metoda insertElementAt. Ștergerea este, de asemenea, foarte simplă, deoarece există metoda remove.

listModel.insertElementAt(employeeName.getText(), index);
listModel.remove(index);
1.6 Formatarea celulelor

O listă folosește un obiect numit Cell Renderer pentru a-și afișa intrările. Cel implicit știe să afișeze șiruri de caractere și imagini și afișează tipul Object invocând metoda toString().

Dacă se dorește schimbarea modului în care se face afișarea sau dacă se dorește un comportament diferit față de cel oferit de toString(), se poate implementa un Cell Renderer customizat.

Pașii ce trebuie urmați sunt:

  • scrierea unei clase care implementează interfața ListCellRenderer;
  • crearea unei instanțe de clasă și apelarea metodei setCellRenderer() pentru listă, folosind instanța ca argument.

2. Tabele

2.1 Introducere

Clasa JTable este folosită pentru a afișa și edita tabele de celule în două dimensiuni.

JTable deține numeroase facilități care permit customizarea după preferințe dar în același timp oferă și opțiuni standard pentru aceste facilități astfel încât tabele simple pot fi create foarte rapid și ușor.

De exemplu, un tabel cu 10 linii și 10 coloane se poate obține astfel:

TableModel dataModel = new AbstractTableModel() {
    public int getColumnCount() { return 10; }
    public int getRowCount() { return 10; }
    public Object getValueAt(int row, int col) {
        return new Integer(row * col);
    }
};
JTable table = new JTable(dataModel);
JScrollPane scrollpane = new JScrollPane(table);

Când se dorește scrierea de aplicații care folosesc JTable, este necesar să se acorde puțină atenție structurilor de date care vor reprezenta datele din table.

DefaultTableModel este o implementare de model care folosește un vector de vectori de obiecte (de tipul Object) pentru a stoca valorile din celule.

La fel cum se pot copia datele dintr-o aplicație în instanța DefaultTableMode, este, de asemenea, posibil să se ascundă datele în metodele interfeței TableModel astfel încât acestea să poată fi transmise direct către JTable, la fel ca în exemplul de mai sus.

Această abordare duce deseori la aplicații mai eficiente, deoarece modelul este liber să aleagă reprezentarea internă care se potrivește cel mai bine datelor manipulate.

Se recomandă folosirea AbstractTableModel ca și clasă de bază pentru crearea de subclase, respectiv DefaultTableModel atunci când subclasa nu este necesară.

2.2 Folosirea variabilelor întregi

JTable folosește exclusiv variabile întregi pentru a referi liniile și coloanele modelului pe care îl afișează. Este folosită metoda getValueAt(int, int) pentru a întoarce valorile din model pe parcursul desenării.

Coloanele pot fi rearanjate în tabel astfel încât acestea să apară într-o ordine diferită față de cea din model. Acest fapt nu afectează deloc implementarea: atunci când coloanele sunt rearanjate, obiectul de tip JTable menține intern noua ordine și convertește indicii coloanelor înainte de orice interogare a modelului.

Așadar, la programarea unui TableModel, nu este necesară ascultarea după evenimente de reordonare de coloane, întrucât modelul va fi interogat în sistemul propriu de coordonate indiferent de ce se întâmplă la vizualizare.

2.3 Metode de printare

În versiunea curentă de Java sunt adăugate metode la clasa JTable care permit acces convenabil către nevoi obișnuite de afișare. Noile metode print() adaugă cu ușurință suport de printare aplicației ce se dorește a fi dezvoltată.

În plus, noua metodă getPrintable(javax.swing.JTable.PrintMode, java.text.MessageFormat, java.text.MessageFormat) este disponibilă pentru necesități avansate.

La fel ca pentru toate clasele JComponent, se pot folosi InputMap și ActionMap pentru a asocia o acțiune cu tasta și a executa acțiunea în condiții specificate.

3. Arbori

3.1 Introducere

Clasa JTree permite afișarea datelor ierarhice (sub forma unei schițe).

Un nod specific poate fi identificat fie printr-un TreePath (un obiect care încapsulează nodul și toți strămoșii acestuia), fie prin linia de afișare, unde fiecare linie din zona de afișare conține un singur nod.

3.2 Tipuri de noduri
  • Nod expandat - este un nod care nu este frunză (metoda TreeModel.isLeaf(node) întoarce false) și care își va afișa copiii când toți strămoșii săi sunt expandați;
  • Nod colapsat - este un nod care își ascunde copiii;
  • Nod ascuns - este un nod care este situat sub un strămoș colapsat;
  • Nod afișat - toți părinții unui nod care poate fi vizualizat sunt expandați, dar aceștia pot sau nu fi afișați. Un nod afișat se regăsește în zona de afișare și poate fi vizualizat.

Următoarele metode din clasa JTree folosesc cuvântul “visible” pentru a se referi la “afișat”:

  • isRootVisible();
  • setRootVisible();
  • scrollPathToVisible();
  • scrollRowToVisible();
  • getVisibleRowCount();
  • setVisibleRowCount().

Următorul grup de metode folosesc cuvântul “visible” pentru a se referi la “poate fi vizualizat” (sub un părinte expandat):

  • isVisible();
  • makeVisible().

3.3 Detectarea schimbării selecției

Pentru a detecta schimbarea selecției, se va implementa interfața TreeSelectionListener și se va adăuga o instanță folosind addTreeSelectionListener(). Metoda valueChanged() va fi invocată atunci când utilizatorul selectează alt nod, și doar o dată, chiar dacă se efectuează un click de două ori pe același nod.

Cu toate acestea, pentru a face separarea cazurilor de dublu click, indiferent de selecția anterioară, este recomandată abordarea prezentată în codul prezentat mai jos.

// final JTree tree = ...;
MouseListener ml = new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        int selRow;
        selRow = tree.getRowForLocation(e.getX(), e.getY());
        TreePath selPath;
        selPath = tree.getPathForLocation(e.getX(), e.getY());
        if (selRow != -1) {
            if (e.getClickCount() == 1) {
                mySingleClick(selRow, selPath);
            }
            else if (e.getClickCount() == 2) {
                myDoubleClick(selRow, selPath);
            }
        }
    }
};
tree.addMouseListener(ml);
3.4 Afișarea și editarea nodurilor

Pentru afișarea nodurilor complexe (de exemplu, noduri conținând atât text, cât și o icoană) se va implementa interfața TreeCellRenderer și se va folosi metoda setCellRenderer(javax.swing.tree.TreeCellRenderer).

Pentru a edita astfel de noduri, se va implementa interfața TreeCellEditor și se va folosi metoda setCellEditor(TreeCellEditor).

Precizările legate de InputMap și ActionMap sunt aceleași ca pentru JTable (din secțiunea anterioară).

Laborator 10 - Interfete grafice

  • Responsabil: Mihai Nan
  • Profesor titular: Carmen Odubășteanu

Introducere

In cazul programelor pe care le-am facut pana acum, toate mesajele si raspunsurile apareau ca linii de text sugestive, ecranul fiind folosit in mod text. Un astfel de stil de comunicare nu este atractiv pentru utilizatori, motiv pentru care se prefera dialogul prin interfete grafice sau GUI (Graphical User Interface), ecranul fiind folosit in mod grafic.

In trecerea de la o versiune la alta, bibliotecile de clase care ofera servicii grafice au suferit, probabil, cele mai mari schimbari. Acest lucru se datoreaza, pe de o parte, dificultatii legate de implementarea notiunii de portabilitate, iar pe de alta parte nevoii de a integra mecanismele GUI cu tehnologii aparute si dezvoltate ulterior.

In momentul actual, exista doua modalitati de a crea o aplicatie cu interfata grafica, iar acestea sunt:

  • AWT (Abstract Windowing Toolkit) - este API-ul initial pus la dispozitie incepand cu primele versiuni de Java;
  • Swing - este parte dintr-un proiect mai amplu numit JFC (Java Foundation Classes) creat in urma colaborarii dintre Sun, Netscape si IBM, care se bazeaza pe modelul AWT, extinzand functionalitatea acestuia si adaugand sau inlocuind unele componente pentru dezvoltarea aplicatiilor GUI.

Este preferabil ca aplicatiile Java sa fie create folosind tehnologia Swing, deoarece aceasta pune la dispozitie o paleta mult mai larga de facilitati, insa nu se va renunta complet la AWT, deoarece aici exista clase esentiale, reutilizate in Swing.

Pachetul Swing

Componentele Swing, spre deosebire de predecesoarele din versiunile Java anterioare, sunt implementate in intregime in Java. Aceasta are ca rezultat o mai buna compatibilitate cu platforme diferite decat in cazul folosirii componentelor AWT. Unul din principalele deziderate ale tehnologiei Swing a fost sa puna la dispozitie un set de componente GUI extensibile care sa permita dezvoltarea rapida de aplicatii Java cu interfata grafica competitiva, din punct de vedere comercial. Cel mai important pachet, care contine componentele de baza este javax.swing.

Orice interfata utilizator Java este compusa din urmatoarele elemente:

  • Componente – orice poate fi plasat pe o interfata utilizator, cum ar fi butoane, liste de derulare, meniuri pop-up, casete de validare sau campuri de text;
  • Containere – acestea reprezinta componente care pot conţine alte com- ponente (de exemplu panouri, casete de dialog sau ferestre independente);
  • Administratori de dispunere – reprezinta obiecte care definesc modul in care sunt aranjate (dispuse) componentele intr-un container. Admin- istratorul de dispunere nu este vizibil intr-o interfata, insa sunt vizibile rezultatele ”muncii” sale. Dispunerea componentelor interfetei este de mai multe feluri: dispunere secventiala, dispunere tabelara, dispunere marginala sau dispunere tabelara neproportionala.

Crearea ferestrelor

Clasa JFrame este cea pe care o vom folosi pentru a crea ferestre. Ca orice alta clasa care reprezinta componente Swing ea se afla in pachetul javax.swing. Pentru ca este un container, vom folosi, de cele mai multe ori, aceasta clasa prin mostenire nu prin instantiere. Altfel spus, vom crea clase care sa reprezinta ferestre si pentru ca acestea sa devina ferestre de tip JFrame ele vor mosteni aceasta clasa.

public class Fereastra extends JFrame {
    private JButton button;
 
    public Fereastra (String text){
        super(text);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setMinimumSize(new Dimension (300, 200));
        getContentPane().setBackground (Color.blue);
        setLayout(new SpringLayout());
        button = new JButton("Apasa");
        add(button);
        show();
        pack();
   }
 
    public static void main(String args[]) {
        Fereastra f = new Fereastra("Laborator POO" ) ;
    }
}

Metoda add(Component c) este folosita pentru a adauga pe fereastra o componenta exact ca in cazul Appleturilor. Metoda add() este mostenita din clasa Container.

Crearea butoanelor

Un buton poate fi creat folosind clasa JButton. De obicei, butonul este contruit folosind unul dintre constructorii:

  • public JButton(); → un buton fara text
  • public JButton(String text) → un buton cu text dat ca parametru
  • public JButton(String text, Icon ico) → buton cu text si imagine

Textul de pe buton poate fi modificat folosind metoda setText(String text) sau poate fi preluat folosind metoda getText(). Metodele setLabel() si getLabel() sunt considerate obsolete si nu se mai folosesc in prezent.

class Button extends JFrame implements ActionListener{
    private JButton button;
 
    public Button(String text){
        super(text);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setMinimumSize(new Dimension(300, 200));
        getContentPane().setBackground(Color.blue);
        setLayout(new SpringLayout());
        button = new JButton("Apasa");
        button.addActionListener(this);
        add(button);
        show();
        pack();
   }
 
    public static void main (String args[]){
        Button b = new Button ("LaboratorPOO");
    }
 
   @Override
   public void actionPerformed(ActionEvent e){
 
   /*   
    JButton button = (JButton)e.getSource();
    if (button.getText().equals("Apasa")) // valabil daca aveam mai multe butoane ascultate de acest ascultator
    */
       System.out.println("Butonul a fost apasat ! " ) ;
   }
}

Din moment ce butonul reprezinta o componenta, poate fi adaugat pe un container folosind metoda add(Component c) a containerului. Butonul JButton este sensibil la evenimente de tip ActionEvent, asadar, modalitatea de atasare a ascultatorilor si de creare evenimente este similara butoanelor Button din pachetul java.awt.

Crearea componentelor text

class Text extends JFrame implements ActionListener{
    private JButton button;
    private JTextField user;
    private JPasswordField pass;
 
    public Text (String text){
        super(text);
        setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        setMinimumSize(new Dimension(300, 200));
        getContentPane().setBackground(Color.blue);
        setLayout(new FlowLayout());
        button = new JButton ("Apasa");
        button.addActionListener(this);
        add(button);
        user = new JTextField(15);
        add(user);
        pass = new JPasswordField(15);
        add(pass);
        show();
        pack();
    }
 
    public static void main (String args[]){ 
        Text b = new Text ("LaboratorPOO");
    }
 
    @Override
    public void actionPerformed(ActionEvent e){
        /*   
        JButton button = (JButton)e.getSource();
        if (button.getText().equals("Apasa")) // valabil daca aveam mai multe butoane ascultate de acest ascultator
       */
        System.out.println(user.getText() + pass.getText());
    }
}

Vom folosi trei tipuri de componete text: JTextField, JTextArea si JPasswordField. Componenta JTextFiled este un camp de text cu un singur rand pe care pot incapea mai multe caractere. In general, aceasta componenta este folosita pentru a introduce un text de dimensiuni mici. Componenta JTextArea este un camp de text care permite introducerea unui text pe mai multe randuri, deci, aceasta componenta este folosita pentru texte de dimensiuni mai mari. Componenta JPasswordField este similara componentei JTextField, doar ca, aceasta ascunde caracterele introduse de utilizator ca in cazul unui camp pentru introducerea parolei obisnuit.

Metoda folosita pentru preluarea textului dintr-o componenta text este: getText() care returneaza o instanta de String. Pentru a modifica textul dintrun camp de text, se foloseste metoda setText(String text). Componenta JTextArea are in comportament si metoda append(String text) care permite adaugarea unui text la componenta.

Butoane de selectie

Clasele JCheckBox si JRadioButton definesc componente de tip butoane de selectie. Standard, butoanele JCheckBox sunt folosite pentru a crea liste de optiuni de tip multiple-choice (din care poti selecta mai multe variante), iar JRadioButton pentru crearea de liste de tip single-choice (din care se poate selecta o singura optiune). Astfel, pentru butoanele JRadioButton adaugam o noua clasa ButtonGroup cu ajutorul careia precizam care sunt butoanele din care se selecteaza o singura optiune. ButtonGroup defineste un grup de butoane. Din butoanele ce apartin aceluiasi grup, nu putem selecta decat o singura optiune.

Ambele tipuri de componente se creaza similar butoanelor JButton. In general, acestea nu sunt folosite cu evenimente si prin verificari ale starilor acestora (se verifica daca este sau nu select la un anumit eveniment). Verificarea starii unei astfel de componente se face folosind medoda isSelected() care returneaza true daca butonul este bifat si false daca nu este bifat.

Pentru o intelegere mai buna, se recomanda analizarea exemplelor propuse in arhiva laboratorului.

JScrollPane

JScrollPane este o componenta folosita pentru adaugarea de bare de defilare pentru o alta componenta care ar putea depasi dimensiunile containerului in care este adaugata. Instanta JScrollPane nu este folosita in actiunile directe pe care le are componenta pe care o sustine. La construire instantei, i se da o componenta pentru care este creata si pe care adauga bare de defilare (scrollbars). Apoi, instanta JScrollPane este adaugata pe container in locul componentei din instanta JScrollPane.

class Text extends JFrame{
    private JTextArea textArea;  
    private JScrollPane scroll;
 
    public Text (String text){
        super(text);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setMinimumSize(new Dimension(300, 200));
        getContentPane().setBackground(Color.blue);
        setLayout(new FlowLayout());
        textArea = new JTextArea(200, 100);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        textArea.setFont(new Font("Tahoma", 2, 1 2));
        scroll = new JScrollPane(textArea);
        add(scroll);
        show();
        pack();
    }
 
     public static void main (String args[]) {
        Text b = new Text("LaboratorPOO");
    }
}

Etichete

O eticheta reprezinta cea mai simpla componenta. Aceasta este folosita pentru afisarea unui text static in cele mai multe cazuri. Textul de pe o eticheta poate fi modificat sau preluat folosind metodele getText() si setText(String text).

Un JLabel poate fi uzitat si pentru afisarea unei imagini. In acest caz, nu i se aplica niciun text, ci doar un imageIcon, aceasta modalitate fiind considerata cea mai simpla de a afisa o imagine intr-un container.

Tratarea evenimentelor

Un eveniment este produs de o actiune a utilizatorului asupra unei componente grafice si reprezinta mecanismul prin care utilizatorul comunica efectiv cu programul. Exemple de evenimente sunt: apasarea unui buton, modificarea textului intr-un control de editare, inchiderea sau redimensionarea unei ferestre, etc. Componentele care genereaza anumite evenimente se mai numesc si surse de evenimente.

Interceptarea evenimentelor generate de componentele unui program se realizeaza prin intermediul unor clase de tip listener (ascultator, consumator de evenimente). In Java, orice obiect poate ”consuma” evenimentele generate de o anumita componenta grafica.

Avand in vedere gama larga de evenimente existente in limbajul Java, am ales sa vi le prezint pe cele considerate mai important, intr-o serie de exemple incluse in arhiva laboratorului.

poo/breviare/breviar-10.1764517006.txt.gz · Last modified: 2025/11/30 17:36 by george.tudor1906
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0