GridLayout

Koziołek

1 Podstawy ogólnie
2 GridLayout informacje podstawowe
3 GridLayout sposób użycia
     3.1 Tworzenie GridLayout
     3.2 Dodawanie elementów
4 Podsumowanie
5 Zobacz też:

Pracując z komponentami Swing dość szybko zorientujemy się, że ich ułożeniem można sterować na kilka sposobów. Najprostszym z nich jest ręczne ustawianie każdego z komponentów w kontenerze. By mieć taką możliwość musimy wyzerować Layout Managera, danego kontenera:

container.setLayout(null);

Takie podejście ma wiele wad. Największą z nich jest konieczność ręcznego wpisywania położeń dla wszystkich komponentów w kontenerze. Jak wiadomo każdy nadmiarowy kod jest przyczyną nowych ciekawych błędów. Błędów chcemy unikać.
W tym artykule przedstawiony będzie nieskomplikowany java.awt.GridLayout (GL).

Podstawy ogólnie

Jeżeli chcesz zrozumieć jak działają LM zapoznaj się z BorderLayout.

GridLayout informacje podstawowe

GridLayout reprezentuje siatkę komponentów o wymiarach NxM. Powoduje to, że bardzo łatwo zrozumieć jak działa i jeszcze łatwiej jest go używać.
Przykładowy program - okno z komponentami wykorzystujące GL:

package eu.runelord.programmers.java.gridlayouttutorial;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GridLayoutExample extends JFrame {

	private static final long serialVersionUID = 5542988826027886015L;

	public static void main(String[] args) {
		GridLayoutExample gridLayoutExample = new GridLayoutExample();
		gridLayoutExample.setVisible(true);
	}
	
	public GridLayoutExample() {
		init();
	}
	
	private void init() {
		GridLayout gridLayout = new GridLayout(3,2);
		JButton b1 = new JButton("Button 1"), b2 = new JButton("Button 2"), b3 = new JButton(
				"Button 3");
		JTextField textField = new JTextField("pole tekstowe");
		JLabel label = new JLabel("Tekst");
		setLayout(gridLayout);
		setTitle("Przykład BorderLayout");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		add(b1);
		add(b2);
		add(b3);
		add(textField);
		add(label);
		pack();		
	}
}

Jak widać elementy zostały rozmieszczone na siatce o wymiarach 3x2, a ostatnia komórka pozostała pusta.

GridLayout sposób użycia

GridLayout powinien być używany wszędzie tam, gdzie wymagana jest pewna sztywność w rozłożeniu komponentów. Świetnie nadaje się do rozmieszczania komponentów "jak pruskiego wojska", czyli na bazie siatki.
Zanim jednak zdecydujemy się na użycie GL trzeba pamiętać o:

  1. GL teoretycznie dostarcza miejsca tylko dla określonej liczby komponentów. W praktyce wszystkie nadmiarowe komponenty są umieszczane w kolejności dodania.
  2. GridLayout musi mieć co najmniej jedną z przestrzeni większa od zera. Dokładnie przyjrzymy się temu przy omawianiu konstruktorów.
  3. GridLayout ignoruje minimalny i maksymalny rozmiar dla komponentów. Przy tworzeniu kontenera brany pod uwagę jest tylko rozmiar preferowany.
  4. Jeżeli zmniejszamy okno to komponenty są pomniejszane proporcjonalnie. Wymiary wszystkich komórek są takie same i wynoszą szerokość = max({C.width}), wysokość = max({C.height}), gdzie C to zbiór wszystkich komponentów.
    Przykładowy program w którym jeden z komponentów zawiera bardzo długi napis:
package eu.runelord.programmers.java.gridlayouttutorial;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GridLayoutExample2 extends JFrame {

	private static final long serialVersionUID = 5542988826027886015L;

	public static void main(String[] args) {
		GridLayoutExample2 gridLayoutExample2 = new GridLayoutExample2();
		gridLayoutExample2.setVisible(true);
	}
	
	public GridLayoutExample2() {
		init();
	}
	
	private void init() {
		GridLayout gridLayout = new GridLayout(3,2);
		JButton b1 = new JButton("Button 1"), b2 = new JButton("Button 2"), b3 = new JButton(
				"Button 3");
		JTextField textField = new JTextField("pole tekstowe");
		JLabel label = new JLabel("bardzo długi tekst, który napewno jest dłuższy niż standardowy przycisk");
		setLayout(gridLayout);
		setTitle("Przykład BorderLayout");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		add(b1);
		add(b2);
		add(b3);
		add(textField);
		add(label);
		pack();		
	}
}

Jak widać długość wszystkich komponentów jest taka sama i równa długości najdłuższego z komponentów.

Tworzenie GridLayout

GridLayout jest "inteligentnym" LM, który sam unie rozmieścić komponenty mają zadaną tylko ich ilość. Oczywiście robi to w pewien ustalony sposób. Zanim jednak zajmiemy się tym tematem przeanalizujmy jakie mamy możliwości używając tylko konstruktorów. Klasa GridLayout ma dwa konstruktory. Pierwszy jako argumenty przyjmuje odpowiednio ilość rzędów i ilość kolumn siatki. Drugi przyjmuje dodatkowo odległości pomiędzy poszczególnymi komponentami w pionie i poziomie. W obu przypadkach suma ilości kolum i ilości rzędów musi być większa od zera. Użycie drugiego z konstruktorów ilustruje poniższy program:

package eu.runelord.programmers.java.gridlayouttutorial;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GridLayoutExample3 extends JFrame {

	private static final long serialVersionUID = 5542988826027886015L;

	public static void main(String[] args) {
		GridLayoutExample3 gridLayoutExample3 = new GridLayoutExample3();
		gridLayoutExample3.setVisible(true);
	}
	
	public GridLayoutExample3() {
		init();
	}
	
	private void init() {
		GridLayout gridLayout = new GridLayout(2,3,10,10);
		JButton b1 = new JButton("Button 1"), b2 = new JButton("Button 2"), b3 = new JButton(
				"Button 3");
		JTextField textField = new JTextField("pole tekstowe");
		JLabel label = new JLabel("Tekst");
		setLayout(gridLayout);
		setTitle("Przykład BorderLayout");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		add(b1);
		add(b2);
		add(b3);
		add(textField);
		add(label);
		pack();		
	}
}

Dodawanie elementów

GridLayout nie posiada zaimplementowanych metod odpowiedzialnych za "ręczne" sterowanie rozmieszczeniem komponentów. Nie jest mu to potrzebne za to algorytm rozmieszczania komponentów jest dość skomplikowany.
Opiera się on na kilku prostych zasadach oraz dość pokrętnej instrukcji warunkowej. Podstawowe zasady:

  1. Minimalna suma ilości kolumn i ilości wierszy musi być większa od 0.
  2. Komponenty są umieszczane w takiej kolejności w jakiej zostały dodane do kontenera.
  3. Komponenty są umieszczane od prawej do lewej i od góry do dołu.
    Wspomniałem o pokrętnej instrukcji warunkowej. Otóż GL posiada "mechanizm równoważący" rozmieszczenie komponentów. Wyobraźmy sobie sytuację, w której dodano więcej komponentów niż jest dostępnych komórek. W takim momencie mamy dwa wyjścia. Pierwsze opiera się o zasadę programowania defensywnego. Wyrzucamy wyjątek i nasz kod nie musi zachowywać się w sposób nieoczekiwany. To rozwiązanie w tym przypadku ma tą wadę, że to kod klienta jest odpowiedzialny za kontrolę ilości komponentów. W dodatku w momencie gdy pominie tą kontrolę to całość przestaje działać. Druga strategi wywodzi się z programowania opartego o kontrakt. Zakłada ona, że klient wie co robi. Nasz kod jest w pewnym stopniu uodporniony na zachowania niewłaściwe, a zerwanie kontraktu skutkuje tylko nieoczekiwanym zachowaniem się elementów, a nie wyrzuceniem wyjątku. Mechanizm równoważący GL jest napisany w oparciu o warunek na ilość wierszy. Mówi on, że jeżeli zadeklarowana liczba wierszy jest większa od zera to ilość kolumn, w których zostaną rozmieszczone jest wyliczana ze wzoru:
    I_{kolumn} = \frac{I_{komponentow} + I_{wierszy} - 1}{I_{wierszy}}
    W przeciwnym wypadku (ilość wierszy równa 0) stosowany jest wzór:
    I_{wierszy} = \frac{I_{komponentow} + I_{kolumn} - 1}{I_{kolumn}}
    Jako, że obiekt gwarantuje, że zawsze jest co najmniej jeden wiersz lub jedna kolumna więc wzory te mają sens. Należy tu jednak zauważyć, że jeżeli wyliczona ilość kolumn lub wierszy będzie większa niż zadeklarowana to zostaną użyte wartości wyliczone. Nadal obowiązują też przedstawione wcześniej zasady rozmieszczania komponentów. Poniższy program przedstawia sytuację w której zadeklarowano siedem komponentów i tylko sześć rzędów. Pojawia się dodatkowa kolumna, a ilość wypełnionych rzędów wynosi 3 "i pół".
package eu.runelord.programmers.java.gridlayouttutorial;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GridLayoutExample3 extends JFrame {

	private static final long serialVersionUID = 5542988826027886015L;

	public static void main(String[] args) {
		GridLayoutExample3 gridLayoutExample3 = new GridLayoutExample3();
		gridLayoutExample3.setVisible(true);
	}
	
	public GridLayoutExample3() {
		init();
	}
	
	private void init() {
		GridLayout gridLayout = new GridLayout(6,1);
		JButton b1 = new JButton("Button 1"), b2 = new JButton("Button 2"), b3 = new JButton(
				"Button 3");
		JTextField textField = new JTextField("pole tekstowe");
		JLabel label = new JLabel("Tekst"), label2 = new JLabel("text"), label3 = new JLabel("text");
		setLayout(gridLayout);
		setTitle("Przykład BorderLayout");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		add(b1);
		add(b2);
		add(b3);
		add(textField);
		add(label);
		add(label2);
		add(label3);
		pack();		
	}
}

Odwrotna sytuacja. Mamy siedem elementów i sześć kolumn oraz zero rzędów. Wygenerowane zostają dodatkowe rzędy:

package eu.runelord.programmers.java.gridlayouttutorial;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GridLayoutExample4 extends JFrame {

	private static final long serialVersionUID = 5542988826027886015L;

	public static void main(String[] args) {
		GridLayoutExample4 gridLayoutExample4 = new GridLayoutExample4();
		gridLayoutExample4.setVisible(true);
	}
	
	public GridLayoutExample4() {
		init();
	}
	
	private void init() {
		GridLayout gridLayout = new GridLayout(0,6);
		JButton b1 = new JButton("Button 1"), b2 = new JButton("Button 2"), b3 = new JButton(
				"Button 3");
		JTextField textField = new JTextField("pole tekstowe");
		JLabel label = new JLabel("Tekst"), label2 = new JLabel("text"), label3 = new JLabel("text");
		setLayout(gridLayout);
		setTitle("Przykład BorderLayout");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		add(b1);
		add(b2);
		add(b3);
		add(textField);
		add(label);
		add(label2);
		add(label3);
		pack();		
	}
}

Oraz zaskakujące zachowanie. Mamy zadeklarowane siedem elementów, sześć kolumn i jeden rząd.

package eu.runelord.programmers.java.gridlayouttutorial;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class GridLayoutExample5 extends JFrame {

	private static final long serialVersionUID = 5542988826027886015L;

	public static void main(String[] args) {
		GridLayoutExample5 gridLayoutExample5 = new GridLayoutExample5();
		gridLayoutExample5.setVisible(true);
	}
	
	public GridLayoutExample5() {
		init();
	}
	
	private void init() {
		GridLayout gridLayout = new GridLayout(0,6);
		JButton b1 = new JButton("Button 1"), b2 = new JButton("Button 2"), b3 = new JButton(
				"Button 3");
		JTextField textField = new JTextField("pole tekstowe");
		JLabel label = new JLabel("Tekst"), label2 = new JLabel("text"), label3 = new JLabel("text");
		setLayout(gridLayout);
		setTitle("Przykład BorderLayout");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		add(b1);
		add(b2);
		add(b3);
		add(textField);
		add(label);
		add(label2);
		add(label3);
		pack();		
	}
}

W tym przypadku zostaje wygenerowana dodatkowa kolumna ponieważ spełniony jest warunek mówiący, że liczba rzędów musi być większa niż zero by wyliczać ilość potrzebnych kolumn.

Podsumowanie

GridLayout jest dobrym punktem zaczepienia jeżeli chcemy zarządzać komponentami w prosty sposób i wiemy ile ich będzie. Zdecydowanie nie należy używać tego LM jeżeli nie wiemy ile komponentów będzie znajdować się w kontenerze.

Zobacz też:

BorderLayout
BoxLayout
CardLayout
FlowLayout
GridBagLayout
GroupLayout
SpringLayout

.. [#] Pełna dokumentacja klasy GridLayout w javie 1.5 znajduje się pod adresem: http://java.sun.com/j2se/1.5.0/docs/api/java/awt/GridLayout.html

0 komentarzy