Kapitola 1. GUI – pokračování

Obsah

1.1. Rozmísťování komponent -- JPanel komponenta
1.2. Rozmísťování komponent -- Layout Manager
1.2.1. FlowLayout
1.2.2. GridLayout
1.2.3. BorderLayout
1.2.4. GridBagLayout
1.3. Použití ikon
1.3.1. Základní informace
1.3.2. Problémy při natažení obrázku
1.3.3. Seskupení tlačítek s ikonou do nástrojové lišty (toolbar)

1.1. Rozmísťování komponent -- JPanel komponenta

  • API http://java.sun.com/javase/6/docs/api/javax/swing/JPanel.html

  • nejpoužívanější kontainer v JFC-Swing

  • někdy se používá opravdu jako kontejner, tj. pro optické seskupení několik jiných komponent

  • většinou se používá pro řízení toho, jak mají být komponenty rozmístěny

  • při návrhu GUI typicky použijete až desítky instancí JPanel

  • screenshot z http://java.sun.com/docs/books/tutorial/uiswing/components/panel.html (btw, doporučená četba)

  • při ladění GUI je užitečné nastavit pozadí panelu na nějakou barvu (ale jen při ladění)

  • používané metody jsou v podstatě jen:

    • add(parametry)

    • remove(parameter)

    • setLayout(parametr)

1.2. Rozmísťování komponent -- Layout Manager

  • doporučená četba:

  • za umístění jednotlivých komponent v kontejnerové komponentě zodpovídá layout manager

    • objekt třídy java.awt.LayoutManager resp. některé ze zděděných tříd

    • ze jména balíčku je vidět, že to je jedna z oblastí kde Swing používá dědictví ze staršího AWT

  • každý kontejner má svůj vlastní implicitní layout manager, ale lze jej snadno vyměnit za jiný

    • typické použití jiného manageru je např.:

      objektKontejneru.setLayout(new FlowLayout());

  • každý layout manager má jiné možnosti, k dispozici jsou např.:

    • dva běžné – FlowLayout a GridLayout

    • speciální – BorderLayout

    • velmi sofistikovaný – GridBagLayout

  • téměř nikdy nevoláme jejich metody – stačí manager vytvořit pomocí konstruktoru a použít metodu add() pro umístění komponent

  • v krajním případě lze použití layout managerů vypnout a rozmísťovat komponenty na absolutní pozice (souřadnice)

  • každý kontejner má svůj implicitní layout manager:

    JPanelFlowLayout

    JFrame, JDialogBorderLayout

  • vyhovuje-li implicitní layout manager, nemusíme dělat vůbec nic

1.2.1. FlowLayout

  • http://java.sun.com/docs/books/tutorial/uiswing/layout/flow.html

  • vkládané komponenty rozmísťuje v pořadí vložení do řádky a nemění jejich velikosti implicitně centrovaně s pětibodovou mezerou mezi nimi

    • nevejdou-li se na řádku, pokračuje na další řádce

      • výška řádky je odvozena od výšky nejvyšší komponenty

  • při použití pack() dostaneme jen jednu řádku komponent -- pack() nastaví velikost okna "tak akorát" podle hodnot vrácených po volání getPreferedSize() komponent umístěných k okně

    • požadujeme-li více řádek, musíme použít setSize() u kontejneru

1.2.2. GridLayout

  • vkládá komponenty v pořadí vložení do předdefinované mřížky

    • na další řádku přechází, když vyplní všechny sloupce na stávající řádce

  • např. GridLayout experimentLayout = new GridLayout(2,3);

  • horizontální i vertikální velikosti komponent nastavuje podle velikosti největší komponenty v řádku/sloupci pro všechny řádky a sloupce jednotně

  • vyplní celou plochu kontejneru (vlevo s setHGap(5) a setVgap(10))

1.2.3. BorderLayout

  • http://java.sun.com/docs/books/tutorial/uiswing/layout/border.html

  • může umístit maximálně pět komponent

    • umístění nezáleží na pořadí vložení – je přesně specifikováno světovou stranou

      • lze použít konstanty NORTH, SOUTH, WEST, EAST, CENTER

  • vkládané komponenty zvětšuje podle následujícího algoritmu:

    • severní a jižní – roztáhne na maximální šířku, ale ponechá výšku

    • západní a východní – roztáhne do maximální výšky, ale ponechá jejich šířku

    • středová – vyplní všechen zbylý prostor

  • není-li v některé světové straně umístěna komponenta, ostatní se do tohoto směru roztáhnou

  • je matoucí různé použití metody add() – existují až čtyři funkční možnosti

    this.add(new Button("Sever"), BorderLayout.NORTH);
    this.add(new Button("Jih"), "South");
    this.add(BorderLayout.WEST, new Button("Zapad"));
    this.add("East", new Button("Vychod"));
    • zásadně používat jen první z nich

1.2.4. GridBagLayout

  • http://java.sun.com/docs/books/tutorial/uiswing/layout/gridbag.html

  • nejvíce sofistikovaný (hodně možností), ale prakticky nejužívanější, protože dává většinou nejlepší výsledky

  • spolupracují třídy GridBagLayout a GridBagConstraints

  • využívá strukturu mřížky, každá komponenta může obsadit více řádek a/nebo sloupců

  • každé komponentě lze individuálně stanovit pozici, roztažitelnost, velikost, okraje a umístění

  • dodržuje-li se v kódu pořádek, je použití přímočaré, ale zdlouhavé

public class MujGridBagLayout extends JFrame  {
  private GridBagLayout gbl;
  private GridBagConstraints gbc;
  private JPanel vnitrekPN;
  private static final int NON=GridBagConstraints.NONE;
  . . .
  
  MujGridBagLayout() {
    this.setTitle(getClass().getName());
    this.getContentPane().add(vytvorVnitrek());
    this.setLocationRelativeTo(null);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.pack();
    this.setVisible(true);
  }

  private void nastav(Component c, int x, int y, 
                      int s, int v, 
                      double rs, double rv,  
                      int vyp, int k) {
    gbc.gridx = x;
    gbc.gridy = y;
    gbc.gridwidth = s;
    gbc.gridheight = v;
    gbc.weightx = rs;
    gbc.weighty = rv;
    gbc.fill = vyp;
    gbc.anchor = k;
    gbl.setConstraints(c, gbc);
    vnitrekPN.add(c);
  }
  Container vytvorVnitrek() {
    JButton btn;
    vnitrekPN = new JPanel();
    gbl = new GridBagLayout();
    vnitrekPN.setLayout(gbl);
    gbc = new GridBagConstraints();
    gbc.insets = new Insets(5, 5, 5, 5);

    btn = new JButton("Tl. 1");
    nastav(btn, 0, 0, 1, 1, 0.0, 0.0, NON, WES);
    btn = new JButton("Tlač. 2");
    nastav(btn, 1, 0, 1, 1, 0.0, 0.0, NON, CEN);
    btn = new JButton("Tlačítko 3");
    nastav(btn, 2, 0, 1, 1, 0.0, 0.0, NON, EAS);
    btn = new JButton("Tlač. 4");
    nastav(btn, 0, 1, 2, 1, 1.0, 0.0, HOR, CEN);
    btn = new JButton("Tlačítko 5");
    nastav(btn, 2, 1, 1, 2, 1.0, 1.0, BOT, CEN);
    btn = new JButton("Tlačítko 6");
    nastav(btn, 0, 2, 1, 1, 0.0, 0.0, NON, EAS);
    btn = new JButton("T. 7");
    nastav(btn, 1, 2, 1, 1, 0.0, 0.0, NON, WES);

    return vnitrekPN;
  }

1.3. Použití ikon

1.3.1. Základní informace

  • nejedná se přímo o použití grafiky, ale o umístění obrázku na nejčastěji JButton

    • výhodou je, že všechna známá práce s "buttonem" zůstává zcela stejná

  • používáme ikony, tj. objekty s obrázky pevné velikosti splňující rozhraní javax.swing.Icon

  • pro zjednodušení práce se používá třída javax.swing.ImageIcon, která umí vykreslit obrázky ve formátech GIF, JPEG a PNG

  • ikony může doprovázet současně zobrazený text, ale toto se používá zřídkakdy

    • většinou je ikona doprovázena bublinkovou nápovědou javax.swing.JToolTip, což je něco jiného než text na tlačítku (viz dále)

1.3.2. Problémy při natažení obrázku

import java.net.*;
import java.awt.*;
import javax.swing.*;

public class NacteniIkony {
  public static void main(String[] args) throws Exception {
    JFrame oknoF = new JFrame("NacteniIkony");
    oknoF.setLayout(new FlowLayout());
    
    ImageIcon krizekIK = new ImageIcon("krizek.jpg");
    JButton krizekBT = new JButton("krizek", krizekIK);

    ImageIcon koleckoIK = new ImageIcon("ikony/kolecko.jpg");
    JButton koleckoBT = new JButton(koleckoIK);
    koleckoBT.setMargin(new Insets(0,0,0,0));

    URL trojuhURL = NacteniIkony.class.getResource("ikony/trojuh.jpg");
    ImageIcon trojuhIK = new ImageIcon(trojuhURL);
    JButton trojuhBT = new JButton(trojuhIK);
    trojuhBT.setToolTipText("trojúhelník");
  
    oknoF.add(krizekBT);
    oknoF.add(koleckoBT);
    oknoF.add(trojuhBT);
    oknoF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    oknoF.setSize(320, 90);
    oknoF.setVisible(true);
  }
}
  1. pokud bude program spouštěn z .class souboru, lze obrázek natáhnout

    ImageIcon krizekIK = new ImageIcon("krizek.jpg"); (je-li ve stejném adresáři jako .class soubor)

  2. jako oddělovač adresářů se používá /

    ImageIcon koleckoIK = new ImageIcon("ikony/kolecko.jpg");

    u takto natahovaných souborů nezáleží na velikosti písmen ve jménu krizek.jpg je stejné jako krizek.JPG

  3. pro spuštění z .jar souboru toto jednoduché natažení nefunguje a je třeba použít instanci java.net.URL

    URL trojuhURL = NacteniIkony.class.getResource("ikony/trojuh.jpg");

    v případě, že URL získáváme v instanční metodě, lze použít

    URL trojuhURL = getClass().getResource("ikony/trojuh.jpg");

    Výstraha

    při použití URL záleží na velikosti písmen ve jménu souboru krizek.jpg je různé od krizek.JPG

  4. u JButtonu se ještě přidají k obrázku okraje, kterých se lze zbavit:

    koleckoBT.setMargin(new Insets(0,0,0,0));

  5. popis tlačítka je něco jiného než tooltip (bublinková nápověda)

    JButton krizekBT = new JButton("krizek", krizekIK);
    trojuhBT.setToolTipText("trojúhelník");

    popisu ikony (současně s obrázkem) se snažíme zbavit – viz dále

  6. pro soubory obrázků ikon je lepší použít formát GIF než JPEG, zejména kreslíme-li je sami

1.3.3. Seskupení tlačítek s ikonou do nástrojové lišty (toolbar)

  • použijeme komponentu JToolBar

import java.net.*;
import java.awt.*;
import javax.swing.*;

public class ToolBarVytvoreni {
  public ToolBarVytvoreni() {
    JFrame oknoF = new JFrame("ToolBarVytvoreni");
    oknoF.setLayout(new BorderLayout());
    oknoF.add(vytvorToolBar(), BorderLayout.PAGE_START);
    oknoF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    oknoF.pack();
    oknoF.setVisible(true);
  }

  ImageIcon nactiObrazekIkony(String jmeno) {
    URLClassLoader urlLoader = 
                   (URLClassLoader) this.getClass().getClassLoader();
    URL ikonaURL = urlLoader.findResource("ikony/" + jmeno + ".gif");
    ImageIcon ikonaIK = new ImageIcon(ikonaURL);
    return ikonaIK; 
  }
  
  JButton vytvorTlacitkoProToolbar(String jmenoObrazkuIkony, String toolTip) {
    JButton tlac = new JButton(nactiObrazekIkony(jmenoObrazkuIkony));
    if (tlac.getIcon() != null) {
      tlac.setText("");     // je-li obrazek, neni treba popis
    }        
    tlac.setMargin(new Insets(0,0,0,0));
    tlac.setToolTipText(toolTip);
    return tlac; 
  }

  JToolBar vytvorToolBar() {
    JToolBar hlavniTB = new JToolBar("Jméno ToolBaru");
    JButton tl = vytvorTlacitkoProToolbar("krizek", "křížek - tool tip");    
    hlavniTB.add(tl);    
    hlavniTB.add(vytvorTlacitkoProToolbar("kolecko", "kolečko - tool tip"));
    hlavniTB.addSeparator(new Dimension(100, 20)); // rozměr "natvrdo"
    hlavniTB.add(vytvorTlacitkoProToolbar("trojuh", "trojúhelník - tool tip"));
    hlavniTB.setFloatable(false);
    hlavniTB.setRollover(true);
    return hlavniTB;
  }
  
  public static void main(String[] args) throws Exception {
    new ToolBarVytvoreni();
  }
}
  1. aby JToolBar správně pracoval, je vhodné použít jako layout manager hlavního okna BorderLayout a JToolBar umístit nahoru

    oknoF.add(vytvorToolBar(), BorderLayout.PAGE_START);

  2. pojmenování tool baru není nutné, ale pokud bude přemístitelný (floatable), je to velmi vhodné

    JToolBar hlavniTB = new JToolBar("Jméno ToolBaru");

  3. jednotlivá tlačítka (ikony) mísťujeme nejjednodušeji v pořadí výskytu

    hlavniTB.add(tl);

  4. mezi ikony lze umístit oddělovací mezeru, jejíž rozměr lze volit

    hlavniTB.addSeparator();

    nebo

    hlavniTB.addSeparator(new Dimension(100, 20));

  5. tool bar je implicitně nastaven jako přemístitelný (floatable), což znamená možnost přesunout jej dynamicky doleva, doprava nebo dolů v hlavním okénku

    další možnost je vytáhnout jej zcela mimo hlavní okénko

    • pak bude mít nově vzniklé okénko jméno, které jsme použili v konstruktoru JToolBar

    • zpět do hlavního okénka se dostane zavřením

  6. vlastnost přemístitelnosti lze zakázat (většinou rozumná volba)

    hlavniTB.setFloatable(false);

  7. velmi užitečná vlastnost se dá zapnout pomocí

    hlavniTB.setRollover(true);

    pak by byla orámovaná pouze ikona s fokusem

    bohužel: The implementation of a look and feel may choose to ignore this property.