Biblioteka "Swing" umożliwia rysowanie dowolnych kształtów geometrycznych na ekranie 🎨. Czy to linia, czy to kółko, okrąg, prostokąt, obrazek pobrany z pliku 📁. Dzięki poznaniu metody "paintComponent" występującej w bibliotece "Swing", to wszystko staje się możliwe! Trzeba jednak obchodzić się z nią inaczej, niż przy całej reszcie innych metod 😲! Dalszy ciąg zostawiam w środku, dla zainteresowanych 🚧.
"PAINTCOMPONENT" W "SWING", CZYLI RYSOWANIE Z NALEŻYTĄ OSTROŻNOŚCIĄ
Zaczniemy wątek od wyjaśnienia co to jest, a potem na co trzeba uważać.
O SAMEJ METODZIE ZDAŃ KILKA
Wspomniana metoda odpowiedzialna jest za rysowanie elementów graficznych w obrębie panelu 🎨. Ponadto, samodzielnie ustala kiedy trzeba ponownie przerysować cały panel ✅. Z tego względu, nie powinniśmy sami wywoływać metody "paintComponent" ⚠️!!! Ona jest wywoływana samoistnie "pobudzając do życia" operacje niskopoziomowe, tak aby ponownie odświeżyć i wyświetlić obraz tylko, gdy potrzeba 🔔. Powodem jest sam proces rysowania.
Domyślnie, metoda rysuje w taki sposób, jakby to była jedna klatka 🎬. Jakby obecną chwilę "zatrzymał w czasie" i rozrysował wszystko to, co zostało w niej zawarte. Obraz składający się z pojedynczych pikseli jest statyczny i zostaje w takiej samej postaci, dopóki nie wymaga ponownego przerysowania pod wpływem jakiegoś zdarzenia 🚨.
Na przykład rozciągnięcie okna jest ważnym powodem do tego, że obraz trzeba ponownie przerysować, gdyż współrzędne poszczególnych elementów graficznych są zależne od szerokości okna. Oczywiście jest wiele innych przykładów jakie są dla systemu argumentem do ponownego wywołania metody "paintComponent" w "Swing", natomiast nie ma sensu ich wszystkich cytować 😄.
PRZYKŁAD KODU ŹRÓDŁOWEGO
Rzućmy okiem na przykładowy kod rysujący niebieską elipsę wewnątrz panelu "JPanel" 💙. Ujmując prostymi słowy, "JPanel" to osobny komponent, który można "nałożyć" na obiekt typu "JFrame", także nie utożsamiaj ze sobą tych obiektów - "ramka" i "panel" to nie są synonimy 😄!
KLASA "MAIN"
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(MainJFrame::new);
}
}W klasie uruchomieniowej "Main", jak zwykle mamy tylko wywołanie konstruktora klasy typu "JFrame". "Opakowujemy" to wywołanie w metodę "invokeLater" ze "SwingUtilities" ze względu na bezpieczeństwo (możesz poszukać informacji w innych źródłach na ten temat 🙂) ➕.
KLASA "MAINJFRAME"
import java.awt.*;
import javax.swing.*;
public class MainJFrame extends JFrame {
private final MainJPanel mainJPanel = new MainJPanel();
public MainJFrame() {
configureFrame();
addPanelToFrame();
}
private void configureFrame() {
setTitle("Rysowanie przez metodę paintComponent w Swing");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(false);
setSize(512, 512);
setLocationRelativeTo(null);
}
private void addPanelToFrame() {
getContentPane().add(BorderLayout.CENTER, mainJPanel);
}
}W tej klasie mamy najwięcej instrukcji. Przypomnę, że "JFrame" (ramka) to ta część, która jest całym "oknem", razem z ramką dookoła z opcjami do minimalizacji, maksymalizacji i zamknięcia okna 😊. Jedyna zmienna prywatna ("mainJPanel"), to jest obiekt panelu, który zostanie dodany do ramki.
W konstruktorze, mamy wywołania dwóch metod. Pierwsza z nich odpowiednio konfiguruje ramkę: jej rozmiar, właściwości, pozycję, tytuł i reakcję po jej zamknięciu "krzyżykiem" w prawym górnym rogu. Poniżej zostawiam szczegóły odnośnie kilku mniej oczywistych zapisów 👇:
- "setDefaultCloseOperation" określa jak ma zachować się sam proces w momencie zamknięcia okna (jeżeli chcesz, aby zamknięcie okna jednocześnie zamykało cały proces, to musisz ustawić na "JFrame.EXIT_ON_CLOSE" - domyślnie przyjmuje "JFrame.HIDE_ON_CLOSE"),
- "setVisible" ustawione na "true", sprawi, że okno będzie widoczne (bo domyślnie będzie zminimalizowane - kolejne "gotcha!" języka Java 😏!),
- "setLocationRelativeTo" ustawia pozycję okna według podanego w parametrze komponentu (korzystam tutaj z takiego "myka", że wpisując wartość "null", zostanie wycentrowane na środku ekranu 💡).
A druga metoda po prostu dodaje panel do ramki ✔️. Nie robimy tego przez "add" dostępne bezpośrednio z poziomu klasy "JFrame" (niestety 🙂), tylko odwołujemy się do niej przez wywołanie "getContentPane". "BorderLayout.CENTER" oznacza, że panel zostanie wstawiony wewnątrz centralnego regionu układu "BorderLayout", który taki jest domyślnie ustawiony w "JFrame".
KLASA "MAINJPANEL"
import java.awt.*;
import javax.swing.*;
public class MainJPanel extends JPanel {
@Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.setColor(Color.BLUE);
graphics.drawOval(16, 16, 480, 450);
System.out.println("Rysowanie zakończone.");
}
}I ostatnia klasa dla samego panelu: "MainJPanel". Ona z kolei dziedziczy po "JPanel". Tutaj mamy jedynie przesłonięcie metody "paintComponent" (przesłanianie w Javie poznasz po zapisie "@Override" 👍). Ona jest już zdefiniowana wewnątrz klasy i posiada modyfikator dostępu "protected" (pozwala na uzyskanie dostępu do składowej przez klasy potomne) 💡. Właśnie tutaj wstawiamy sobie rysowanie okręgu 🎨. Prześledźmy instrukcje po kolei.
Najpierw korzystamy z wywołania pierwotnej "wersji" metody używając słowa "super". Tutaj czai się kolejny "ukryty powód" jaki nam serwuje język Java 😏. Ta instrukcja zapobiega "nakładaniu się" kolejnych klatek rysowania.
EFEKT "BRAKU CZYSZCZENIA"
Kiedy rysowane obiekty mają się poruszać po ekranie, to będą zostawiały po sobie ślad z poprzedniej pozycji 🤯. W konsekwencji, zobaczysz efekt "rozjeżdżania się" obiektów w ruchu:
![]() |
"Efekt uboczny" rysowania w metodzie "paintComponent" w "Swing" bez czyszczenia klatki. Przesuwanie okręgu przy inkrementacji osi X i Y co klatkę.
Aby się przed tym uchronić, trzeba po prostu wyczyścić zawartość poprzednich klatek 🧹. Albo korzystamy z "super.paintComponent" (pozwala zastąpić metodę, która musiałaby "posprzątać" po tej poprzedniej klatce), albo rysujemy prostokąt na cały ekran metodą "clearRect", wstawioną przed instrukcjami rysującymi 👇:
graphics.clearRect(0, 0, getWidth(), getHeight());W obu przypadkach skutecznie pozbywamy się "efektu braku czyszczenia" ✅.
RYSOWANIE ELIPSY
Wracamy do tłumaczenia instrukcji 😅. Kolejną z nich jest ustawianie koloru rysowania ("setColor"). Robimy to przy pomocy parametru typu "Graphics". "paintComponent" w "Swing" działa w sposób podobny do staromodnych podejść, w których osobno ustawiało się kolor i potem osobno wywoływało metodę rysującą 🎨. Rysowanie według odpowiedniego koloru polega na wywołaniu metody ustawiającej kolor, a kolejne metody rysujące będą rysowały figury w podanym wcześniej kolorze ℹ️. Nas interesuje niebieski, więc korzystamy z wartości typu wyliczeniowego "Color.BLUE" 🔵.
Możesz też podać własny niestandardowy kolor korzystając z konstruktora 😊. To jest przykład 👇:
new Color(255, 127, 63, 255)Tak dochodzimy w końcu do instrukcji "drawOval", która pozwala na rysowanie obwodu elipsy 😊. Przyjmuje 4 parametry 👇:
- pozycję w osi X,
- pozycję w osi Y,
- szerokość,
- wysokość.
Jeżeli wkleisz sobie powyższe klasy i uruchomisz program, to Twoim oczom ukaże się taka oto elipsa 👇:
![]() |
Wygląd narysowanej elipsy na nałożonym komponencie "JPanel" przy użyciu metody "paintComponent" w "Swing".
Jeżeli chcesz "wypełnioną" elipsę od środka, zamień "drawOval" na "fillOval" ℹ️. W taki sam sposób możesz narysować następujące dostępne figury w "Swing" 👇:
- Arc (łuk),
- Line (linia prosta),
- Oval (elipsa),
- Rect (prostokąt),
- Image (obrazek),
- 3DRect (prostokąt symulujący trójwymiarowość poprzez pogrubienie niektórych boków),
- String (łańcuch znaków),
- Polygon (wielokąt),
- Polyline (linia ciągła powstała z wielu punktów),
- RoundRect (prostokąt z zaokrąglonymi rogami).
Po więcej szczegółów na każdą z możliwości, skieruj się już bezpośrednio do dokumentacji Javy 📖.
ODŚWIEŻENIE OBRAZU RĘCZNIE METODĄ "REPAINT"
Jeżeli chcesz, aby cały panel został ponownie przerysowany poprzez wywołanie metody, to wbrew intuicyjnemu podejściu, wystarczy że wywołasz inną metodę przeznaczoną do tego celu: "repaint" 💡. Nie przyjmuje żadnych argumentów i pozwala "nakazać" metodzie "paintComponent" narysować wszystko jeszcze raz. Nie ma to jak kolejne zaskoczenie po stronie języka Java 😅!
Materiału o "paintComponent" w "Swing" nadszedł kres 🏁. Tyle informacji wystarczy na ten temat 😁!
NASTĘPNY ARTYKUŁ: Graphics2D w Swing. Ukryte możliwości po rzutowaniu

