Java - aplikacje okienkowe - Programowanie

advertisement
Okienko
Zdefiniujemy własną klasę dla obiektu - okienka naszej aplikacji.
Wyprowadzimy tę klasę jako rozszerzenie - klasę potomną - z klasy bazowej JFrame zdefiniowanej
w pakiecie javax.swing.JFrame.
Nasza klasa będzie wówczas od razu wyposażona w standardowe elementy okienka, w tym: ramkę,
pasek tytułowy, menu systemowe i przyciski sterujące min-max-zamknij. Wykorzystując metody
odziedziczone po klasie JFrame można nadać okienku żądany tytuł, rozmiar, położenie.
import javax.swing.*;
public class Apok {
public static void main(String[] args) {
Okno ok = new Okno();
ok.setVisible(true);
}
}
class Okno extends JFrame {
Okno() {
// konstruktor klasy Okno
setSize(200,150);
setLocation(100,100);
// lokalizacja względem początku ekranu;
setTitle("Java jest OK");
setDefaultCloseOperation(EXIT_ON_CLOSE); // 3 - zamknięcie okna
zamyka VMJava
}
}
Całe wnętrze okienka jest domyślnie obiektem klasy Container. Aby zmienić właściwości wnętrza
okienka (na przykład kolor tła) lub umieścić w okienku jakieś komponenty (na przykład przyciski,
pola tekstowe) - trzeba odwołać się do tego kontenera. Dostęp do niego zapewnia metoda
getContentPane() klasy JFrame.
Przykład: aby zmienić kolor wnętrza okienka należałoby dodać do
powyższego kodu import pakietów definiujących klasy: Color i
Contenair:
import java.awt.Color;
import java.awt.Container;
a w klasie Okno zadeklarować kontener o nazwie np wnetrze i w
konstruktorze klasy Okno ustawić kolor tła:
Container wnetrze = this.getContentPane();
wnetrze.setBackground(Color.yellow);
Wewnętrzny kontener klasy JFrame ma ograniczone możliwości (m.in. nie umożliwia obramowania).
Dlatego programiści najczęściej definiują wewnątrz okienka JFrame dodatkowy kontener - panel klasy
JPanel i dopiero wewnątrz niego umieszczają potrzebne komponenty.
class Okno extends JFrame {
JPanel p = new JPanel();
Okno() {
setSize(400,300);
setLocation(100,100);
setTitle("Java jest OK");
setDefaultCloseOperation(EXIT_ON_CLOSE);
p.setBackground(Color.yellow);
add(p);
kontenera klasy Okno
}
}
// ustawia kolor tła
// dodaje panel p do wewnętrznego
Komponenty wewnątrz okienka
W okienku można umieścić komponenty - obiekty różnych klas. Wykorzystamy wybrane klasy z
biblioteki swing zdefiniowane w pakiecie javax.swing.JComponent:



JTextField - pole tekstowe do wyświetlania danych i do wprowadzania danych z klawiatury
JLabel - etykietka do wyświetlania tekstu
JButton - przycisk
Rozmieszczeniem komponentów w kontenerze/panelu zarzadza jego metoda setLayout(). Przyjmuje
ona jako parametr nowy obiekt LayoutManager - zarządca rozmieszczenia. Zarządca może być różnej
klasy. Najczęściej używane klasy LayoutManager-ów :




FlowLayout() - komponenty rozmieszczane są kolejno w wierszu
GridLayout(n,m) - komponenty rozmieszczane są w siatce o n wierszach i m kolumnach
BoxLayout - pozwala na składanie okienka z niezależnych "pudełek" zawierajacych
komponenty
null - każdy komponenty musi być wyposażony we własne parametry decydujące o jego
położeniu i rozmiarach
Komponenty dodajemy do kontenera/panelu jego metodą add(komponent)
W przykładach poniżej stosowane sa dwie etykietki, jedno pole tekstowe i jeden przycisk służący do
pobierania danych.
Uwaga! Przycisk w tych programach na razie nie reaguje jeszcze na kliknięcie. Dopiero w następnym
punkcie wyposażymy program w
obsługę zdarzenia.
Przykład: zarządca klasy
FlowLayout
import javax.swing.*;
import java.awt.FlowLayout;
public class Apok {...}
// klasa wymaga importu
// jak w programie powyżej
class Okno extends JFrame {
JPanel p;
JLabel pytanie;
// składniki okna
JTextField dana;
JLabel powitanie;
JButton wez;
Okno() {
// konstruktor okna
setSize(400,100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
p = new JPanel();
p.setLayout(new FlowLayout(8));
komponentami
// odstęp 8px między kolejnymi
pytanie = new JLabel("Jak Ci na imię ?");
powitanie = new JLabel("Witaj");
dana = new JTextField(10);
wez = new JButton("weź dane");
p.add(pytanie);
p.add(dana);
p.add(wez);
p.add(powitanie);
add(p);
}
}
Przykład: zarządca klasy GridLayout
import javax.swing.*;
import java.awt.GridLayout;
importu
public class Apok {...}
// klasa wymaga
// jak w programie powyżej
class Okno extends JFrame {
JPanel p;
JLabel pytanie;
JTextField dana;
JLabel powitanie;
JButton wez;
// składniki okna;
Okno() {
// konstruktor okna
setSize(200,150);
setDefaultCloseOperation(EXIT_ON_CLOSE);
p = new JPanel();
p.setLayout(new GridLayout(4,1));
// 4 wiersze 1 kolumna
pytanie = new JLabel("Jak Ci na imię ?");
powitanie = new JLabel("Witaj");
dana = new JTextField(10);
wez = new JButton("weź dane");
p.add(pytanie);
p.add(dana);
p.add(wez);
p.add(powitanie);
add(p);
}
}
Obsługa kliknięcia przycisku
Aby przycisk zareagował na zdarzenie, trzeba dołączyć do niego "nasłuchiwacz zdarzeń" ActionListener.
Jest to abstrakcyjna klasa, która ma zadeklarowaną metodę obsługi zdarzeń actionPerformed(), ale
bez jej implementacji (to znaczy: z pustym ciałem metody). Programista sam musi wypełnić ciało
metody actionPerformed poleceniami, które mają być wykonane w razie wystąpienia zdarzenia
skierowanego do wybranego komponentu.
W naszym przykładzie program po kliknięciu przycisku ma odczytać tekst umieszczony w
komponencie dana i dołączyć ten tekst do pozdrowienia wyświetlanego w komponencie powitanie.
Do odczytania tekstu i umieszczenia nowego tekstu wykorzystamy metody klasy TextField:


getText() - pobiera zawartość pola tekstowego
setText(String) - umieszcza w polu tekstowym wartość typu String
Do kodu programu trzeba zaimportować pakiet java.awt.event.* w którym zdefiniowane są narzędzia
do obsługi zdarzeń.
Cały program prezentuje się następująco:
import javax.swing.*;
import java.awt.GridLayout;
import java.awt.event.*;
public class Apok1 {
public static void main(String[] args) {
Okno ok = new Okno();
ok.setVisible(true);
}
}
class Okno extends JFrame {
JPanel p = new JPanel();
JLabel pytanie;
JTextField dana;
JLabel powitanie;
JButton wez;
Okno() {
// konstruktor
setSize(200,150);
setDefaultCloseOperation(3);
p = new JPanel();
p.setLayout(new GridLayout(4,1));
pytanie = new JLabel("Jak Ci na imię ?");
dana = new JTextField(10);
powitanie = new JLabel("Witaj");
wez = new JButton("weź dane");
wez.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent zdarzenie) {
powitanie.setText("Witaj "+dana.getText());
}
}
);
p.add(pytanie);
p.add(dana);
p.add(wez);
p.add(powitanie);
add(p);
}
}
Wiele przycisków i jeden ActionListener
Interfejs graficzny programu może zawierać wiele przycisków o różnej funkcjonalności. W takiej
sytuacji programista używa interfejsu ActionListener, obsługującego wszystkie przyciski jedną i tą
samą metodą actionPerformed(). Metoda actionPerformed() "sama" wykryje który przycisk jest
źródłem zdarzenia - zrobi to przy pomocy metody getSource() wbudowanej w zdarzenie. Metoda
getSource() zwraca nazwę obiektu wywołującego zdarzenie.
Program poniżej utrwala i rozwija polecenia służące do
budowania okienka.
Okienko zawiera dwa panele o różnych kolorach tła,
obramowane.
Etykietka JLabel w drugim panelu, zawierająca treść
powitania, ma rozmiar ustawiony na stałe (domyślnie
obiekt typu JLabel dopasowywałby swoją wielkość do
treści którą ma wyświetlać) oraz czcionkę wybraną przez
programistę.
Przycisk Weź dane ma za zadanie odczytać dane wpisane przez użytkownika do pól tekstowych i
wyświetlić uprzejme powitanie w dolnym panelu. Treść powitania zależy od podanego wieku: osoby
poniżej 18 lat wita słowem "dziecino",dla osób starszych treść powitania jest inna.
Wyjątek Exception ex obsłuży sytuację, gdy użytkownik nieprawidłowo poda wiek (gdy poda
łańcuch znaków który nie da się przetłumaczyć na wartość liczbową, na przykład poda litery)
Przycisk Czyść dane ma za zadanie usunąć zawartość pól tekstowych służących do wpisywania
danych.
import javax.swing.*;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.event.*;
import javax.swing.border.*;
import java.awt.Dimension;
etykiety
import java.awt.Font;
import java.awt.Color;
// do klasy bazowej JFrame
// do rozmieszczenia komponentów
// do obsługi zdarzeń
// do obramowania panelu
// do ustalenia preferowanych wymiarów
// do wyboru czcionki
class Zdarzenia {
public static void main(String[] args) {
Okno ok = new Okno();
ok.setVisible(true);
}
}
class Okno extends JFrame implements ActionListener {
JPanel p1,p2;
// składniki okna : 2 panele
JLabel lImie, lWiek, powitanie;
// 3 etykietki
JTextField tImie, tWiek;
// 2 pola tekstowe
JButton bWez, bCzysc;
// 2 przyciski
Okno() {
// konstruktor okna
setSize(260,200);
setTitle("2 przyciski demo");
setLocation(100,100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLayout(new FlowLayout(20));
od krawędzi
// wewn.kontener, 20px odstepu
p1 = new JPanel(new GridLayout(3,2));
// pierwszy panel - dla
danych
p1.setBorder(BorderFactory.createTitledBorder("Dane"));
p1.setBackground(Color.yellow);
lImie
lWiek
tImie
tWiek
=
=
=
=
new
new
new
new
JLabel("Jak Ci na imię ?");
JLabel("Ile masz lat ?");
JTextField(10);
JTextField(10);
bWez = new JButton("weź dane");
bCzysc = new JButton("czyść dane");
bWez.addActionListener(this);
do przycisków
bCzysc.addActionListener(this);
// tworzy przyciski
// przyłącza ActionListener
p1.add(lImie);
// dodaje komponenty do panelu p1
p1.add(tImie);
p1.add(lWiek);
p1.add(tWiek);
p1.add(bWez);
p1.add(bCzysc);
add(p1);
// na końcu dodaje panel p1 do wewnętrznego
kontenera klasy Okno
p2 = new JPanel(new GridLayout(1,1));
// drugi panel - p2 dla powitania
p2.setBorder(BorderFactory.createTitledBorder("Powitanie"));
p2.setBackground(Color.pink);
powitanie = new JLabel("");
powitanie.setPreferredSize(new Dimension(230,20));
powitanie.setFont(new Font("Monotype Corsiva",1,16));
p2.add(powitanie);
add(p2);
// dodaje panel p1 do wewnętrznego
kontenera klasy Okno
}
// koniec
konstruktora okna
public void actionPerformed(ActionEvent e) {
if (e.getSource()== bWez) {
// obsługa kliknięcia przycisku:
Weź dane
try{
int wiek = Integer.parseInt(tWiek.getText());
if (wiek<18) powitanie.setText("Witaj "+tImie.getText()+"
dziecino");
else powitanie.setText("Witaj "+tImie.getText()+" chodzmy
na piwo");
} catch (Exception ex) { powitanie.setText("bledne dane"); }
}
if (e.getSource()== bCzysc) {
Czyść dane
tImie.setText("");
tWiek.setText("");
powitanie.setText("");
}
}
}
// obsługa kliknięcia przycisku:
// czyści pole tekstowe
// koniec obsługi zdarzeń
Ćwiczenie
Napisz program okienkowy który pobiera w polu tekstowym odległość w kilometrach. W odpowiedzi
na kliknięcie przycisku Oblicz program powinien przeliczyć podaną odległość na:




metry
stopy (1 stopa = 30,48 cm)
jardy (1 jard = 0,9144 metra)
mile morskie (1 mila morska = 1,85166 km)
wyświetlając wyniki w czterech kolejnych etykietkach poniżej przycisku.
Przycisk i okienka dialogowe JOptionPane
Klasa JOptionPane z pakietu javax.swing.JOptionPane umożliwia łatwe wyświetlanie okienek
dialogowych różnych typów przy pomocy metod:




showMessageDialog - komunikat informacyjny z przyciskiem potwierdzenia
showConfirmDialog - komunikat z wyborem przycisków yes/no/cancel
showOptionDialog - rozszerzenie dwóch powyższych typów
showInputDialog - zachęta do wprowadzenia odpowiedzi
Wywołania metod:
void showMessageDialog(null,"akuku")
// wersja najprostsza
void showMessageDialog(Component parent, Object treść, String tytuł, int typKomunikatu)
int showConfirmDialog(null,"akuku")
// wersja najprostsza
int showConfirmDialog( Component parent, Object treść, String tytuł, int typOpcji)
int showOptionDialog(Component parent, Object treść, String tytuł,
int typOpcji, int typKomunikatu, Icon icon, Object[] options, Object initialValue)
String showInputDialog( String pytanie)
wersja najprostsza
//
String showInputDialog( Component parent,
String pytanie, String wartośćDomyslna, int
typKomunikatu)
Parametry pobierane przez metody
showXxxDialog:
typKomunikatu:
ERROR_MESSAGE
INFORMATION_MESSAGE
WARNING_MESSAGE
QUESTION_MESSAGE
PLAIN_MESSAGE
typOpcji:
YES_OPTION
NO_OPTION
CANCEL_OPTION
OK_OPTION
CLOSED_OPTION
oraz ich kombinacje: YES_NO_OPTION,
YES_NO_CANCEL_OPTION
parent - komponent nadrzędny (rodzic). Jeśli
rodzic jest podany, to okienko dialogowe
zostanie wyświetlone w obszarze rodzica
(przykryje go częściowo). Jesli rodzic nie jest
podany (lub podano: null), to okienko
dialogowe zostanie wyświetlone w środku ekranu.
Przykładowy program
Okienko główne zawiera wyłącznie przycisk, który należy kliknąć aby uruchomić metodę
actionPerformed() interfejsu ActionListener.
import javax.swing.*;
import java.awt.event.*;
public class Mess implements ActionListener {
JFrame ok;
public static void main(String[] args){
Mess db = new Mess();
}
public Mess() {
ok = new JFrame("Dialogowe okienko komunikatu");
JButton button = new JButton("Kliknij mnie");
button.addActionListener(this);
ok.add(button);
ok.setSize(300, 200);
ok.setVisible(true);
ok.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e){
// komunikat ze standardową treścią napisów na przciskach
JOptionPane.showConfirmDialog(ok,"Czy mnie lubisz?",
"tytuł - pytanie", JOptionPane.YES_NO_CANCEL_OPTION);
// komunikat z własną treścią napisów na przyciskach, zwraca wartość
typu int:
// 0 - jeśli wybrano pierwszy przycisk, 1 - jeśli drugi itd
Object[] options = { "DALEJ", "ANULUJ" };
int x= JOptionPane.showOptionDialog(null,
"Czy chcesz kontunuować?", "tytuł okienka",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
null, options, options[0]);
if (x==0) {
// pobranie danej z okienka dialogowego
String imie = JOptionPane.showInputDialog(null,"Podaj imię",
"tytuł - pobieranie danej",JOptionPane.QUESTION_MESSAGE);
// zwykły komunikat
JOptionPane.showMessageDialog(ok,"Witaj "+imie,"tytuł komunikatu",
JOptionPane.INFORMATION_MESSAGE);
}
}
}
Więcej o obsłudze zdarzeń. Jak to działa ?
Zdarzenie jest obiektem, który opisuje zmianę stanu swojego źródła wywołaną na przykład:




kliknięciem przycisku
przesunięciem myszy
naciśnięciem klawisza
wyborem elementu z listy
Źródłem zdarzenia najczęściej jest mysz, klawiatura lub element interfejsu graficznego użytkownika
(przycisk, lista wyboru itp).
W Javie zdarzenia są delegowane:
1. źródło generuje zdarzenie
2. zdarzenie jest wysyłane do słuchacza
3. słuchacz wywołuje metodę która obsługuje zdarzenie
Słuchacz jest obiektem, który nasłuchuje - czeka na wystąpienie zdarzenia, po otrzymaniu informacji
o zdarzeniu obsługuje je i natychmiast powraca do stanu nasłuchu.
Słuchacz musi implementować interfejs dla określonego rodzaju zdarzeń, to znaczy
implementować metody które uruchomi w razie wystąpienia zdarzenia.
Interfejs - to zbiór nazw metod, bez ich definicji. Można go dodać do klasy aby rozszerzyć jej
możliwości. Interfejsy stosowane są w sytuacji, gdy takie same czynności muszą być dostępne w
grupie wielu niezależnych klas.
Jeśli obiekt będzie miał reagować na zdarzenia, to klasa która go definiuje musi implementować
słuchacza odpowiednich zdarzeń. Informację o implementacji wybranego interfejsu trzeba umieścić
w nagłówku definicji klasy:
class NazwaKlasy implements NazwaInterfejsu {
ciało klasy
}
Klasa musi implementować WSZYSTKIE metody wybranego interfejsu, nawet jeśli niektórych
z nich nie wykorzystuje. Ciała metod nie wykorzystanych pozostają puste.
Słuchacza trzeba przyłączyć do źródła aby mógł otrzymywać zawiadomienia o zdarzeniach
generowanych przez to źródło. Przyłączenia dokonuje się metodą źródła o nazwie podobnej do:
addTYPSŁUCHACZAListener, na przykład:



addActionListener
addMouseListener
addKeyListener
Metody przyłączające słuchacza pobierają parametr - nazwę klasy implementującej interfejs nasłuchu,
this - oznacza bieżącą klasę, ale można zdefiniować osobną klasę.
Słuchacza można odłączyc od źródła metodą removeTYPSŁUCHACZAListener.
Różne źródła mogą być przyłączone do tego samego słuchacza. Słuchacz rozpoznaje źródło na
podstawie informacji przekazywanej mu przez zdarzenie.
Wszystkie zdarzenia posiadają metodę getSource() typu Object. Zwraca ona nazwę obiektu – źródła,
które wywołało zdarzenia.
Narzędzia do obsługi zdarzeń są zawarte w pakiecie java.awt.event. Trzeba importować ten pakiet.
Najczęściej wykorzystywane interfejsy
Interfejs ActionListener posiada tylko jedną metodę. Obsługuje ona zdarzenia generowane przez
dowolną akcję.
void actionPerformed(ActionEvent e) { }
Słuchacza należy przyłączyć do źródła metodą addActionListener().
Interfejs MouseListener do obsługi zdarzeń związanych z myszą posiada metody:
void mouseClicked(MouseEvent me) { } - kliknięcie myszą na obiekcie
void mouseEntered(MouseEvent me) { } - wejście myszy w obszar obiektu
void mouseExited(MouseEvent me) { } - wyjście myszy z obszaru obiektu
void mousePressed(MouseEvent me) { } - naciśnięcie przycisku myszy
void mouseReleased(MouseEvent me) { } - zwolnienie naciśniętego przycisku myszy
Słuchacza należy przyłączyć do źródła metodą addMouseListener().
Interfejs MouseMotionListener do obsługi zdarzeń związanych z ruchem myszy posiada metody:
void mouseDragged(MouseEvent me) { } - mysz przeciągana (poruszana z naciśniętym klawiszem)
void mouseMoved(MouseEvent me) { } - mysz poruszana (bez naciśniętego klawisza)
Słuchacza należy przyłączyć do źródła metodą addMouseMotionListener().
Klasa zdarzenia MouseEvent ma następujące najważniejsze elementy:




Component src - źródło zdarzenia
int type - typ zdarzenia (MOUSE_CLICKED - kliknięcie, MOUSE_DRAGGED przeciąganie, MOUSE_ENTERED – wejście do elementu, MOUSE_EXITED – wyjście z
elementu, MOUSE_MOVED - przesuwanie, MOUSE_PRESSED – naciskanie,
MOUSE_RELEASED - zwalnianie)
int x,y - współrzędne myszy
metody: int getX() oraz int getY() pozwalają pobrać współrzędne myszy w chwili zdarzenia
Interfejs KeyListener służy do obsługi klawiatury i posiada metody:
void keyPressed(KeyEvent ke) { } // klawisz jest naciśnięty
void keyReleased(KeyEvent ke) { } // klawisz jest zwolniony
void keyTyped(KeyEvent ke) { }
// znak jest wygenerowany
Słuchacza należy przyłączyć do źródła metodą addKeyListener().
Klasa zdarzenia KeyEvent ma następujące elementy:








Component src - źródło zdarzenia
int type - typ zdarzenia (KEY_PRESSED – klawisz naciśnięty, KEY_RELEASED – klawisz
zwolniony, KEY_TYPED – znak wygenerowany)
int code - kod naciśniętego klawisza
char ch - znak naciśniętego klawizsa
char getKeyChar() - pobranie znaku naciśniętego klawisza
int getKeyCode() - pobranie kodu naciśniętego klawisza
int getKeyLocation() - określa lokalizacje klawisza (pozwala odróznić np. lewy Shift od
prawego Shifta, klawisze cyfr na klawiaturze podstawowej od tych samych cyfr na klawiaturze
numerycznej)
String getKeyText(code) - opis tekstowy klawisza o podanym kodzie, np: Home, Shift, Space
Klawisze specjalne mają określony kod wirtualny: VK_ENTER, VK_ESCAPE, VK_CANCEL,
VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_SHIFT, VK_ALT.
Interfejs TextListener posiada tylko jedną metodę. Jest na wywoływana w sytuacji, gdy zmianie
ulegnie zawartość tekstu w źródle (polu tekstowym).
void textValueChanged(TextEvent e) { }
Słuchacza należy przyłączyć do źródła metodą addTextListener().
Adapter
Implementacja interfejsu nasłuchu wymaga umieszczenia w klasie implementującej wszystkich metod
interfejsu, nawet tych które nie są potrzebne w bieżącym programie. Można się od tego "wykręcić"
stosując Adapter. Klasa Adapter ułatwia programowanie obsługi zdarzeń. Dostarcza puste
implementacje wszystkich metod w danym interfejsie zdarzeń. Wystarczy dziedziczyć po klasie
odpowiedniego adaptera, implementując tylko te zdarzenia które nas interesują. Przykłady będą dalej
:)
Źródło: http://www.math.hosted.pl/math_2/programowanie_obiektowe/wyklad12.pdf
Obsługa naciskania klawiszy – KeyListener
Przypomnijmy: obsługa klawiatury wymaga zastosowania interfejsu KeyListener.
Interfejs KeyListener posiada metody, które wszystkie musimy zaimplementować, choć
wykorzystamy tylko KeyPressed:



void keyPressed(KeyEvent ke) { } // klawisz jest naciśnięty
void keyReleased(KeyEvent ke) { } // klawisz jest zwolniony
void keyTyped(KeyEvent ke) { }
// znak jest wygenerowany
Słuchacza należy przyłączyć do źródła metodą addKeyListener().
Wykorzystamy następujące metody klasy zdarzenia KeyEvent:



char getKeyChar() - pobranie znaku naciśniętego klawisza
int getKeyCode() - pobranie kodu naciśniętego klawisza
String getKeyText(code) - opis tekstowy klawisza o podanym kodzie, np: Home, Shift, Space.
Przykładowy program reaguje na naciśnięcie
klawisza.
W etykietce o nazwie labKod podaje kod klawisza
(nie kod znaku!!!), a w etykietce labText - opis
klawisza: znak odpowiadający klawiszowi (litera,
cyfra) lub jego tekstowy opis (Shift, Enter itp).
W okienku nie zastosowano żadnego menadżera rozkładu: setLayout(null). Każda z etykietek ma
indywidualnie określone położenie w okienku przy pomocy metody setBound(). Metoda ta pobiera
parametry - współrzędne dwóch przeciwległych wierzchołków obiektu: lewego górnego i prawego
dolnego. Jeśli zawartość obiektu, tu: etykietki, nie mieści się w podanych rozmiarach, to etykietka
domyślnie rozszerza się w prawo tyle ile potrzebuje.
W odpowiedzi na naciskanie klawiszy strzałek etykietka z opisem porusza się w kierunku wskazanym
przez strzałkę (realizacja - przez zmianę parametrów metody setBound), a w razie wyjścia etykietki
poza krawędź okienka - powraca ona z przeciwnej strony okienka.
Klawisze strzałek mają odpowiadające im kody:
37 - w lewo
38 - do góry
39 - w prawo
40 - na dół
Oto kod programu:
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
class Klaw {
static public void main (String[] args) {
Okno ok = new Okno();
ok.setVisible(true);
}
}
class Okno extends JFrame implements KeyListener {
JLabel labKod, labText;
int x=80, y=50;
// współrzędne początkowe etykietki z
kodem
int width=300, height=150;
// rozmiary okienka
Okno () {
setTitle("Kod naciśniętego klawisza");
setSize(width,height);
setDefaultCloseOperation(3);
setLayout(null);
labKod = new JLabel(" ");
klawisza
labKod.setBounds(x,20,x,20);
add(labKod);
labText = new JLabel(" ");
klawisza
labText.setBounds(x,y,x,y);
add(labText);
// etykietka do wyświetlania kodu
// etykietka do wyświetlania opisu
addKeyListener(this);
}
public void keyPressed(KeyEvent e) {
int kod = e.getKeyCode();
labKod.setText(" "+kod);
String klawisz = e.getKeyText(kod);
labText.setText(klawisz);
// weź kod klawisza
// weź opis klawisza
if (kod == 37) { x=x-10; if (x<0) x=width;
labText.setBounds(x,y,x,y);} // w lewo
if (kod == 39) { x=x+10; if (x>width) x=0;
labText.setBounds(x,y,x,y);} // w prawo
if (kod == 38) { y=y-10; if (y<0) y=height;
labText.setBounds(x,y,x,y);} // w górę
if (kod == 40) { y=y+10; if (y>height) y=0;
labText.setBounds(x,y,x,y);} // w dół
}
public void keyReleased(KeyEvent e) { }
tym programie
public void keyTyped(KeyEvent e) { }
tym programie
// metoda nie wykorzystana w
// metoda nie wykorzystana w
}
PODSTAWY GRAFIKI. Klasy: Canvas i Graphics
Rysunek powinien powstawać w komponencie klasy Canvas. Jest to prostokątny czysty obszar, po
którym aplikacja może bazgrać, pisać, wklejać gotowe rysunki i wykonywać na nich różne operacje
graficzne. Klasa Canvas jest zdefiniowana w pakiecie java.awt.Canvas.
Można także utworzyć własny komponent rysunkowy bazujący na klasie JComponent.
W zasadzie możnaby bazgrać po każdym obiekcie wizualnym (JPanel, JLabel itp), ale nie powinno się
tego robić, tak jak nie powinno się bazgrać po ścianach, samochodach itp, bo to i bałagan i skutki
bywają nieprzewidywalne ;)
Do tworzenia rysunków służy klasa Graphics z pakietu java.awt.Graphics.
Klasa Graphics pełni dwie role:


Tworzy kontekst graficzny: kanwę rysunku zwiazaną z wybranym obiektem. Przypisuje mu
atrybuty: wymiary, kolor tła, kolor pierwszoplanowy, czcionkę.
Dostarcza metod do rysowania prostych figur geometrycznych, tworzenia napisów,
ładowania obrazu z pliku graficznego.
Aby program mógł coś rysować, trzeba udostępnić mu kontekst graficzny (obiekt klasy Graphics).
Następnie można odwoływać się do metod tego obiektu, aby rysować odcinki, łuki, wieloboki, elipsy.
Obiekt klasy Canvas (każdy inny obiekt wizualny też) ma wbudowaną metodę paint(Graphisc g),
która jest odpowiedzialna za rysowanie obrazka. Tę metodę należy nadpisać, tzn zdefiniować
samodzielnie jej ciało. Tu należy umieszczać polecenia rysowania czegokolwiek.
Metoda paint(Graphics g) jest wykonywana każdorazowo:




na starcie programu
gdy okno programu zostało zminimalizowane, a następnie przywrócone do normalnych
rozmiarów
gdy okno programu zostało przykryte innym oknem, a następnie wyciągniete na wierzch
gdy wywołano metodę repaint()
Układ współrzędnych kontekstu graficznego ma początek (0,0) w lewym górnym narożniku
kontekstu graficznego, oś X rośnie w prawo, oś Y rośnie w dół.
Metody rysujące proste figury geometryczne (XXX - zastąpisz nazwą figury geometrycznej)
drawXXX() - rysuje linię lub kontur figury zamkniętej linią o szerokości 1px
fillXXX() - rysuje figury zamknięte wypełnione kolorem pierwszoplanowym
Kolor pierwszoplanowy (linii, wypełnienia) należy ustawić metodą setColor(Color) przed
poleceniem rysowania.
Oto niektóre metody klasy Graphics:
drawLine(x1,y1,x2,y2) - odcinek łączący punkty o podanych współrzędnych typu int
drawRect(x,y,szerokość,wysokość) - prostokąt o lewym górnym narożniku (x,y)
drawRoundRect(x,y,szerokość,wysokość, szerokość_łuku, wysokość_Łuku) - prostokąt o
zaokrąglonych wierzchołkach
drawOval(x,y,szerokość,wysokość) - elipsa wpisana w prostokąt o lewym górnym narożniku
(x,y)
drawPolygon(x[],y[],n) - wielobok o n wierzchołkach, których wspórzędne podano w
tablicach x[] oraz y[] typu int
drawPolyLine(x[],y[],n) - linia łamana łącząca n punktów, których wspórzędne podano w
tablicach x[] oraz y[] typu int
drawArc(x,y,szerokosc,wysokosc, kąt_początkowy,kąt_końcowy) - łuk wpisany w prostokąt
drawImage(obraz, wymiary)
drawString(tekst,x,y) - tekst umieszczony lewym dolnym wierzchołkiem w (x,y), wcześniej
należy wybrać czcionkę metodą setFont(czcionka)









oraz niektóre metody rysujące zamknięte figury wypełnione (parametry jak powyżej):




fillRect(x,y,szerokość,wysokość)
fillOval(x,y,szerokość,wysokość)
fillPolygon(x[],y[],n)
fillArc(x,y,szerokosc,wysokosc,
kąt_początkowy,kąt_końcowy)
Przykładowy program
import javax.swing.*;
import java.awt.Graphisc;
public class Grafika extends JFrame{
public static void main(String[] args) {
Grafika ok = new Grafika();
ok.setTitle("Moja pierwsza j-grafika");
ok.setSize(300,200);
ok.setDefaultCloseOperation(EXIT_ON_CLOSE);
ok.add(new Kanwa());
ok.setVisible(true);
}
}
class Kanwa extends Canvas {
public void paint(Graphics g) {
g.setColor(Color.yellow);
g.fillRoundRect(30,30,190,100,10,20);
g.setColor(Color.green);
g.fillOval(50,50,150,100);
g.setColor(Color.blue);
g.drawLine(10,10,280,150);
Font f = new Font("TimesRoman",Font.BOLD,36);
g.setFont(f);
g.setColor(Color.red);
g.drawString("Wow - Java!", 60 , 100);
}
}
Klasa Graphics2D
Klasa Graphics2D jest rozszerzeniem klasy Graphics, zdefiniowanym w pakiecie
java.awt.Graphics2D. Dostarcza bardziej wyrafinowanych narzędzi graficznych.
Aby wykorzystać nowe możliwości na powierzchni rysunkowej wybranego komponetu, trzeba
rzutować jego domyślny kontekst graficzny Graphics g na typ Graphics2D, tworząc nowy kontekst
graficzny:
Graphics2D g2 = (Graphics2D)g;
Klasa Graphics2D umożliwia między innymi rysowanie konturów o żądanej szerokości, stylu
zakończenia linii i miejsca połaczeń odcinków:
g2.setStroke(new BasicStroke(szerokość_linii, zakończenie_linii, łaczenia_linii));
float
int 0-2
Przykład: g2.setStroke(new BasicStroke(5.0f));
// linia ciągła szerokość linii 5px
Kolor, teksturę i przezroczystość ustala się w klasie Graphics2D
metodą setPaint()
import java.awt.*;
import javax.swing.*;
public class Bs extends JFrame {
public static void main(String s[]) {
Bs ok = new Bs();
ok.setSize(240, 170);
ok.setTitle("Graphics2D");
ok.setDefaultCloseOperation(EXIT_ON_CLOSE);
int 0-2
ok.add(new Kanwa());
ok.setVisible(true);
}
}
class Kanwa extends Canvas {
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
rozszerzonych możliwościach
g2.setStroke(new BasicStroke(8.0f,0,2));
połączenia zaokrąglone
// kontekst graficzny o
// szerokość linii 5px,
g2.setPaint(Color.blue);
Rectangle r = new Rectangle(10,10,200,110);
g2.draw(r);
g2.setPaint(Color.red);
Rectangle rr = new Rectangle(50,50,50,50);
g2.draw(rr);
g.setColor(Color.green);
g.fillOval(120,30,100,45);
g.setColor(Color.yellow);
g.drawOval(120,30,100,45);
}
}
Przykład - Wykres kołowy
Program rysujący wykres kołowy dla liczb o losowo
wybranych wartościach. Ilość liczb jest podawana jako
argument przy uruchomieniu programu. Program jest
przygotowany na przyjęcie maksymalnie 20 liczb (ale to
ograniczenie można usunąć, stosując Vector zamiast
tablicy liczb).
Przykład uruchomienia (po skompilowaniu) dla 8 liczb:
java Kolowy 8
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Kolowy {
public static void main(String []
args) {
int n = Integer.parseInt(args[0]);
Okno ok = new Okno(n);
}
}
class Okno extends JFrame {
Random r = new Random();
int n;
int [] x;
int suma=0;
// n - argument przy uruch.programu
Wykres k;
Okno( int n) {
setSize(300,300);
setTitle("Wykres kołowy - dane losowe");
setDefaultCloseOperation(EXIT_ON_CLOSE);
x = new int[n];
for (int i=0;i< n; i++)
// tworzenie obiektu k klasy Wykres dla n liczb
k = new Wykres(n,x);
// o wartościach w tablicy x
add(k);
setVisible(true);
}
}
class Wykres extends Canvas {
int n=20;
int [] x = new int[n];
int suma=0;
Random r = new Random();
Wykres(int nd, int[] dane) {
n=nd;
for (int i=0; i< n; i++) {
x[i]=dane[i];
suma=suma+x[i];
}
setBackground(Color.orange);
}
public void paint(Graphics g) {
int xp=10, yp=10;
int szer = getWidth()-10;
// pobranie wymiarów okienka
int wys = getHeight()-10;
int katP = 0;
// kąt początkowy dla wycinka
for (int i=0; i< n; i++) {
double kK = (double)x[i]/suma*360;
// kąt końcowy wycinka
int katK = (int)kK;
if (i == n-1) katK=360-katP;
Color k = new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256));
// losowy kolor wycinka
g.setColor(k);
g.fillArc(xp,yp,szer,wys,katP,katK);
// rysowanie wycinka
katP = katP+katK;
}
g.setColor(Color.white);
StringBuilder s = new StringBuilder("Liczby: ");
for (int i=0; i< n; i++) s.append(x[i] +", ");
g.drawString(s.toString(),10,70);
}
}
Grafika i zdarzenia związane z myszą
Klasa komponentu, po którym ma rysować mysz, musi implementować interfejs
MouseMotionListener, zaś do utworzonego komponentu - obiektu tej klasy trzeba przyłączyć
słuchacza ruchu myszy: addMouseMotionListener(this).
Interfejs MouseMotionListener posiada dwie metody:
mouseDragged(MouseEvent me) - przesuwanie myszy (bez naciśnięcia jej klawisza)
mouseMoved(MouseEvent me) - przeciąganie myszy (przesuwanie z naciśniętym klawiszem)
Trzeba implementować OBIE te metody, nawet jeśli w programie potrzebna jest tylko jedna z nich
(niewykorzystana metoda ma ciało puste).
Przykładowy program wykorzystuje metodę mouseMoved. Przy poruszaniu myszą program
wyświetla bieżące współrzędne myszy i testuje położenie: wewnątrz / na zewnątrz czerwonego
prostokąta. Program działa przy przesuwaniu myszy bez naciśnięcia klawisza. Przy przesuwaniu
naciśniętej myszy zachodziłoby zdarzenie:
przeciąganie myszy i należałoby obsłużyć drugą z
metod interfejsu MouseMotionListener - metodę
mouseDragged.
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.awt.Container;
class Okno extends JFrame {
Kanwa k;
public static void main(String[] args) {
Okno ok = new Okno();
}
Okno () {
setTitle("Ruszaj myszą");
setSize(300,250);
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container con = this.getContentPane();
con.setBackground(Color.yellow);
k = new Kanwa();
add(k);
setVisible(true);
}
}
class Kanwa extends Canvas implements
MouseMotionListener {
boolean wewnatrz = false;
int X=0,Y=0;
int xw=40,szer=200,yw=60,wys=100; // położenie i wymiary czerwonego prostokąta
Kanwa () {
addMouseMotionListener(this);
}
public void paint(Graphics g) {
g.setColor(Color.red);
g.fillRect(xw,yw,szer,wys);
g.setColor(Color.blue);
if (wewnatrz) g.drawString("Mysz w srodku "+X+" "+Y,60,120);
else g.drawString("Mysz na zewnatrz "+X+" "+Y,60,40);
}
public void mouseDragged(MouseEvent me) {
}
public void mouseMoved(MouseEvent me) {
X = me.getX();
Y = me.getY();
if ( X>xw && Xyw && Y< yw+wys) wewnatrz=true;
repaint();
}
else wewnatrz=false;
}
Rysowanie prostych figur geometrycznych
Program demonstruje:

Zastosowanie grupy
przycisków opcji (radiobuttonów) do wyboru kształtu
figury (elipsa, prostokąt, trójkąt).
Główna klasa programu
implementuje interfejs
ActionListener. Jego metoda
actionPerformed sprawdza który
przycisk opcji wygenerował
zdarzenie ActionEvent - stąd
wiadomo jakie kształy rysować.

Implementację interfejsu
MouseListener do testowania
naciśnięcia i zwolnienia
przycisku myszy, oraz odczytu współrzędnych myszy w chwili zdarzenia. Interfejs
MouseListener jest implementowany przez klasę obiektu, po którym chcesz rysować, czyli
przez kanwę. Zaś do samego obiektu przyłacza się słuchacza: addMouseListener(this).
Wykorzystano następujące metody interfejsu MouseListener:
mousePressed(MouseEvent e) - naciśnięcie myszy
mouseReleased(MouseEvent e) - zwolnienie myszy
zaś pozostałe metody tego interfejsu: mouseClicked(), mouseEntered() i mouseExited() mają ciała
puste.
Klasa Kanwa jest w programie rozszerzeniem klasy JPanel. W zasadzie powinno się rysować na
komponentach klasy Canvas, nie na panelach. Dlaczego?
Jedna z różnic w działaniu tych obiektów jest widoczna w chwili zastosowania metody repaint():
Metoda repaint() zastosowana dla obiektu klasy Canvas czyści całą powierzchnię i rysuje nową figurę
na czystej powierzchni (usuwając poprzednio narysowane figury).
Metoda repaint() zastosowana wprost do obiektu klasy JPanel NIE CZYŚCI obrazu poprzednio
narysowanych figur, one są jakby narysowane w innej warstwie. Dopiero odświeżenie wewnętrznego
kontenera tego panelu (lub w ogóle głównego okna aplikacji!) powoduje usunięcie poprzednio
narysowanych figur.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class Simple extends JFrame implements ActionListener {
// współrz. lewego górnego wierzchołka i wymiary prostokąta/elipsy
int x, y, w, h;
// współrzędne wierzchołków trójkąta
int[] xt = new int[3];
int[] yt = new int[3];
String ksztalt = "Prostokat";
Random r = new Random();
public static void main(String[] args) {
Simple frame = new Simple();
frame.setVisible(true);
}
public Simple() {
setTitle("Ciągnij naciśniętą mysz i puść");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,300);
setLayout(new BorderLayout());
// grupa przycisków opcji
ButtonGroup opcje = new ButtonGroup();
JRadioButton elipsa = new JRadioButton("Elipsa");
JRadioButton prosto = new JRadioButton("Prostokat");
JRadioButton trojkat = new JRadioButton("Trojkat");
opcje.add(elipsa);
opcje.add(prosto);
opcje.add(trojkat);
elipsa.addActionListener(this);
prosto.addActionListener(this);
prosto.setSelected(true);
trojkat.addActionListener(this);
// panel przycisków opcji
JPanel opcjePanel = new JPanel();
opcjePanel.setLayout(new FlowLayout());
opcjePanel.add(elipsa);
opcjePanel.add(prosto);
opcjePanel.add(trojkat);
add(opcjePanel, BorderLayout.NORTH);
// panel do rysowania
Kanwa p = new Kanwa();
add(p,BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent ae) { // wybór opcji kształtu
ksztalt = ae.getActionCommand().toString();
}
class Kanwa extends JPanel implements MouseListener{ // można też tak: extends Canvas
ale...???
Container con;
// wewnętrzny kontener Kanwy
Kanwa() {
con = getContentPane();
con.setBackground(new Color(220,255,100));
addMouseListener(this);
}
public void paint(Graphics g) {
// losowy wybór koloru wypełnienia figury
g.setColor(new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)));
if (ksztalt=="Prostokat") g.fillRect(x,y,w,h);
if (ksztalt=="Elipsa") g.fillOval(x,y,w,h);
if (ksztalt=="Trojkat") {
Polygon tr = new Polygon(xt,yt,3);
g.fillPolygon(tr);
}
}
public void mouseClicked(MouseEvent me) { }
public void mouseEntered(MouseEvent me) { }
public void mouseExited(MouseEvent me) { }
public void mousePressed(MouseEvent me) {
x = me.getX(); // współrzędne myszy w chwili naciśnięciu klawisza
y = me.getY();
}
public void mouseReleased(MouseEvent me) {
int x2 = me.getX();
int y2 = me.getY();
// współrzędne myszy w chwili zwolnieniu klawisza
if (ksztalt=="Prostokat" || ksztalt=="Elipsa") {
w = Math.abs(x2-x); // szerokość figury
x = Math.min(x,x2); // współrzędna x lewego górnego wierzchołka
h = Math.abs(y2-y); // wysokość figury
y = Math.min(y,y2); // współrzędna y lewego górnego wierzchołka
}
if (ksztalt=="Trojkat") {
xt[0]=x; xt[1]=x2; xt[2]= x;
yt[0]=y; yt[1]=y2; yt[2]= y2;
}
repaint();
// con.repaint();
tak jeśli chciałbyś odświeżać tło dla każdej rysowanej figury
}
} // koniec klasy Kanwa
} // koniec klasy Simple
Rysowanie linii w ślad za myszą
Początek linii ma miejsce w miejscu położenia wskaźnika myszy w chwili gdy naciśnięto klawisz
myszy. Współrzędnie pobierane są przy pomocy metody mousePressed() interfejsu MouseListener.
System operacyjny sprawdza położenie myszy w pewnych odstępach czasu. Poruszana mysz zdąży w
tym czasie, między jednym a drugim odczytem, pokonać niewielki odcinek na ekranie.
Przy przeciąganiu myszy (poruszaniu z naciśniętym klawiszem) program wykorzystuje metodę
mouseDragged() interfejsu MouseMotion Listener. Pobierane są bieżące współrzędne myszy.
Polecenie repaint() powoduje
wywołanie metody paint(), która
rysuje odcinek łączący punkty: od
poprzedniego do bieżącego położenia
myszy. Powstaje linia łamana,
złożona z odcinków o niewielkiej
długości.
Piewrsza wersja programu
wykorzystuje obiekt klasy JPanel
jako kanwę rysunku - wówczas nie
ma problemu z usuwaniem wcześniej
narysowanych fragmentów linii,
kolejne odcinki są dodawane do
końca bieżącej linii.
import
import
import
import
javax.swing.*;
java.awt.*;
java.awt.event.*;
javax.diva.canvas.JCanvas;
public class Malarz {
static public void main(String[] args) {
Okno ok = new Okno();
ok.setVisible(true);
}
}
class Okno extends JFrame {
Kanwa k;
Okno() {
setSize(400,300);
setTitle("Rysuj naciśniętą myszą");
setDefaultCloseOperation(EXIT_ON_CLOSE);
k = new Kanwa();
add(k);
}
}
class Kanwa extends JPanel implements MouseMotionListener, MouseListener {
int x0,y0,x1,y1;
Kanwa() {
x0=0;
y0=0;
addMouseMotionListener(this);
addMouseListener(this);
}
public void paint(Graphics g) {
g.drawLine(x0,y0,x1,y1); x0=x1; y0=y1;
}
// metody interfejsu MouseMotionListener
public void mouseDragged(MouseEvent me) {
x1=me.getX();
y1=me.getY();
repaint();
}
public void mouseMoved(MouseEvent me) {
}
// metody interfejsu MouseListener
public void mousePressed(MouseEvent me) {
x0=me.getX();
y0=me.getY();
}
public
public
public
public
void
void
void
void
mouseClicked(MouseEvent me) { }
mouseEntered(MouseEvent me) { }
mouseExited(MouseEvent me) { }
mouseReleased(MouseEvent me) {}
}
Druga wersja programu wykorzystuje
obiekt klasy Canvas jako kanwę
rysunku. Tym razem program tworzy
w pamięci kolekcję rysowanych
odcinków. Każdy następny odcinek
jest dodawany do tej kolekcji.
Polecenie repaint() czyści kanwę i
rysuje od nowa całą kolekcję obiektów
- odcinków.
W programie zastosowano klasę
ArrayList z pakietu
java.util.ArrayList.
import
import
import
import
javax.swing.*;
java.awt.*;
java.awt.event.*;
java.util.ArrayList;
public class Malarz1 {
public static void main(String[] args) {
Okno ok = new Okno();
ok.setVisible(true);
}
}
class Okno extends JFrame {
Kanwa k;
Okno() {
setSize(400,300);
setDefaultCloseOperation(3);
k=new Kanwa();
add(k);
}
}
class Kanwa extends Canvas implements MouseMotionListener, MouseListener {
// tworzenie kolekcji obiektów klasy
ArrayList lamana = new ArrayList();
int x0,y0;
Odcinek
// współrzędne początku każdego kolejnego odcinka
Kanwa() {
setBackground(Color.orange);
addMouseListener(this);
addMouseMotionListener(this);
}
public void paint(Graphics g) {
// dla każdego obiektu z kolekcji lamana wykonaj polecenie: narysuj go
for (Odcinek s : lamana) g.drawLine(s.x1,s.y1,s.x2,s.y2);
}
// metody interfejsu MouseMotionListener
public void mouseDragged(MouseEvent me) {
int x1=me.getX(); // pobierz współrzędne kończ odcinka
int y1=me.getY();
// utwórz obiekty klasy Odcinek i dodaj go do kolekcji lamana
Odcinek s = new Odcinek(x0,y0,x1,y1);
lamana.add(s);
repaint();
// przyjmij koniec bieżącego odcinka jako początek dla następnego
odcinka
x0=x1;
y0=y1;
}
public void mouseMoved(MouseEvent me) {
}
// metody interfejsu MouseListener
public void mousePressed(MouseEvent me) {
x0=me.getX();
y0=me.getY();
}
public void mouseReleased(MouseEvent me) { }
public void mouseClicked(MouseEvent me) { }
public void mouseEntered(MouseEvent me) { }
public void mouseExited(MouseEvent me) { }
}
class Odcinek{
public int x1,y1,x2,y2;
Odcinek (int a,int b,int c,int d){
x1=a; y1=b; x2=c; y2=d;
}
}
Ćwiczenie
Napisz program który rysuje kółka w miejscu kliknięcia myszą, a następnie:


łączy odcinkami środki kolejnych kółek
oblicza i wyświetla długość w pikselach ostatnio narysowanego odcinka
Download