W niniejszym artykule o bibliotece "Swing" języka Java, już znajdującym się w kategorii bezpośrednio związanej z tym tematem, wtajemniczę Cię w podstawowe elementy jakie możesz wstawić do okna 🪟. Aby w ogóle zrobić cokolwiek, trzeba najpierw poznać podstawowe komponenty "Swing"To będzie sama lista z krótkim opisem co do czego służy i jak prezentuje się w oknie. Jak zainteresowany(-a), to zapraszam 👋!

OTO JAKIE MOŻESZ ZNALEŹĆ KOMPONENTY W "SWING"!

Komponent, mam na myśli pojedynczy widoczny element okna, taki puzzel 🧩. W "Swing", interfejsy użytkownika budowane są poprzez tworzenie komponentów i przydzielanie im miejsca w oknie (prawie jak puzzle 🙂). Teksty, przyciski, listy rozwijane i tym podobne rzeczy, nazywane są komponentami ℹ️. Zanim jednak zaczniemy je poznawać, musimy się dowiedzieć jak je ujrzeć w oknie 😳.

JAK ZACZĄĆ?

Artykuł wprowadzający do biblioteki "Swing", w którym zaprezentowałem tworzenie samego okna, znajduje się tutaj, a w tym miejscu jedynie przypomnę jak sprawić, żeby dowolny komponent pojawił się w oknie.

W klasie odpowiedzialnej za wyświetlanie okna ("JFrame"), obiekt komponentu do okna wprowadzisz używając metody "add", dostępnej po wywołaniu innej metody, "getContentPane" 👇:

getContentPane().add([komponent]);

To jest najprostsza droga do tego, aby ujrzeć dany komponent w środku okna ✅.

LISTA KOMPONENTÓW

Teraz komponenty! Przedstawię Ci zbiór takich, jakie uważam za podstawowe. Podam nazwę jaką przyjmuje w kodzie, opiszę krótko co to jest i zarzucę obrazek jak to wygląda w swojej standardowej (domyślnej) formie ✔️. 

JLABEL

"Etykieta", czyli po prostu miejsce na tekst 😄. Najbardziej podstawowy konstruktor oczekuje jedynie podania łańcucha znaków. Wtedy pojawi się po lewej stronie okna ℹ️. Możesz także określić wyrównanie, czyli od której strony ma zostać wyświetlony 👍. Używamy wtedy specjalnego typu wyliczeniowego "SwingConstants", dzięki któremu ustalamy czy ma być pisany od lewej strony, na środku, czy od prawej strony.

Dla przykładu, taka postać 👇:

var label = new JLabel("Jestem tekstem!", SwingConstants.CENTER);

spowoduje wypisanie podanego łańcucha na środku okna. Wtedy tak on wygląda:

Wygląd komponentu "JLabel" w "Swing"

Wygląd komponentu "JLabel" w bibliotece "Swing".

Gdybyś chciał(a) zmienić sobie rozmiar czcionki (bo domyślnie jest bardzo mała), to jest jeden trik polegający na wywołaniu metody "setFont" w następującej postaci:

label.setFont(new Font(label.getFont().getName(), Font.PLAIN, 32));

Efekt: nałożenie tej samej czcionki, którą ma nasza "etykieta", bez pogrubiania ani kursywy, o rozmiarze 32 pikseli 🌟.

JBUTTON

"JButton" to przycisk, który możemy kliknąć, aby powstała jakaś reakcja ⏩. Celem zdefiniowania co ma się wydarzyć po kliknięciu, należy nałożyć metodę do zdarzenia kliknięcia przycisku, które pełni rolę "wywołania zwrotnego" (ang. callback) - metody wywoływanej "z opóźnieniem" pod wpływem jakiegoś zdarzenia ℹ️. Pokazuję nieco niżej jak się to robi.

TWORZENIE PRZYCISKU

Sam przycisk niewykonujący żadnych czynności, wymaga jedynie użycia konstruktora, w którym wprowadzamy łańcuch znaków, czyli jaki tekst ma przyjmować przycisk 📝. Oto prosty przykład konstruktora z łańcuchem znaków 👇:

var button = new JButton("Jestem przyciskiem!");

Wówczas po dodaniu go do okna, zobaczymy przycisk w całej okazałości:

Wygląd komponentu "JButton" w "Swing"

Wygląd komponentu "JButton" w bibliotece "Swing".

Miej jednak na uwadze, iż przycisk może przybierać różne rozmiary w zależności od układu ramki/panelu, do którego się go "przykleiło" . Na przykład "BorderLayout" sprawi, że przycisk zajmie calutką powierzchnię okna wyczerpując przestrzeń co do piksela 🔥! Zaś "GridLayout" (znowu na przykład 😝), ustawi przycisk w jednej z komórek siatki.

DEFINIOWANIE ZDARZENIA PO KLIKNIĘCIU PRZYCISKU

Jak już tworzymy przycisk, to po to, żeby coś robił 😄! W celu dodania zdarzenia do przycisku "Swing", należy albo dorzucić klasę wewnętrzną i do niej zaimplementować interfejs "ActionListener", albo skorzystać z podejścia znanego z paradygmatu funkcyjnego - z wyrażenia lambda. W obu przypadkach korzystamy z metody "addActionListener" 👇:

button.addActionListener([implementacja interfejsu ActionListener]);

To nam pozwala na zdefiniowanie reakcji na zdarzenie kliknięcia przycisku 💥. W miejsce parametru wprowadzamy implementację interfejsu "ActionListener". Najlepiej wstawić wyrażenie lambda osadzone bezpośrednio w miejsce parametru:

button.addActionListener(event -> System.out.println("Kliknięto przycisk!"));

Powyższy zapis da mały efekt po kliknięciu przycisku: wypisanie w konsoli tekstu 🙂.

JTEXTFIELD

Pole tekstowe, do którego możemy wstawić dowolną treść przy pomocy klawiatury - to znajdziesz pod hasłem "JTextField". Podobnie jak z przyciskiem, tutaj też możemy określać co się stanie po wprowadzeniu lub usunięciu dowolnego znaku 🧨.

TWORZENIE POLA TEKSTOWEGO

Oto jeden z wariantów konstruktora, do którego wprowadzamy tekst i szerokość pola w znakach 👇:

var textField = new JTextField("Jestem polem tekstowym!");

A tak prezentuje się pole tekstowe:

Wygląd komponentu "JTextField" w "Swing"

Wygląd komponentu "JTextField" w bibliotece "Swing".

Tutaj też szerokość pola zależy od układu, którym posługuje się ramka bądź panel. "BorderLayout" rozciągnie na całą powierzchnię, a np. "FlowLayout" wyznaczy szerokość na podstawie długości tekstu, jaki występuje w środku od chwili uruchomienia naszej aplikacji (chyba, że wstawimy drugi parametr liczbowy określający szerokość pola w znakach).

DEFINIOWANIE ZDARZENIA PO EDYCJI TREŚCI POLA TEKSTOWEGO

Aby dostać się do zdarzeń wprowadzenia lub usunięcia znaku, trzeba sięgnąć po bardziej niekonwencjonalny zapis. Najpierw wywołujemy metodę "getDocument", a potem "addDocumentListener". W miejsce parametru, definiujemy implementację interfejsu "DocumentListener", który przyjmuje 3 metody 👇:

  1. "insertUpdate" (zdarzenie wprowadzenia znaku),
  2. "removeUpdate" (zdarzenie usunięcia znaku),
  3. "changedUpdate" (zdarzenie zmiany stylu znaków odpalane wyłącznie w typie "StyledDocument").

Gdy chcemy dla przykładu, zaprogramować wypisywanie w konsoli aktualną liczbę znaków co dowolne wprowadzenie lub usunięcie znaku, musimy napisać tak:

textField.getDocument().addDocumentListener(new DocumentListener() {
	@Override
	public void insertUpdate(DocumentEvent documentEvent) {
		writeTextLength();
	}

	@Override
	public void removeUpdate(DocumentEvent documentEvent) {
		writeTextLength();
	}

	@Override
	public void changedUpdate(DocumentEvent documentEvent) {
		writeTextLength();
	}

	private void writeTextLength() {
		System.out.println(textField.getText().length());
	}
});

Stosując tę metodę, możesz wpływać na inne komponenty w "Swing" 🔥! Na przykład można sprawić, że przycisk będzie zablokowany, jeśli okaże się, że pole tekstowe jest puste 💡.

JTEXTAREA

"JTextArea" także jest polem tekstowym, jednak w odróżnieniu od poprzednika, tutaj możemy wstawić wiele wierszy tekstu do jednego pola.

TWORZENIE WIELOWIERSZOWEGO POLA TEKSTOWEGO

Konstruktor przyjmuje 3 parametry 👇:

  1. łańcuch znaków jaki zostanie do niego umieszczony po uruchomieniu aplikacji (obowiązkowy),
  2. liczba wierszy (opcjonalny),
  3. liczba kolumn (opcjonalny).

Dodanie tylko pierwszego z nich sprawi, że pole tekstowe zacznie się "rozciągać" zgodnie z zawartością w środku:

var textArea = new JTextArea("Jestem polem tekstowym...\n...składającym się z wielu wierszy!");

W oknie wygląda to następująco:

Wygląd komponentu "JTextArea" w "Swing"

Wygląd komponentu "JTextArea" w bibliotece "Swing".

DEFINIOWANIE ZDARZENIA PO EDYCJI TREŚCI WIELOWIERSZOWEGO POLA TEKSTOWEGO

"JTextArea" jak najbardziej wspiera te same zdarzenia zmiany zawartości i postępujemy identycznie, jak przy "JTextField 💪".

JCHECKBOX

Oprócz "tekstowych" spraw, na komponenty w "Swing" sprowadza się jeszcze pole wyboru, czyli "ptaszek" (ang. checkbox). Chodzi o przełączanie na "prawdę" lub "fałsz" ⏏️. Dzięki temu możemy "zerojedynkowo" kontrolować stan włączenia/wyłączenia i potem uzależniać działanie czegoś innego od aktualnej wartości 💡.

TWORZENIE POLA WYBORU

Przykładowy konstruktor ukazuje utworzenie pola wyboru z przypisanym łańcuchem znaków wyświetlanym obok ptaszka i domyślną wartością (włączone czy wyłączone) 👇:

var checkbox = new JCheckBox("Jestem polem wyboru", true);

Po dodaniu do ramki możemy ujrzeć nasze pole wyboru, które domyślnie przyjmuje następujący kształt:

Wygląd komponentu "JCheckBox" w "Swing"

Wygląd komponentu "JCheckbox" w bibliotece "Swing".

DEFINIOWANIE ZDARZENIA PO PRZEŁĄCZENIU STANU W POLU WYBORU

Aby podłączyć jakieś instrukcje w momencie kliknięcia pola wyboru i zmiany stanu wartości (wł./wył.), wystarczy skorzystać z metody "addActionListener", dokładnie jak przy "JButton" ℹ️. Tak jak wtedy, możesz to zrobić przez klasę wewnętrzną, a jeszcze lepiej to zrobić przez wyrażenie lambda, które jest dużo prostsze w budowie 👇:

checkbox.addActionListener(event -> System.out.println(checkbox.isSelected()));
JSLIDER

Kolejny komponent na liście to "JSlider" czyli po polsku "suwak" 🤐. Możliwość kontroli wartości liczbowej "jeżdżąc" paskiem od lewej do prawej.

TWORZENIE SUWAKA

Najbardziej podstawowy konstruktor nie wymaga jakichkolwiek parametrów i spowoduje utworzenie suwaka dopuszczającego wartość od 0 do 100 przy wartości początkowej równej 50 👇:

var slider = new JSlider();

W efekcie czego mamy taki oto suwaczek, czyli tytułowy "JSlider":

Wygląd komponentu "JSlider" w "Swing"

Wygląd komponentu "JSlider" w bibliotece "Swing".

Uprzedzając pytanie: sam suwak nie zapewnia "licznika" aktualnej wartości. Należy dołożyć osobno obiekt "JLabel" i za pomocą podpięcia pod zdarzenie zmiany wartości, aktualizować tekst co każde "przesunięcie".

RODZAJE KONSTRUKTORÓW

"JSlider" możemy utworzyć na nieco bardziej "barwną" liczbę sposobów 🖌️.

Za pomocą poniższego zapisu, możesz zrobić suwak w orientacji pionowej 👇:

var slider = new JSlider(SwingConstants.VERTICAL);

Używamy w tym miejscu "SwingConstants" - wbudowanego typu wyliczeniowego do określania orientacji poziomej (domyślnie) lub pionowej ⭐. Ponieważ za tym kryje się liczba całkowita "int", można wstawiać inne (niekoniecznie pasujące) wartości, jednak nie radzę - i to "IntelliJIDEA" też będzie Ci odradzał ⚠️! Lepiej korzystać z określonych wcześniej stałych ✅.

Inny konstruktor umożliwia definiowanie zakresu i początkowej wartości 🔥 - składa się on z 3 parametrów:

  1. wartość minimalna,
  2. wartość maksymalna,
  3. wartość początkowa.

Oto przykład:

var slider = new JSlider(0, 10, 1);

To przypisze zakres: <0; 10> i nada wartość początkową równą 1.

DEFINIOWANIE ZDARZENIA PO ZMIANIE WARTOŚCI W SUWAKU

Postępujemy tak samo, jak przy przycisku ("JButton") i polu wyboru ("JCheckBox") - jedyna różnica jest w nazwie ℹ️. Metoda "addChangeListener" odpowiada za zdarzenie zmiany wartości podczas przeciągania suwaka. Znowu, dwa sposoby - klasa wewnętrzna bądź wyrażenie lambda 👇:

slider.addChangeListener(event -> System.out.println(slider.getValue()));
JLIST

Kolejny z komponentów podstawowych, to lista z pozycjami do wyboru 🔢. Do tego służy "JList". Tu wyjątkowo, aby ujrzeć listę w oknie, trzeba wykonać 2 kroki 2️⃣.

TWORZENIE LISTY Z POZYCJAMI

Po pierwsze, utworzyć "DefaultListModel", czyli listę z pozycjami. Tworzymy obiekt i dodajemy do niego pozycje, jakie się mają znaleźć na liście 👇:

var defaultListModel = new DefaultListModel<String>();

defaultListModel.addElement("JLabel");
defaultListModel.addElement("JButton");
defaultListModel.addElement("JTextField");
defaultListModel.addElement("JTextArea");
defaultListModel.addElement("JCheckbox");
defaultListModel.addElement("JSlider");
defaultListModel.addElement("JList");

Dopiero wtedy, mając tak przygotowaną listę, wprowadzamy ją jako parametr do instancji "JList":

var list = new JList<>(defaultListModel);

Powyższe operacje spowodują ukazanie się oto takiej listy ze wszystkimi zdefiniowanymi w modelu elementami:

Wygląd komponentu "JList" w "Swing"

Wygląd komponentu "JList" w bibliotece "Swing".

DEFINIOWANIE ZDARZENIA PO ZMIANIE ZAZNACZONEJ POZYCJI W LIŚCIE

Gdyby Cię interesowało przechwycenie zdarzenia zmiany zaznaczonej pozycji w liście, to korzystasz z metody "addListSelectionListener". Zasada jest taka sama, jak w każdym poprzednio wymienionym komponencie - tworzysz sobie albo klasę wewnętrzną, albo sięgasz po wyrażenie lambda, które oszczędzi Ci długiego pisania 😅:

list.addListSelectionListener(event -> {
	if(!list.getValueIsAdjusting()) {
		System.out.println(list.getSelectedIndex());
	}
});

Tutaj wyjątkowo musimy zrobić pewien "myk", który powoduje podwójne wykonywanie instrukcji ⚠️! Wszelkie instrukcje umieszczamy w warunku:

!list.getValueIsAdjusting()

To ma na celu zapobiec reagowaniu na sytuację samego naciśnięcia dowolnej pozycji w liście przy użyciu lewego przycisku myszy. Nam przyda się jedynie moment puszczenia przycisku, czyli to co robimy automatycznie podczas klikania myszką w dowolne miejsce na monitorze 💻.

JCOMBOBOX

Ostatnie zdania na temat odmiany listy "zwykłej" - listy rozwijanej. Tutaj Java robi psikusa i to nosi zupełnie inną nazwę, niż jaka mogła Ci przyjść do głowy 😜. Lista rozwijana, to w "Swing" nazywa się "JComboBox" 😱.

TWORZENIE LISTY ROZWIJANEJ

Jeżeli chodzi o "JComboBox", tutaj również musimy najpierw utworzyć model z danymi. Tym razem posługujemy się "DefaultComboBoxModel", natomiast dodajemy wartości w taki sam sposób, co poprzednio 👇:

var defaultComboBoxModel = new DefaultComboBoxModel<String>();

defaultComboBoxModel.addElement("JLabel");
defaultComboBoxModel.addElement("JButton");
defaultComboBoxModel.addElement("JTextField");
defaultComboBoxModel.addElement("JTextArea");
defaultComboBoxModel.addElement("JCheckbox");
defaultComboBoxModel.addElement("JSlider");
defaultComboBoxModel.addElement("JList");
defaultComboBoxModel.addElement("JComboBox");

następnie tworzymy obiekt samej listy rozwijanej i wprowadzamy do niego nasz model:

var comboBox = new JList<>(defaultComboBoxModel);

W efekcie czego, nasze wysiłki dadzą nam taki efekt:

Wygląd komponentu "JComboBox" w "Swing"

Wygląd komponentu "JComboBox" w bibliotece "Swing".

Normalnie cała lista jest ukryta i elementy odkrywają się dopiero po kliknięciu strzałki po prawej stronie ✅.

DEFINIOWANIE ZDARZENIA PO WYBRANIU POZYCJI Z LISTY ROZWIJANEJ

Metoda "addActionListener" pozwala na określenie co ma się wydarzyć po wybraniu dowolnej pozycji z listy ▶️. Ten sam sposób, co pokazano wcześniej (wyrażenie lambda) 👇:

comboBox.addActionListener(event -> System.out.println(comboBox.getSelectedIndex()));

Małe wtrącenie na temat słowa kluczowego "var" - ono pozwala Tobie uniknąć każdorazowego wprowadzania ręcznie typu danych tworzonej zmiennej. O ile kompilator jest w stanie wywnioskować typ na podstawie informacji "stojących obok", możesz dzięki temu uprzyjemnić sobie pisanie ❤️!


Takie oto występują podstawowe komponenty w "Swing" 👍.

NASTĘPNY ARTYKUŁ: Układ w Swing. Konfiguracja pozycjonowania komponentów