Java - Swing Kontejneri (Containers)


Kontejneri

Do sada smo se već upoznati sa Swing kontejnerima. Za gotovo sve primjere koristili smo JFrame klasu kao osnovni prozor, a upravo ona predstavlja kontejner. Svaki kontejner koji može da se ponaša kao prozor i da sadrži ostale komponente i kontejnere naziva se top-level kontejner (u jednoj od klasifikacija) i svaka vidljiva komponenta unutar aplikacije mora se naći u hijerarhiji top-level kontejnera. Svaki top-level kontejner sastoji se od pet kontejnera: to su Root Pane (JRootPane), Glass Pane (Component), Layered Pane (JLayeredPane), Content Pane (JComponent) i Menu Bar (JMenuBar).




Root Pane

Root Pane komponenta sadrži sve ostale komponente iz liste. To je početna tačka u hijerarhiji top-level kontejnera. Svaki novi Root Pane unutar postojećeg top-level kontejnera zapravo predstavlja novu hijerarhiju. Za raspored kontrola po nivoima zadužen je Layered Pane. Njegovo postojanje omogućava da se kontrole pojavljuju jedna preko druge unutar kontejnera, što je neophodno u raznim situacijama (na primer, za Tooltip text, kontekstne menije i slično). Layered Pane dijeli kontejner na nekoliko nivoa po z osi (dubini): podrazumevani (DEFAULT_LAYER), nivo za palete (PALETTE_LAYER), nivo modalnih prozora (MODAL_LAYER), nivo Pop-up prozora (POPUP_LAYER) i nivo za prevlačenje (DRAG_LAYER).



Glass Pane

Glass Pane se nalazi iznad svih ostalih komponenti kontejnera i koristi se za „hvatanje” različitih akcija miša. S obzirom na to da je iznad svih komponenti, ovaj sloj garantuje da će događaji biti uhvaćeni.



Menu Bar

Menu Bar je poseban kontejner rezervisan za Menu Bar i dijeli prostor sa Content Pane kontejnerom. Prostor koji Menu Bar rezerviše jeste gornja ivica kontejnera, dok kompletan drugi deo zauzima Content Pane. Oba kontejnera su deca Layered Pane kontejnera i nalaze se u njegovom podrazumevanom nivou.



Content Pane

Content Pane je kontejner koji sadrži sve kontrole. Zato pravilno smještanje kontrola na kontejner podrazumeva smještanje baš na ovaj kontejner. Svaki top-level kontejner sadrži metodu getContentPane(), koja dobavlja njegov aktuelni Content Pane. Na primer, sljedeći kod dodaje kontrolu (btn) na aktuelni Content Pane top kontejner:

myFrame.getContentPane().add(btn);

Programabilno smještanje kontrola na kontejner trebalo bi da podrazumeva smeštanje kontrole na Content Pane tog kontejnera (moguće je i eksplicitno dodjeliti neki kontejner Content Paneu metodom setContentPane(myContainer)). Pored metoda getContentPane, svaki top-level kontejner sadrži i metode za preuzimanje ostalih podkontejnera: getRootPane(), getLayeredPane(), getGlassPane().



JPanel

JPanel se ponaša poput top-level kontejnera. Možemo mu dodavati i skidati kontrole ili formirati njegovu veličinu. Sa druge strane, ova komponenta se pozicionira unutar kontejnera relativno u odnosu na ostale komponente, pa je često korisna za grupisanje (često ćemo željeti da prikazujemo ili sakrivamo određenu grupu kontrola). Korišćenje JPanela je dobar način za to:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;


public class Main {

    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        jFrame.setLayout(new FlowLayout());
        
        jFrame.setSize(new Dimension(550, 550));
        JPanel redPnl = new JPanel();
        redPnl.setBackground(Color.gray);
        redPnl.setPreferredSize(new Dimension(300, 300));
        redPnl.add(new JButton("Gray"));
        redPnl.add(new JButton("Button"));
        redPnl.add(new JButton("Group"));
        
        JPanel yelPnl = new JPanel();
        yelPnl.setBackground(Color.green);
        yelPnl.setPreferredSize(new Dimension(300, 300));
        yelPnl.add(new JButton("Green"));
        yelPnl.add(new JButton("Button"));
        yelPnl.add(new JButton("Group"));
        
        jFrame.add(redPnl);
        jFrame.add(yelPnl);
        
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
}

Prikaz koda iz gornjeg primjera izgledaće ovako:



JScrollPane

Ako je sadržaj neke kontrole previše dugačak da bismo ga smjestili u neki kontejner, smještamo ga u Scroll Pane, a zatim taj Scroll Pane smještamo u kontejner. Ovo omogućava da kompletan sadržaj bude dostupan pomoću ScrollBar-ova koji se nalaze na ivicama kontrole. Sama kontrola sastoji se od vidljivog dela – JViewport i ScrollBar-ova – JScrollBar-s. Unutar Viewporta nalazi se aktuelni sadržaj. Ukoliko je prevelik, pojavljuju se odgovarajući ScrollBar-ovi. Pojavljivanje ScrollBar-ova možemo koordinisati polisama (konstante ScrollPaneConstants klase) i tako odlučiti da li će se i u kojim situacijama pojavljivati. Na primer, ako hoćemo da se uvek pojavljuje vertikalni ScrollBar, napisaćemo:

sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

Ukoliko želimo uvijek horizontalni ScrollBar, možemo napisati:

sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

ScrollBar poznaje sljedeće konstante:

VERTICAL_SCROLLBAR_AS_NEEDED
HORIZONTAL_SCROLLBAR_AS_NEEDED
VERTICAL_SCROLLBAR_ALWAYS
HORIZONTAL_SCROLLBAR_ALWAYS
VERTICAL_SCROLLBAR_NEVER
HORIZONTAL_SCROLLBAR_NEVER

JScrollPane komponenta sadrži još jednu zonu na koju možemo uticati. To su zaglavlja. Ova kontrola razlikuje zaglavlje reda i zaglavlje kolona. Ukolko hoćemo da preuzimamo, odnosno postavljamo sopstvene vrednosti u zaglavlja, koristimo metode setRowHeaderView() i setColumnHeaderView().

import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;


public class Main {

    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        jFrame.setLayout(new FlowLayout());
        jFrame.setSize(new Dimension(350, 350));

        JTextArea ta = new JTextArea();
        ta.setLineWrap(true);
        JScrollPane sp = new JScrollPane(ta);
        sp.setColumnHeaderView(new JLabel("Naslov kolone"));
        sp.setRowHeaderView(new JLabel("Red"));
        sp.setPreferredSize(new Dimension(300, 300));
        jFrame.add(sp);
        
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
}

Prikaz koda iz gornjeg primjera izgledaće ovako:



JSplitPane

Ovaj kontejner sadrži dva kontejnera čiju veličinu korisnik može da modifikuje programabilno ili ručno kroz kontrolu za manipulaciju. Da bismo kreirali Split panel, moramo odrediti po kojoj ćemo ga orijentaciji podeliti (horizontalno ili vertikalno) i moramo svakoj strani dodjeliti komponentu. Takođe, svaka dodata komponenta mora imati postavljenu podrazumjevanu veličinu (setPreferedSize()). U suprotnom će Split panel biti sveden na minimalnu veličinu. Orijentaciju podjele moramo prosljediti kroz konstruktor JSplitPane klase (klasa koja reprezentuje Split panel), i to kroz jednu od dve konstante ove klase – VERTICAL_SPLIT ili HORIZONTAL_SPLIT:

JSplitPane jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

Kada kreiramo instancu, moramo joj dodjeliti i komponente (mada je ovo moguće i putem konstruktora). Split panel se uvek dijeli na dva dijela, lijevi i desni, ili vrh i dno, i u zavisnosti od te podele, odlučujemo na koji način ćemo dodeljivati komponente. Ukoliko je podjela na lijevi i desni, metodi za dodelu komponenti će biti setLeftComponent() i setRightComponent(), a ako je podjela na gornji i donji, metodi su setTopComponent() i setBottomComponent(). Ukoliko isprobate ove metode u različitim kombinacijama, primjetićete da su funkcionalne čak i ako se orijentacije i metodi ne poklapaju:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new FlowLayout());
        
        JSplitPane jSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        
        JPanel p1 = new JPanel();
        p1.setBackground(Color.red);
        
        JPanel p2 = new JPanel();
        p2.setBackground(Color.blue);
        
        p1.setPreferredSize(new Dimension(200, 200));
        p2.setPreferredSize(new Dimension(200, 200));
        
        jSplitPane.setLeftComponent(p1);
        jSplitPane.setRightComponent(p2);
        
        frame.add(jSplitPane);
        
        frame.setSize(600, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);
    }   
}

Prikaz koda iz gornjeg primjera izgledaće ovako:



JTabbedPane

Panel sa tabovima (paletama) podrazumjeva više panela unutar jednog kontejnera od kojih, pomoću odabira određene palete, možemo birati koji ćemo koristiti. Postavljanje komponenti tabova vrši se putem metode addTab(). Ova metoda prihvata instancu komponente, a pored nje, može prihvatiti i naziv palete, kao i njenu ikonicu. Sljedeći primjer kreira tri palete sa tri različite komponente:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

public class Main{
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new FlowLayout());
        
        JTabbedPane jtp = new JTabbedPane();
        
        JPanel red = new JPanel();
        red.setBackground(Color.red);
        red.setPreferredSize(new Dimension(300,100));
        jtp.addTab("Crveni tab", red);
        
        JPanel yellow = new JPanel();
        yellow.setBackground(Color.yellow);
        yellow.setPreferredSize(new Dimension(300,100));
        jtp.addTab("Žuti tab", yellow);
                
        JPanel white = new JPanel();
        white.setBackground(Color.white);
        white.setPreferredSize(new Dimension(300,100));
        jtp.addTab("Bijeli tab", new ImageIcon("icon.png"), white);
        
        frame.add(jtp);
        
        frame.setSize(600, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);
    }   
}

Prikaz koda iz gornjeg primjera izgledaće ovako:



JInternalFrame

Kada hoćemo da napravimo prozor unutar prozora, koristimo JInternalFrame klasu. Kreirana JInternalFrame klasa ponaša se kao i svaki drugi prozor, ali se pojavljuje unutar postojećeg prozora, i ne može da ga napusti:

import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new FlowLayout());
        
        JInternalFrame internalFrame = new JInternalFrame("Interni frame");
        internalFrame.add(new JButton("Dugme"));
        internalFrame.setPreferredSize(new Dimension(200,100));
        
        frame.add(internalFrame);
        internalFrame.setVisible(true);
        
        frame.setSize(600, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);
    }   
}

Prikaz koda iz gornjeg primjera izgledaće ovako:



Podrazumjevano, JInternalFrame objekat nema nijednu opciju karakterističnu za ostale prozore (smanjivanje, povećavanje, minimizacija, maksimizacija...), ali je sve te opcije moguće dodati naknadno. Metoda setClosable() postavlja kontrolu za zatvaranje prozora:

internalFrame.setClosable(true);



setResizable() dozvoljava izmjenu veličine prozora sljedećmo linijom koda:

internalFrame.setResizable(true);

Metoda setMaximizable() omogućava maksimizaciju prozora:

internalFrame.setMaximizable(true);



Očigledno je da JInternalFrame ima iste karakteristike kao i JFrame. Ipak, postoji jedna razlika. JInternalFrame može da postoji samo unutar nekog kontejnera, ali ne i kao osnovni kontejner (glavni prozor aplikacije).


Prikaz kompletnog koda i upotreba gore navedenih funkcija:

import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new FlowLayout());
        
        JInternalFrame internalFrame = new JInternalFrame("Interni frame");
        internalFrame.add(new JButton("Dugme"));
        internalFrame.setPreferredSize(new Dimension(200,100));
        internalFrame.setClosable(true);
        internalFrame.setResizable(true);
        internalFrame.setMaximizable(true);
        
        frame.add(internalFrame);
        internalFrame.setVisible(true);
        
        frame.setSize(600, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setVisible(true);
    }   
}