Wielkość każdego z obszarów
zależy m. in. od liczby i wielkości komponentów, jakie w
nich zostaną umieszczone. Generalną zasadą jest, że w danym obszarze
umieszczamy dokładnie jeden komponent, a jeśli zachodzi potrzeba
umieszczenia tam więcej niż jednego komponentu, to wstawiamy najpierw
kontener (zazwyczaj
JPanel) z własnym rozkładem
(layoutem) i dopiero do kontenera wstawiamy poszczególne
komponenty (lub kolejne kontenery).
Sprawdźmy w której części rozkładu znajduje się nasz
komponent edycji
jtaEdycja. W tym celu zaznaczamy
go w projekcie okna i w edytorze właściwości wybieramy zakładkę
Layout.
Widać, że wartość atrybutu
Direction wynosi
Center
i taka wartość nam odpowiada - obszar edycji zajmować będzie środkową
część okna.
Dodamy teraz u dołu panel, w którym znajdą się przyciski
Nowy,
Otwórz i
Zapisz.
Proszę z palety komponentów wybrać
JPanel
i kliknąć gdziekolwiek na projekcie okienka. Panel (
jPanel1)
został dodany nad obszarem edycji, a my chcieliśmy aby był poniżej
(inaczej mówiąc
Direction naszego panelu
ma wartość
North, a nam odpowiada
South).
Należy odnaleźć w edytorze właściwości kategorię
Layout
i atrybut
Direction ustawić na
South.
Panel jest tam gdzie chcieliśmy. Na razie jest wąski, ponieważ nie
zawiera żadnych komponentów.
Zauważmy, że nasz panel dostał domyślny rozkład (layout)
FlowLayout.
Pozostawimy taki rozkład, jednak ustawimy większy odstęp między
komponentami (
FlowLayout powoduje wstawianie
jednego komponentu obok drugiego, z zadaną przerwą między nimi). Proszę
w hierarchii komponentów "rozwinąć" nasz
JPanel,
zaznaczyć w nim
FlowLayout i w edytorze
właściwości, w zakładce
Properties ustawić
Horizontal
Gap na
20 (oznacza to 20 pikseli
odstępu między komponentami).
Dodamy teraz nasze przyciski. W palecie komponentów proszę
wybrać
JButton i kliknąć myszką w obszar naszego
nowego panelu. Powinien pojawić się przycisk z napisem
jButton1.
Ustawmy najpierw właściwości
text na
Nowy
i
mnemonic na
n. Musimy teraz
określić co ma się dziać po kliknięciu przycisku. Ale moment, przecież
myśmy już zaimplementowali funkcję nowego pliku! Zatem metoda
obsługująca zdarzenie
actionPerformed naszego
przycisku może skorzystać z metody obsługującej
actionPerformed
pozycji
Nowy z menu. Zatem w implementacji metody
jButton1ActionPerformed(java.awt.event.ActionEvent
evt) wpiszmy
jMenuItem1ActionPerformed(evt)
co spowoduje odwołanie się do obsługi pozycji
Nowy
z menu.
Proszę w sposób analogiczny dodać przyciski
Otwórz
i
Zapisz.
Można teraz poeksperymentować z właściwościami
Horizontal Gap
i
Alignment rozkładu
FlowLayout
panelu aby dobrać satysfakcjonujące nas ustawienie
przycisków.
Zaimplementujemy teraz możliwość wyszukiwania tekstu. Chcielibyśmy, aby
po wybraniu funkcji
Szukaj (uruchomienie tej
funkcji zaimplementujemy w formie przycisku) pojawiło się
okienko dialogowe umożliwiające wprowadzenie wyszukiwanego tekstu, a
następnie aby program wyszukał wprowadzony ciąg począwszy od pozycji
kursora i go podświetlił (zaznaczył).
Zaimplementujemy najpierw własne okno dialogowe umożliwiające
wprowadzenie tekstu do wyszukania, z przyciskami
OK
i
Anuluj. Z menu
File wybieramy
funkcję
New File. Wybieramy kategorię
Java
GUI Forms i typ pliku
JDialog Form, po
czym klikamy
Next u dołu okna. Jako nazwę klasy (
Class
Name) podajemy
OknoSzukania, jako
pakiet (
Package) wybieramy
edytor,
pozostałe pola (projekt, lokalizacja, pakiet) pozostawiamy bez zmian i
klikamy
Finish.
W edytorze formularzy pojawi się nowe okienko (pusty prostokąt).
Chcemy, aby nasze okno dialogowe wyszukiwania otrzymywało i
przechowywało referencję do głównego okna edytora (to okno
wyszukiwania będzie szukać i podświetlać tekst wprowadzony przez
użytkownika). W tym celu dodajmy do klasy
OknoSzukania
pole edytor klasy
FEdytor. Można to zrobić na
wiele sposobów, na przykład klikając prawym przyciskiem na
klasie
OknoSzukania w eksploratorze projektu i
wybierając
Add -> Field. W oknie dialogowym
jako nazwę pola podajemy
edytor, a jako typ -
FEdytor.
Przejdźmy na moment do edytora kodu źródłowego i poprawmy
konstruktor. W tej chwili ma on postać:
public OknoSzukania(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
}
a powinien mieć postać:
public OknoSzukania(java.awt.Frame parent, boolean modal, FEdytor e) {
super(parent, modal);
initComponents();
edytor = e;
}
Zauważmy, że konstruktor nie jest podświetlony na niebiesko, zatem
możemy go swobodnie modyfikować.
Sam algorytm wyszukiwania i podświetlenia tekstu umieścimy w oknie
dialogowym, jednak w tym celu musimy mieć dostęp do komponentu
jtaEdycja
klasy
FEdytor, który w tej chwili jest
prywatny dla klasy
FEdytor. Przejdźmy zatem w
edytorze form do
FEdytor i zaznaczmy
jtaEdycja.
W edytorze właściwości wybieramy zakładkę
Code i
modyfikujemy atrybut
Variable Modifiers z
private
na
public. Przechodzimy teraz z powrotem do
edytora form naszego okienka dialogowego
OknoSzukania
i ustawiamy mu layout na
NullLayout. Następnie
ustawiamy atrybut
Form Size Policy w zakładce
Code
na
Generate Resize Code i rozmiar (
Form
Size) na
[400,200]. Ustawmy jeszcze dla
porządku atrybut
title (zakładka
Properties)
na
Szukaj oraz atrybut
Resizable
(kategoria
Other Properties) na
false
(ta ostatnia czynność uniemożliwia zmianę rozmiaru okna). W NetBeans
właściwości logiczne reprezentowane są za pomocą
checkbox'ów, czyli ustawienie właściwości na
false
jest równoznaczne z "odhaczeniem" checkboxa.
Dodajmy teraz do naszego okna następujące komponenty:
- jLabel u góry okienka,
ustawiając text na Podaj tekst do
wyszukania:,
- jTextField poniżej etykiety,
ustawiając jego nazwę (w hierarchii obiektów) na jtfDoZnalezienia
oraz właściwość text na pusty łańcych,
- jButton z tekstem OK
i mnemonikiem k,
- jButton z tekstem Anuluj
i mnemonikiem a.
Zaprojektowane okno powinno wyglądać mniej więcej tak:
Oprogramowywanie akcji komponentów zaczniemy od
Anuluj.
Jego
actionPerformed powinno zamykać okno
dialogowe bez podejmowania jakichkolwiek działań. Można to uzyskać
przez wywołanie metody
dispose(),
która zamknie okno i zwolni przydzielone mu zasoby. Zatem
metoda
jButton2ActionPerformed powinna mieć
postać:
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
dispose();
}
Zanim przejdziemy do implementacji reakcji na
OK
zauważmy, że NetBeans wygenerował nam metodę
main
w kodzie źródłowym klasy
OknoSzukania,
która po pierwsze nie jest nam potrzebna w tej klasie, a po
drugie generuje w tej chwili błąd, ponieważ zmieniliśmy konstruktor
klasy
OknoSzukania, a w
main
nadal używany jest stary konstruktor. Najprostszym rozwiązaniem jest
usunięcie metody
main z kodu klasy
OknoSzukania.
Można to zrobić ręcznie, kasując kod w źródle. Można też
skorzystać z eksploratora projektu: należy rozwinąć drzewko klasy
OknoSzukania,
następnie jego poddrzewo
Methods, kliknąć prawym
przyciskiem myszy na metodzie
main i z menu
kontekstowego wybrać
Delete. Po dodatkowym
potwierdzeniu metoda zostanie wyrzucona z klasy
OknoSzukania.
Reakcja na kliknięcie
OK jest bardziej
skomplikowana. Aby zaimplementować
jButton1ActionPerformed,
potrzebne nam jest kilka informacji (które w przypadku
typowym należałoby wydobyć z dokumentacji JDK):
- klasa String posiada metodę int
indexOf(String), która szuka w łańcuchu, z
którego została wywołana, podanego podłańcucha i jeśli go
znajdzie, zwraca jego pozycję (początek podłańcucha w łańcuchu) lub -1
jeśli podanego podłańcucha nie znaleziono,
- JTextArea posiada (znaną nam już)
metodę String getText() zwracającą cały edytowany
tekst oraz String getText(int offset, int len),
która zwraca tekst o długości len od
pozycji offset (UWAGA: metoda
ta może wyrzucić wyjątek BadLocationException,
więc trzeba pamiętać o przechwyceniu wyjątków przy
korzystaniu z tej metody),
- String posiada metody int
length() (długość łańcucha) oraz String
substring(int begindex) (zwracającą podłańcuch od znaku begindex
do końca łańcucha),
- JTextArea posiada metodę int
getCaretPosition() zwracającą bieżącą pozycję kursora w
tekście,
- JTextArea posiada metody setSelectionStart(int)
i setSelectionEnd(int) pozwalające ustawić
bieżące podświetlenie tekstu,
- wszystkie komponenty posiadają metodę requestFocus()
pozwalającą na ustawienie ich jako komponentów aktywnych,
- znaki w łańcuchach indeksowane są od 0.
Algorytm obsługi przycisku
OK w oknie wyszukiwania
tekstu jest więc następujący:
- sprawdź czy wprowadzony tekst do wyszukania jest pusty
(metoda String getText() klasy JTextField
oraz int length() klasy String),
jeśli tak, przejdź do p. 7,
- pobierz aktualną pozycję kursora z edytor.jtaEdycja,
- pobierz tekst z edytor.jtaEdycja od
pozycji kursora do końca,
- w pobranym tekście wyszukaj wprowadzony podłańcuch,
- jeśli nie występuje, zamknij okno dialogowe i przejdź do
p. 8,
- jeśli występuje, ustaw początek podświetlenia w edytor.jtaEdycja
na pozycja_kursora + pozycja_podłańcucha a koniec
na pozycja_kursora + pozycja_podłańcucha + długość_podłańcucha,
- zamknij okno dialogowe,
- ustaw edytor.jtaEdycja jako aktywny
komponent.
Zadanie 1
Zaimplementować obsługę przycisku
OK w
OknoWyszukiwania,
tak aby realizowała opisany wyżej algorytm.
Teraz pozostaje dodać przycisk
Szukaj do panelu u
dołu okna
FEdytor i oprogramować go tak aby
wykonywał:
OknoSzukania o = new OknoSzukania(this,true,this);
o.setVisible(true);
Korzystając z obiektu klasy
JScrollPane dodamy
możliwość przewijania tekstu w oknie edytora. Obiekty klasy
JScrollPane
umożliwiają pokazywanie komponentów większych niż dostępna w
oknie przestrzeń za pomocą automatycznie obsługiwanych
pasków przewijania (tzw.
scrollbars).
Na początek proszę dodać obiekt klasy
JScrollPane
do naszego okna edytora (wybierając
JScrollPane w
palecie komponentów i klikając gdziekolwiek w obrębie
projektu okna edytora).
Musimy teraz przenieść nasze pole edycji
jtaEdycja
do środka komponentu
jScrollPane1. Należy metodą
"przeciągnij-i-upuść" przenieść
jtaEdycja do
jScrollPane1
tak, aby w hierarchii obiektów nasze pole edycji pokazywane
było jako "podkomponent" obiektu
jScrollPane1.
Przeciąganie najlepiej wykonać w hierarchii obiektów, a nie
w projekcie okna. Należy jeszcze ustawić właściwość
Direction
komponentu
jScrollPane1 na
Center,
bo teraz to ten właśnie komponent ma wypełniać środek naszego okna
edycji, natomiast
jtaEdycja jest teraz
komponentem wewnętrznym obszaru przewijania.
Po zbudowaniu i uruchomieniu programu możemy się przekonać, że
wprowadzenie lub wczytanie z pliku większej ilości tekstu automatycznie
pojawią się paski przewijania.
Dodamy jeszcze do naszego edytora możliwość wyboru kroju i rozmiaru
czcionki, która będzie wykorzystywana do wyświetlania tekstu
w
jtaEdycja. Utworzymy w tym celu nowe okno
dialogowe, w którym będziemy wyświetlać listę dostępnych
czcionek do wyboru.
Z menu
File wybieramy
New File,
a następnie z kategorii
Java GUI Forms znaną nam
już pozycję
JDialog Form. Jako nazwę klasy
wprowadźmy
FCzcionka. Ustawiamy pakiet (
Package)
na
edytor i klikamy
Finish.
Dodajmy nowe pole do
FCzcionka, o nazwie
edytor
i typie
FEdytor (będzie tam referencja do
głównego okna edytora) - sposób dodania nowego
pola do klasy został już wcześniej pokazany na przykładzie okienka
wyszukiwania tekstu.
W nowo utworzonym oknie dialogowym w pierwszej kolejności edytujemy kod
źródłowy, usuwamy całą metodę
main
(nasze okienko będzie uruchamiane z edytora, a nie jako osobny program)
oraz poprawiamy konstruktor tak, aby przyjmował dodatkowo jako parametr
referencję na obiekt klasy
FEdytor i zapamiętywał
ją w swoim polu
edytor (pole dodaliśmy już
wcześniej). Modyfikacja konstruktora jest analogiczna do opisanej wyżej
modyfikacji konstruktora klasy
OknoSzukania.
Przejdziemy teraz do projektowania okna wyboru czcionek. Chcielibyśmy,
aby użytkownik widział listę dostępnych czcionek, mógł
wybrać nazwę czcionki z listy oraz podać jej rozmiar.
Czcionki reprezentowane są w formie obiektów klasy
java.awt.Font
(nazwę konkretnej czcionki możemy uzyskać za pomocą metody
getName()).
Nam potrzebna jest lista wszystkich czcionek. Można ją uzyskać za
pomocą metody
getAllFonts (zwracającej
java.awt.Font[])
z klasy
java.awt.GraphicsEnvironment. Problem w
tym, że klasa ta jest abstrakcyjna (nie możemy utworzyć obiektu tej
klasy), a metoda
getAllFonts() nie jest
statyczna. Musimy posłużyć się konstrukcją:
java.awt.GraphicsEnvironment e =
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
czcionki = e.getAllFonts();
przy czym
java.awt.Font[] czcionki dodajemy
wcześniej jako pole prywatne naszej klasy
FCzcionka.
Pobranie listy czcionek dodajemy w konstruktorze, po wywołaniu
initComponents():
/** Creates new form FCzcionka */
public FCzcionka(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
java.awt.GraphicsEnvironment e =
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
czcionki = e.getAllFonts();
}
Po uzupełnieniu kodu konstruktora, przejdźmy do wizualnego projektu
okna. Zrezygnujemy z layoutu (proszę go ustawić na
Null Layout
i pamiętać o wyłączeniu odwołań do
pack() w
zakładce
Code oraz zablokowaniu możliwości
rozszerzania okna - właściwość
resizable w zakładce
Properties). Dodajmy teraz do naszego okienka
dialogowego obiekt klasy
JList i ustawmy jego
rozmiary na
200x200 pikseli oraz położenie na
(0,0).
Obiekt
jList1 (lista) będzie pokazywał dostępne
czcionki. Oprócz tego musimy dać możliwość wyboru rozmiaru
czcionki. Dodajmy obiekt klasy
JSpinner i ustawmy
go po prawej stronie listy, po czym zmieńmy jego szerokość na
100
pikseli. Potrzebne nam będą jeszcze przyciski
OK i
Anuluj
(standardowe, klasy
JButton - proszę je dodać).
Elegancja wymagałaby jeszcze dodania
JLabel-i
informujących jakie dane prezentują poszczególne komponenty
- proszę je dodać później wedle uznania; z punktu widzenia
funkcjonalności nie są teraz potrzebne.
Zaprojektowane okno może wyglądać np tak:
Przycisk
Anuluj oprogramowujemy tak, aby zamknął
okno dialogowe nie wykonując żadnych innych operacji (wiemy już jak to
zrobić - wystarczy wywołanie
dispose()). Przycisk
OK na razie zostawiamy - oprogramujemy go na końcu.
Wróćmy teraz do kodu konstruktora. Chcemy, aby lista
dostępnych nazw czcionek pojawiła się na liście
jList1.
Zatem w konstruktorze musimy dodać kod umieszczający w
jList1
nazwy czcionek z tablicy
czcionki. Wykorzystamy w
tym celu znaną nam już klasę
java.util.Vector,
ponieważ klasa
JList posiada metodę
setListData(Vector).
Algorytm powinien być następujący (nadal mówimy o
konstruktorze klasy
FCzcionka):
- stwórz nowy obiekt v klasy java.util.Vector,
- dla każdej czcionki z tablicy czcionki: dodaj nazwę
czcionki (metoda getName()) do v
(metoda addElement(Object)) ,
- ustaw v jako dane jList1
(metoda setListData(Vector)).
Ostatecznie więc konstruktor klasy
FCzcionka
powinien wyglądać jak niżej:
public FCzcionka(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
java.awt.GraphicsEnvironment e =
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
czcionki = e.getAllFonts();
java.util.Vector v = new java.util.Vector();
for (int i = 0; i < czcionki.length; i++)
v.add(czcionki[i].getName());
jList1.setListData(v);
}
Pozostaje nam oprogramowanie przycisku
OK. Po
pierwsze musimy sprawdzić którą czcionkę z listy wybrał
użytkownik. Służy do tego np. metoda
getSelectedIndex()
z klasy
JList (zwraca numer - począwszy od 0 -
wybranego elementu z listy). Zauważmy, że numer ten jest jednocześnie
indeksem w tablicy
czcionki (czcionki na liście
są ułożone w takiej samej kolejności, w jakiej występują w tablicy
czcionki).
Wielkość czcionki odczytamy z
jSpinner1, jednak
metoda jest dość skomplikowana, więc poniżej pokazuję przykład
implementacji ustawienia czcionki w oknie edycji:
int c = jList1.getSelectedIndex();
if (c == -1) return; // nic nie wybrano
// nasz spinner operuje na liczbach, jego model to SpinnerNumberModel
javax.swing.SpinnerNumberModel m =
(javax.swing.SpinnerNumberModel) jSpinner1.getModel();
int rozm = m.getNumber().intValue();
// wyprowadzamy nową czcionkę ze stylem 0 i zadanym rozmiarem
java.awt.Font f = czcionki[c].deriveFont(0,rozm);
edytor.jtaEdycja.setFont(f); // ustawiamy nową czcionkę w oknie edycji
setVisible(false);
dispose();
edytor.jtaEdycja.requestFocus();
Teraz jedyne co pozostaje, to podpiąć otwarcie okienka klasy
FCzcionka
pod nowy przycisk
Czcionka w głównym
oknie edytora.