Komponenty Javu WingS - tutorial
herman3000
<font size="4">Tworzenie interfejsu użytkownika z użyciem komponentów Javu WingS</span>
Jest to artykuł pokazujący jak używać komponentów Javu WingS do realizacji interfejsu użytkownika
obejmujący typowy "hello world" i prosty kalkulator w 5 krokach,
z zastosowaniem typowych komponentów: przycisk, check box, pole tekstowe, panel, okno i aplet,
demonstrujący jak ładować, zmieniać w locie i tworzyć własne związane z aplikacją arkusze skórek
ZIP zawierający całość z kodem (podzielony źródła i bin): wing-tutorial1.1.1src.zip wing-tutorial1.1.1bin.zip
Applety są dostępne online pod adresem http://www.programics.com/wing-tutorial/wing-tutorial-applets.html
Całość jest też zawarta w pakiecie Javu WingS http://www.programics.com/download/?file=wings
1 Co to jest Javu WingS
2 Hello World
2.1 Ładowanie skórki
2.2 Hello World jako Aplet
3 Kalkulator krok 1
4 Kalkulator krok 2 - interaktywne skróty i tooltip-y
5 Kalkulator krok 3 - zmiana skórki w locie
6 Kalkulator krok 4 - tworzenie arkusza skórki dla aplikacji
7 Kalkulator krok 5
8 Kompilacja
Co to jest Javu WingS
Javu WingS jest zestawem komponentów Java zawierającym większość elementów potrzebnych, dla typowej aplikacji/apletu. WingS jest zgodne z Java od 1.1, J#, J++ i GCJ.Hello World
Pełny kod tego przykładu jest w <font name="Courier New">helloworld/HelloWorld.java</span>WingS jest bardzo podobny do AWT i Swing jak pokazuje poniższy kod:
//WingS hello world
WingPanel panel= new WingPanel(new GridBagLayout());
panel.add(new WingButton("Hello World"));
WingFrame frame= new WingFrame();
frame.setContentPane(panel);
frame.setTitle("Hello World");
frame.pack();
frame.setVisible(true);
// Swing hello world
JPanel panel= new JPanel(new GridBagLayout());
panel.add(new JButton("Hello World"));
JFrame frame= new JFrame();
frame.setContentPane(panel);
frame.setTitle("Hello World");
frame.pack();
frame.setVisible(true);
// AWT hello world
Panel panel= new Panel(new GridBagLayout());
panel.add(new Button("Hello World"));
Frame frame= new Frame();
frame.add(panel, BorderLayout.CENTER);
frame.setTitle("Hello World");
frame.pack();
frame.setVisible(true);
Kod tworzy i wyświetla okno zawierające centralnie przycisk 'Hello World'.
Ładowanie skórki
Jest tylko jedna istotna różnica, WingS używa arkuszy skórek ("skin style sheets") do wyświetlania komponentów. Sensem arkuszy skórek jest opis własności takich jak kolory, fonty, ramki, marginesy, obrazki tła, ikony itd. Relacja pomiędzy komponentami WingS a arkuszami skórek jest analogiczna do relacji pomiędzy HTML a CSS. Przed wyświetleniem jakiegokolwiek komponentu warto załadować skórkę.Robi to pierwsza linia kodu metody <font name="Courier New">main() w HelloWorld.java</span>:
WingSkin.loadSkin("/skin/green", true, HelloWorld.class);
Kod ten ładuje arkusze skórki z folderu skin/green zlokalizowanego w pliku jar zawierającym <font name="Courier New">HelloWorld.class</span>
Skórki mogą być również ładowane z plików lokalnych i adresów sieciowych URL.
Hello World jako Aplet
HelloWorld w wersji apletu, (<font name="Courier New">helloworld/HelloWorldApplet.java</span>) jest nawet prostszy, tylko 5 linii kodu: public void start()
{
WingSkin.loadSkin("/skin/green", true, HelloWorldApplet.class);
WingComponent.updateSkin(this);
WingPanel panel= new WingPanel(new GridBagLayout());
panel.add(new WingButton("Hello World"));
setContentPane(panel);
}
Jest tu jedna istotna zmiana: po <font name="Courier New">loadSkin</span> jest <font name="Courier New">updateSkin</span> służący do odświeżania skórki w czasie pracy - w locie.
Tak musi być, bo komponenty WingS ładują skórki zanim staną się widoczne a kiedy <font name="Courier New">HelloWorldApplet</span> wywołuje <font name="Courier New">loadSkin</span>, aplet jest już widoczny.
Kalkulator krok 1
Pełny kod tego przykładu zawiera calc/step1/Calc.javaTypowy kalkulator ma wyświetlacz i mnóstwo przycisków.
Sensem tego przykładu jest właśnie coś takiego.
Logika kalkulatora jest zawarta w osobnej klasie CalcLogic, która nie korzysta z komponentów WingS i wykracza poza temat tego artykułu.
Klasa ta ma 3 metody wywoływane przez interfejs użytkownika, gdy naciśnie się przycisk kalkulatora:
void buttonAction(int buttonFunction); // wykonuje akcję przypisaną do przycisku
String getDisplayText(); // zwraca aktualną zawartość wyświetlacza
boolean isEnabled(int buttonFunction); // mówi czy przycisk ma być dostępny
Klasa CalcLogic jest zainspirowana przez KCalc z KDE.
Struktura interfejsu użytkownika jest podobna do Hello World, jeden główny panel w jednym oknie/aplecie i WingSkin.loadSkin na początku kodu.
Panel główny zawiera jedno pole tekstowe i matrycę przycisków:
WingPanel panel;
panel.setLayout(new GridBagLayout());
panel.add(tfDisplay= new WingTextField());
tfDisplay.setHorizontalAlignment(RIGHT);
Dla uproszczenia kodu wszystkie przyciski są dodawane do panelu w ten sam sposób metodą addButton
...
addButton(x++, y, 1, 1, "1", CalcLogic.FUNC_1);
addButton(x++, y, 1, 1, "2", CalcLogic.FUNC_2);
addButton(x++, y, 1, 1, "3", CalcLogic.FUNC_3);
addButton(x++, y, 1, 2, "+", CalcLogic.FUNC_PLUS);
...
void addButton(int gridx, int gridy, int gridwidth, int gridheight,
String text, int func)
{
// ustawienia GridBagConstraints dla przycisku
GridBagConstraints c= new GridBagConstraints();
...
//utwórz przycisk
WingButton b= new WingButton(text);
// dodaj przycisk do tablicy
bFunc[func]= b;
// dodaj do panelu
this.add(b, c);
// dodaj nasłuchiwacza
b.addActionListener(this);
// zrób z przycisku duży prostokąt
b.setPreferredSize(new Dimension(40,32));
}
Nie jest to nic szczególnego, kod wygląda jak typowy program w AWT/Swing.
Klasa WingButton wysyła klasyczne zdarzenia AWT java.awt.ActionEvent i wywołuje actionPerformed(ActionEvent e) tak samo jak JButton i Button.
Reszta kodu kalkulatora wygląda tak:
public void actionPerformed(ActionEvent e)
{
Object src= e.getSource();
// przegląda tablicę przycisków w poszukiwaniu naciśniętego
for(int f= 0; f<bFunc.length; f++)
{
if(src==bFunc[f])
{
// wykonaj funkcję związaną z przyciskiem
logic.buttonAction(f);
// wyświetl wynik
tfDisplay.setText(logic.getDisplayText());
// odśwież stan-dostępny przycisku
for(int n= 0; n<bFunc.length; n++)
{
if(bFunc[n]!=null) bFunc[n].setEnabled(logic.isEnabled(n));
}
return;
}
}
}
Kalkulator krok 2 - interaktywne skróty i tooltip-y
W tym kroku każdy przycisk kalkulatora zostanie powiązany ze skrótem klawiaturowym i otrzyma toolpip informujący użytkownika o tym skrócie.Metoda addButton zostaje rozszerzona o 2 linie kodu i ma 3 dodatkowe parametry:
private void addButton(int gridx, int gridy, int gridwidth, int gridheight,
String text, int func,
String tooltipText, int keyCode, int keyModifiers)
{
...
// ustaw skrót klawiaturowy
b.setShortcut(keyCode, keyModifiers);
// ustaw tooltip
b.setTooltip(tooltipText);
}
Kod dodający przyciski teraz będzie wyglądał tak:
...
addButton(x++, y, 1, 1, "1", CalcLogic.FUNC_1, "Digit 1\nShortcut: numpad 1",
KeyEvent.VK_NUMPAD1, 0);
addButton(x++, y, 1, 1, "2", CalcLogic.FUNC_2, "Digit 2\nShortcut: numpad 2",
KeyEvent.VK_NUMPAD2, 0);
addButton(x++, y, 1, 1, "3", CalcLogic.FUNC_3, "Digit 3\nShortcut: numpad 3",
KeyEvent.VK_NUMPAD3, 0);
addButton(x++, y, 1, 2, "+", CalcLogic.FUNC_PLUS,
"Addition operator\nShortcut: numpad +", KeyEvent.VK_ADD, 0);
...
Kalkulator krok 3 - zmiana skórki w locie
W tym kroku kalkulator otrzyma 2 nowe skórki i przełączniki zmieniające skórkę w locie.
Interfejs użytkownika zostaje wzbogacony o trzy przełączniki zmieniające skórkę.
W WingS nie ma oddzielnych klas dla checkbox-ów, przycisków radio i przełączników.
Klasa WingChecbox obejmuje funkcjonalność tych wszystkich kontrolek.
Dla tego przykładu WingChecbox o wyglądzie przełącznika będzie najlepszy.
Przyciski zostają dodane do głównego panelu nad wyświetlaczem poniższym kodem:
// utwórz panel z przełącznikami skórek
WingPanel panel2= new WingPanel(new GridLayout(1,0));
panel2.add(tbGreen= new WingCheckBox("green", TOGGLE));
panel2.add(tbOrange= new WingCheckBox("orange", TOGGLE));
panel2.add(tbVista= new WingCheckBox("vista", TOGGLE));
// dodaj przełączniki do grupy radio
RadioGroup g= new RadioGroup();
g.add(tbGreen);
g.add(tbOrange);
g.add(tbVista);
// zaznacz początkową skórkę
tbGreen.setSelected(true);
//dodaj do głównego panelu
this.add(panel2, c);
// dodaj nasłuchiwacze
tbGreen.addItemListener(this);
tbOrange.addItemListener(this);
tbVista.addItemListener(this);
Klasa RadioGroup działa jak ButtonGroup w Swing czy CheckboxGroup w AWT.
Pozwala jedynie jednemu z checkbox-ów być włączonym w danej chwili, tak jak przyciski w odbiorniku radiowym.
Kod wywoływany przez te przełączniki:
public void itemStateChanged(ItemEvent e)
{
Object src= e.getSource();
// sprawdź przyciski skórek
if(src==tbGreen || src==tbOrange || src==tbVista)
{
loadCalcSkin((tbGreen.isSelected())?"green"
:(tbOrange.isSelected())?"orange":"vista");
// odśwież skórkę zaczynając od root pane
WingComponent.updateSkin(getRootPane());
}
}
gdzie loadCalcSkin ładuje wybraną skórkę:
protected static void loadCalcSkin(String skinTheme)
{
// usuń wcześniej załadowane skórki
WingSkin.removeAllSkins();
// załaduj z folderu skin/skinTheme względem kodu Calc
WingSkin.loadSkin("/skin/"+skinTheme, true, Calc.class);
}
Kalkulator krok 4 - tworzenie arkusza skórki dla aplikacji
W tym kroku kalkulator otrzyma arkusz skórki zmieniający jego wygląd.
Arkusz skórki zmieniający wygląd kalkulatora (plik skin/_common/wingcalc.ini) ustawiający następujące właściwości:
dla przycisków z ID stylu=calc ustawia font na Dialog bold rozmiar 18px z antyaliasingiem,
dla przyciskow z ID stylu=calc.small ustawia font na Dialog bold rozmiar 15px z antyaliasingiem,
dla pola tekstowego z ID stylu=calc.display ustawia font na Dialog bold rozmiar 24px z antyaliasingiem
i kolor tła na biały z 25% przezroczystości
jest pokazany poniżej:
calc.button.normal.font= Dialog
.size= 18
..bold= 1
..antialias= 1
calc.small.button.normal.font= Dialog
.size= 15
..bold= 1
calc.display.textfield.doc.normal.font= Dialog
.size= 24
..bold= 1
..antialias= 1
calc.display.textfield.doc.normal.color= #40ffffff
Format pliku to zwyczajnie klucz = wartość
Kropka na początku klucza oznacza, że klucz jest względny, względem poprzedniego.
Jedna kropka oznacza doklej do poprzedniego:
calc.button.normal.font= Dialog
.size= 18
odpowiada więc
calc.button.normal.font= Dialog
calc.button.normal.font.size= 18
</code
Dwie kropki - jedna gałąź w górę:
calc.button.normal.font.size= 18
..bold= 1
odpowiada
calc.button.normal.font.size= 18
calc.button.normal.font.bold= 1
Teraz zmodyfikowana wersja metody loadCalcSkin ładującej arkusze skórek:
```java
protected static void loadCalcSkin(String skinTheme)
{
// usuń poprzednie skórki
WingSkin.removeAllSkins();
// rejestruj arkusz skórki
WingSkin.registerStyleSheet("wingcalc");
// ładuj z katalogu skin/_common
WingSkin.loadSkin("/skin/_common", true, Calc.class);
// ładuj z katalogu skin/skinTheme
WingSkin.loadSkin("/skin/"+skinTheme, true, Calc.class);
}
Metoda registerStyleSheet("wingcalc") powoduje, że WingS będzie szukał arkusza wingcalc.ini w każdym przetwarzanym folderze. Domyślnie WingS szuka tylko arkusza wings.ini
Dodatkowy loadSkin ze ścieżką do wingcalc.ini ("/skin/_common") jest dodany, zamiast dodawania pliku wingcalc.ini do każdego folderu z osobna (green, orange i vista)
Teraz kilka linii kodu wiążącego komponenty z nowymi stylami:
...
// ustaw ID stylu wyświetlacza
tfDisplay.setStyleId("calc.display");
...
i zmodyfikowana addButton
private void addButton(int gridx, int gridy, int gridwidth, int gridheight,
String text, String styleID, int func,
String tooltipText, int keyCode, int keyModifiers)
{
...
//utwórz przycisk z określonym ID stylu
WingButton b= new WingButton(styleID, text);
...
}
...
addButton(x++, y, 1, 1, "8", "calc", CalcLogic.FUNC_8,
"Digit 8\nShortcut: numpad 8", KeyEvent.VK_NUMPAD8, 0);
addButton(x++, y, 1, 1, "9", "calc", CalcLogic.FUNC_9,
"Digit 9\nShortcut: numpad 9", KeyEvent.VK_NUMPAD9, 0);
addButton(x++, y, 1, 2, "-", "calc", CalcLogic.FUNC_MINUS,
"Subtraction operator\nShortcut: numpad -", KeyEvent.VK_SUBTRACT, 0);
addButton(x++, y, 1, 1, "+/-", "calc.small", CalcLogic.FUNC_SIGN,
"Sign button\nShortcut: F9", KeyEvent.VK_F9, 0);
Większość przycisków otrzymała ID stylu "calc" (font 18px), tylko przyciski z napisami dłuższymi niż 2 znaki "calc.small" (font 15px).
Kalkulator krok 5
W tym kroku napisy na niektórych przyciskach zostaną zastąpione obrazkami i dodatkowo obrazki będą wyświetlane na ich tooltip-ach.Graficzne symbole zostały umieszczone w jednym pliku GIF calc.gif
Ten GIF jest matrycą ikon o wielkości 30x20 pixeli każda.
Obrazki mogą być dodawane do przycisków bezpośrednio jako etykiety lub powiązany w arkuszu skórki.
Arkusz skórki pozwala stosować obrazki jako tło i ikony, które mogą być ustawione w następujący sposób:
Każdy przycisk z ikoną otrzyma własny ID stylu:
...
addButton(x++, y, 1, 2, null, "calc.plus", CalcLogic.FUNC_PLUS,
"Addition operator\nShortcut: numpad +", KeyEvent.VK_ADD, 0);
addButton(x++, y, 1, 1, null, "calc.divide", CalcLogic.FUNC_DIVIDE,
"Division operator\nShortcut: numpad /", KeyEvent.VK_DIVIDE, 0);
...
Tytuły przycisków zostały ustawione na null, bo będą one wyświetlać tylko obrazki bez tekstu.
Teraz zmodyfikowany wingcalc.ini:
...
calc_normal.image= wingcalc/calc.gif
.alpha= #4C
calc.plus.button.normal.icon.image= [calc_normal]
.part= 0,30,20
calc.minus.button.normal.icon.image= [calc_normal]
.part= 1,30,20
calc.multiply.button.normal.icon.image= [calc_normal]
.part= 2,30,20
...
alpha= #4C - opcjonalny parametr, który czyni obrazek przezroczystym w 30%.
part= 0,30,20 - opcjonalny parametr mówiący, że obrazek jest matrycą o wielkości komórki 30x20px i należy użyć pierwszą komórkę z tej matrycy
[calc_normal] jest to referencja, która może być rozwinięta jako:
calc.plus.button.normal.icon.image= wingcalc/calc.gif
.alpha= #4C
..part= 0,30,20
Ostatni krok to dodać obrazki do tooltip-ów.
Zmodyfikowany kod addButton dodający tooltip-y:
// dodaj tooltip do przycisku
if(text!=null)
{
// zwykły przycisk tekstowy
b.setTooltip(tooltipText);
}
else
{
// przycisk z obrazkiem, użyj obrazka jako w tooltip-ie
b.setTooltip(new LabelItem(tooltipText,
WingSkin.getImage(styleID, "button.normal.icon", null)));
}
Methoda getImage ładuje obrazek określony w arkuszu skórki właściwością <styleID>.button.normal.icon.image
Klasa LabelItem służy do wyświetlania etykiety tekst z obrazkiem i może być używana zamiast wartości String jako etykieta.
A jak te komponenty zainstalować by widział je NETBEANS?
Na przyszłość dodam, że nie musisz wrzucać wielu rozmiarów tego samego obrazka - rozmiar możesz określić podając szerokość w pikselach w tagu {{Image...}}
więcej info
Muszę przekazać "szacun" za arta. Co prawda przejrzałem po łebkach [nie trawię Javy], ale od razu widać, że dawno nie było tak elegancko przedstawionego tematu. Oby więcej takich :)