Some of posts from this blog has been moved to dywicki.pl. You will be automatically redirected to new blog if you would submit comment.
New posts are published on dywicki.pl, this blog contains old content and it is not continued.

Niektóre posty z tego bloga zostały przeniesione do dywicki.pl. Zostaniesz automatycznie przekierowany jeśli bedzięsz chciał dodać komentarz.
Nowe posty sa publikowane na dywicki.pl, ten blog zawiera stare treści i nie jest kontynuowany.

Form Layout

Filed under Eclipse,Java,JFace,SWT by

Jakiś czas temu Michał Mech pisał o tym jak można rozkładać komponenty w Swingu przy pomocy Group Layoutu. Dzisiejszego dnia mam zamiar pokazać Wam drugą stronę medalu – mianowicie Form Layout, który można wykorzystać przy tworzeniu aplikacji w SWT.

Ogólnie w Eclipse dominuje, chyba podobnie jak w Swingu, layout oparty na tworzeniu siatki – Grid Layout. Jest on w miarę elastyczny (zrobisz w nim wszystko to, czego potrzebujesz). Niestety wprowadzanie zmian w układzie (dodanie nowego elementu, zmiana pozycji istniejącego) wiąże się z przepisaniem większości kodu na nowo, ponieważ w “siatce” ma znaczenie kolejność dodawania. Gdy mamy siatkę dwukolumnową to trzeci komponent, który dodajemy wyląduje w drugim wierszu niezależnie od nas.

NaszaForm Layout odrywa nas od siatki dając pełną dowolność w rozkładaniu kontrolek. Każdy komponent możemy ją pozycjonować na dwa sposoby – albo względem konturów kontenera (może to być okno, bądź grupa, w której się znajdujemy) albo względem innego komponentu. No, ale może po kolei.
SWT jest zorganizowane inaczej niż Swing/AWT. Tutaj, by stworzyć jakiś komponent w większości przypadków jest potrzebny przodek, podczas gdy w Swingu nie potrzeba w sumie nic (wynika to z trzymania się specyfikacji JavaBeans, która zakłada, że fasolki powinny mieć puste konstruktory). Dodatkowo każdemu tworzonemu komponentowi należy nadać styl przy użyciu stałych z klasy SWT. Jeśli nie chcemy żadnych dodatkowych efektów dajemy po prostu SWT.NONE. Może kilka przykładów

FormData data = new FormData();
data.right = new FormAttachment(0, 50);

Label name = new Label(<em>parent</em>, SWT.NONE);
name.setText("Nazwa");
name.setLayoutData(data);

Powyższy fragment kodu spowoduje przyciągnięcie etykiety tekstowej do 50 piksela.

[

FormData data = new FormData();
// przyklejamy pole do inntch komponentów
data.left = new FormAttachment(name);
data.right = new FormAttachment(righttable);
Text field = new Text(<em>parent</em>, SWT.SINGLE);
field.setText("Krotki opis");
field.setLayoutData(data);

Ten kawałek kodu spowoduje przyciągnięcie komponentu z lewej do etykiety tekstowej a z prawej do tabelki.

FormData data = new FormData();
// ściągamy przycisk prawie na dno, bez 5px
data.bottom = new FormAttachment(100, -5);
// z prawej strony przycisk będzie 5px od środka kontenera
data.right = new FormAttachment(50, -5);

Button no = new Button(<em>parent</em>, SWT.NONE);
no.setText("Nie");
no.setLayoutData(data);

FormData data = new FormData();
data.bottom = new FormAttachment(100, -5);
// odsuwamy o 5px od środka - pierwszy przycisk jest o -5px od środka,
// więc dajemy 5, żeby dojść do 50% i 5 na rzeczywisty offset od centrum
data.left = new FormAttachment(no, 10);

Button yes = new Button(<em>parent</em>, SWT.NONE);
yes.setText("Tak");
yes.setLayoutData(data);

Ten fragment kodu spowoduje umieszczenie dwóch przycisków w odległości 10 pikseli od siebie. Przyznacie, że kod nie jest skomplikowany, prawda? :) Jest go troszkę, ale nie jest on ani trudny ani zawiły. Zmiany, które wprowadzamy najczęściej sprowadzają się do zmiany kilku FormAttachmentów.

Kompletny kod kontrolki widocznej na załączonym obrazku:

package jug;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;

/**
 * Kontrolka prezentująca możliwości {@link FormLayout} wraz z modyfikacjami
 * wprowadzonymi podczas prelekcji.
 * 
 * @author Łukasz Dywicki
 */
public class FormLayoutDemo {

	private Display display;
	private Shell shell;

	public FormLayoutDemo() {
		display = new Display();
		shell = new Shell(display);
		pack();
	}

	/**
	 * Inicjowanie komponentów i układu okienka
	 */
	private void pack() {
		// główny bohater:
		FormLayout layout = new FormLayout();
		// dorzucamy marginesy
		layout.marginTop = 5;
		layout.marginLeft = 5;
		layout.marginRight = 5;
		layout.spacing = 5;

		shell.setLayout(layout);

		// tworzymy wszystkie komponenty
		Label name = new Label(shell, 0);
		Text field = new Text(shell, SWT.SINGLE | SWT.BORDER);
		Label description = new Label(shell, 0);
		Text area = new Text(shell, SWT.MULTI | SWT.BORDER);
		Button no = new Button(shell, SWT.PUSH);
		Button yes = new Button(shell, SWT.PUSH);
		Table righttable = new Table(shell, SWT.BORDER | SWT.V_SCROLL
				| SWT.H_SCROLL);

		// konfiguracja i pozycjonowanie elementów
		FormData data = new FormData();
		data.right = new FormAttachment(0, 50);

		name.setText("&Nazwa");
		name.setLayoutData(data);

		data = new FormData();
		data.left = new FormAttachment(name);
		data.right = new FormAttachment(righttable);

		field.setText("Krotki opis");
		field.setLayoutData(data);

		data = new FormData();
		data.right = new FormAttachment(0, 50);
		data.top = new FormAttachment(field, 5);

		description.setText("Napis");
		description.setLayoutData(data);

		data = new FormData();
		data.left = new FormAttachment(description); // przypięcie w danym
														// kierunku do danego
														// pola
		data.right = new FormAttachment(righttable);
		data.top = new FormAttachment(field, 5);
		data.bottom = new FormAttachment(yes); // przycisk pod polem

		area.setText("Witamy!");
		area.setLayoutData(data);

		data = new FormData();
		data.bottom = new FormAttachment(100, -5);
		data.right = new FormAttachment(50, -5);

		no.setText("Ni&e");
		no.setLayoutData(data);

		data = new FormData();
		data.bottom = new FormAttachment(100, -5);
		data.left = new FormAttachment(no, 10);

		yes.setText("Ta&k");
		yes.setLayoutData(data);

        data = new FormData();
        data.right = new FormAttachment(100);
        data.bottom = new FormAttachment(yes);
        // albo przyciągnięcie prawie do dna
        // data.bottom = new FormAttachment(100, -5);
        data.top = new FormAttachment(0);
        data.left = new FormAttachment(75);
        righttable.setLayoutData(data);
    }

    public void show() {
        shell.setBounds(50, 50, 300, 200);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    public static void main(String[] args) {
        new FormLayoutDemo().show();
    }
}

Osie..Może jeszcze o tym jak w FormLayout zachowują się FormAttachemnty. Wszystkie komponenty pozycjonujemy w dwóch kierunkach (oś X, Y) przy użyciu 4 dostępnych stron. Jeśli jakiś element ma być rozciągnięty na całą szerokość kontrolki (oś X) to z lewej dajemy FormAttachment(0) a z prawej FormAttachment(100). Są to jak wcześniej wspominałem, wartości procentowe z osi X.
Jeżeli chcemy aby komponent był rozciągnięty na całą wysokość kontrolki postępujemy analogicznie – góra 0, dół 100. I teraz ważna informacja o offsetach. Gdy przesuwamy komponent zgodnie z grotem danej osi jest on dodatni, jeśli przeciwnie jest on ujemny. Offsety są podawane w pikselach jako drugi (opcjonalny) argument, zarówno przy pozycjonowaniu absolutnym jak i relatywnym. No i na koniec obrazek pokazujący osie. :)

Na koniec dorzucam link do slajdów, których użyłem podczas prelekcji poprowadzonej w ramach spotkań Warszawskiego JUGu.

Ps. Ostatnimi czasy WordPress zaczął zachowywać się bardzo dziwnie. Po wskoczeniu do jakiejś notki generował kanał RSS.. jeszcze nie wiem jaka jest tego przyczyna (podejrzewam aktualizację softu na serwerze), póki co zmieniły się linki. Proszę o aktualizację adresów do kanałów w swoich czytnikach. :)

4 responses so far

4 Responses to “Form Layout”

  1. Kamil Ch. says:

    Poniżej to samo w MiGLayout. Przykład w Swingu, bo nie znam SWT, ale nie ma problemu z zastosowaniem go w SWT. To jest jeszcze nieduży przykład, ale przy skomplikowanych formatkach MiGLayout IMO bije każdego innego LayoutManager’a.
    Screen -> http://img84.imageshack.us/img84/6140/migdemodr3.png
    Poniżej kod:

    import java.awt.Container;
    import javax.swing.*;
    import net.miginfocom.swing.MigLayout;
    
    public class MiGLayoutDemo {
        public MiGLayoutDemo() {
            JFrame frame = createGui();
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
        }
        
        private JFrame createGui() {
            JFrame frame = new JFrame("MiG Layout Demo");
            Container panel = frame.getContentPane();
            panel.setLayout(new MigLayout("",
                                          "[right,p::][grow,fill]",
                                          "[][grow][]"));
            
            JTextField nazwaTextField = new JTextField(20);
            nazwaTextField.setText("Krótki opis");
            
            JTextArea opisTextArea = new JTextArea(6,20);
            opisTextArea.setText("Witamy!");
            
            panel.add(new JLabel("Nazwa"));
            panel.add(nazwaTextField,                  "wrap");
            panel.add(new JLabel("Opis"),              "top");
            panel.add(new JScrollPane(opisTextArea),   "grow,wrap");
            panel.add(new JButton("Tak"),              "span 2,split 2,tag ok");
            panel.add(new JButton("Nie"),              "tag cancel");
            
            frame.pack();
            frame.setMinimumSize(frame.getSize());
            
            return frame;
        }
        
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new MiGLayoutDemo();
                }
            });
        }
    
    }
  2. Kamil Ch. says:

    Zapomniałem o linku do samego MiG’a -> http://www.miglayout.com/

  3. Kamil Ch.: Dzięki wielkie za przykład, rzeczywiście widać jak bardzo można skompresować kod. O Migu słyszałem już jakiś czas wcześniej (z racji nowości w Javie 7), ale nigdy nie mogłem się do niego przekonać. Przypomina mi on Form Layout z JGoodies.
    Nie podoba mi się, to że otrzymany efekt wiąże się z użyciem gromady opcji, których trzeba się doszukiwać w dokumentacji (jeśli się ich nie zna), grow, wrap, fill, tag, p:: i tak dalej.. przecież to jest straszne.

    Najprawdopodobniej moja niechęć wynika z nieznajomości tego managera. Nie mniej dzięki za komentarz i przykład, być może przyda się w przyszłości. :)

  4. Kamil Ch. says:

    Na stronie Miga jest QuickStart zawierający 8 stron, po przeczytaniu których byłem w stanie stworzyć dowolną formatkę. Nie wyobrażam sobie teraz tworzenia np. takiego panelu http://www.miglayout.com/images/quickstart1.png przy użyciu standardowych LM. Na pewno nie żałuje czasu poświęconego na przeczytanie tych kilku stron.

Leave a Reply