JAVA : SWING, DOKUMENTY I DRUKOWANIE 1. WSTĘP

Wielkość: px
Rozpocząć pokaz od strony:

Download "JAVA : SWING, DOKUMENTY I DRUKOWANIE 1. WSTĘP"

Transkrypt

1 JAVA : SWING, DOKUMENTY I DRUKOWANIE 1. WSTĘP Rozważałem już opracowanie programów z graficznym interfejsem użytkownika, w tym Swing, który jest Java API do tworzenia aplikacji GUI, a także w kolejnych książkach rozważałem więcej przykładów programów GUI. W szczególności mam w Javie 6 informacje o bazach danych poddanych obróbce JTable, która jest najbardziej złożonym komponentem Swing. Wszystko, co wcześniej mówiono o programach GUI, jest wystarczająco dobre, ale jest o wiele więcej do opowiedzenia, w tym kilka technicznych koncepcji Swinga. Poniżej omówiono przede wszystkim to, co wcześniej nie zostało powiedziane, ale niektóre będą również powtórzeniami i opracowaniami tego, o czym już wspomniałem na temat rozwoju aplikacji GUI. Podzielę książkę na następujące rozdziały, a poszczególne rozdziały są w dużej mierze niezależne od siebie i można je czytać w dowolnej kolejności: 1. Szczegóły huśtawki 2. Układ 3. Elementy wahadłowe 4. Komponenty zdefiniowane przez użytkownika 5. Schowek 6. Przeciągnij i upuść 7. Edytuj tekst 8. Internacjonalizacja 9. Ostatni przykład Niektóre z poniższych elementów prawdopodobnie przekraczają potrzeby spotykane w życiu codziennym, ale jako ważne kwestie, szczególnie chciałbym zwrócić uwagę na przeciąganie i upuszczanie, a także schowek, są kwestie, które koniecznie trzeba opanować, aby pracować jako programista w praktyce. Wspomnę również o rozwoju niestandardowych komponentów. 1.1 UWAGI NA TEMAT SWING Zanim przejdę do tematów tej książki, chciałbym wspomnieć o trzech klasach, z których już korzystałem, ale mogłem nie odnosić się do zysku, a przynajmniej warto je poznać i w razie potrzeby będę z nich korzystać w następujący sposób. Po pierwsze chciałbym wspomnieć o SwingUtilities, która jest klasą zawierającą wiele statycznych metod użyteczności. Użyłem metod invokelater () i invokeandwait (), aby upewnić się, że metoda jest wykonywana w wątku programu rozsyłającego zdarzenia. Istnieje wiele innych metod i powinieneś przestudiować klasę i metody, które ona zapewnia. Większość metod, których rzadko będziesz potrzebować, ale dobrze jest poznać ich istnienie. Inna klasa nosi nazwę SwingConstants i, jak sama nazwa wskazuje, klasa definiuje wiele stałych. Zachęcamy do sprawdzenia, które z nich. Zasadniczo różne komponenty Swing definiują stałe, na przykład dotyczące wyrównania. Oznacza to, że te same stałe są zdefiniowane w kilku miejscach, a znaczenie SwingConstants jest zamiast używania stałych zdefiniowanych w poszczególnych klasach komponentów, wtedy powinieneś użyć stałych w SwingConstants. Wreszcie istnieje klasa

2 SwingWorker, która jest klasą abstrakcyjną, która służy do wykonywania czasochłonnych operacji GUI w jednym lub kilku wątkach w tle. Celem jest, aby czasochłonne zadania nie były wykonywane w wątku dyspozytora zdarzeń, ponieważ program będzie wtedy w stanie, w którym nie będzie odpowiadał. Celem tej klasy jest ułatwienie wykonywania czasochłonnych zadań w ich wątkach, a nie pisanie niezbędnego kodu dla wątków. Porównując to, co mówi się o Swingu w tej książce i książce Java 2, znasz większość tego, co Swing udostępnia do pisania rogramów za pomocą graficznego interfejsu użytkownika. Wszystko nie jest wyjaśnione, aw praktyce konieczne jest ciągłe sprawdzanie dokumentacji online, częściowo dlatego, że Swing jest tak obszerny, że nie można się wszystkiego nauczyć, a częściowo dlatego, że Swing również się rozwija. Swing to bardzo udany interfejs API do tworzenia programów GUI, ale krytykuje się go także za konieczność pisania dużo. Jest to również poprawne, ale jeśli opracujesz własne biblioteki klas, które pasują do zadań, które zwykle rozwiązujesz, Swing można faktycznie wykonać dość skutecznie. Istnieją jednak alternatywy, a gdy językiem jest Java, jest to JavaFX, który jest nowszym interfejsem API do tworzenia graficznych interfejsów użytkownika. 2 SZCZEGÓŁY SWINGU Interfejs użytkownika programów jest rozwijany za pomocą komponentów, z którymi użytkownik może coś zrobić, a przykładami są JButton i JTextField, a jest ich około 30 - lub nieco więcej, w zależności od tego, jak liczyć - więc jest dużo do pracy. Podstawowy komponent podstawowy nazywa się JComponent i jest klasą abstrakcyjną zawierającą wszystko, co wspólne dla komponentów Swing. Wszystkie komponenty Swing obsługują dziś specyfikację Java Beans, a to oznacza, że komponenty składają się z właściwości, gdzie właściwość jest zmienną instancji z metodami akcesora. Jeśli na przykład zmienna ma nazwę propertyname i opisuje właściwość komponentu, można oczekiwać, że komponent ma metody setpropertyname () i getpropertyname () lub ispropertyname (). Pierwszy tylko wtedy, gdy jest to właściwość, której wartość musi zostać zmieniona, a ostatni tylko wtedy, gdy jego typ to boolean. Właściwości można podzielić na 1. Proste właściwości, które są właściwościami, a nie wyzwalanie zdarzenia po zmianie wartości. 2. Związane właściwości, które uruchamiają PropertyChangeEvent, gdy wartość się zmienia, i gdzie obiekt typu PropertyChangeListener można zarejestrować jako detektor dla PropertyChangeEvent za pomocą metody addpropertychangelistener (). 3. Ograniczone właściwości, które również uruchomiły PropertyChangeEvent, ale przed zmianą wartości. Obiekt typu VetoableChangeListeners można zarejestrować jako detektor dla tych zdarzeń za pomocą metody addvetoablechangelistener () (tylko kilka komponentów uruchamia te zdarzenia). Obiekt PropertyChangeEvent przypisał trzy wartości: nazwę właściwości, starą wartość i nową wartość. Składnik może również uruchomić ChangeEvent, który jest uruchamiany, gdy stan zmiany właściwości. Obiekty typu ChangeListener można zarejestrować jako detektory dla ChangeEvent. Takie zdarzenie jest powiązane tylko z jedną wartością, która jest składnikiem, który je wywołuje. Innym rodzajem właściwości są tak zwane właściwości klienta i są to pary klucz / wartość przechowywane w Hashtable dołączonym do każdego komponentu Swing. Oznacza to, że za pomocą metody putclientproperty () możesz dodać lub usunąć właściwość klienta. Na przykład możesz dodać właściwość w następujący sposób:

3 component.putclientproperty ( propertyname, propertyvalue); i możesz to usunąć za pomocą instrukcji component.putclientproperty ( propertyname, null); Możesz uzyskać wartość z instrukcją: value = component.getclientproperty("propertyname"); Właściwości klienta umożliwiają dodawanie właściwości w czasie wykonywania i można je traktować jako okazję do powiązania dodatkowych danych ze składnikiem. Właściwości klienta są właściwościami powiązanymi i wywołały właściwość PropertyChangeEvent po zmianie wartości. Ze względów związanych z wydajnością powinieneś tylko sporadycznie dodawać właściwości klienta dla komponentu, a jeśli potrzebujesz nowych funkcji komponentu, powinieneś rozważyć napisanie klasy pochodnej. Na przykład program PropertiesProgram otwiera następujące okno: Kliknięcie jednego z przycisków powoduje wyświetlenie okna komunikatu pokazującego liczbę naciśnięć tego przycisku: a kiedy klikniesz OK, pojawi się kolejne okno komunikatu, które pokazuje, że liczba kliknięć jest liczona przez jeden (tutaj jest liczony od 3 do 4): Poniżej znajduje się wypełniony kod programu: package propertiesprogram; import java.awt.*;

4 import java.awt.event.*; import javax.swing.*; import java.beans.*; import javax.swing.border.*; public class MainView extends JFrame implements PropertyChangeListener public MainView() super("propertiesprogram"); setresizable(false); createview(); setdefaultcloseoperation(exit_on_close); pack(); setlocationrelativeto(null); setvisible(true); public void propertychange(propertychangeevent e) if (e.getoldvalue()!= null) JButton cmd = (JButton)e.getSource(); JOptionPane.showMessageDialog(this, String.format("%s: %d - > %d", cmd.gettext(), e.getoldvalue(), e.getnewvalue())); private void createview() JPanel panel = new JPanel(new GridLayout(3, 3, 5, 5)); panel.setborder(new EmptyBorder(5, 5, 5, 5)); panel.add(createbutton("a")); panel.add(createbutton("b")); panel.add(createbutton("c")); panel.add(createbutton("d")); panel.add(createbutton("e")); panel.add(createbutton("f")); panel.add(createbutton("g")); panel.add(createbutton("h")); panel.add(createbutton("i")); add(panel); private JButton createbutton(string text)

5 JButton cmd = new JButton(text); cmd.setpreferredsize(new Dimension(50, 50)); cmd.setmargin(new Insets(0, 0, 0, 0)); cmd.putclientproperty("clickcounter", 0); cmd.addpropertychangelistener(this); cmd.addactionlistener(new ClickHandler()); return cmd; class ClickHandler implements ActionListener public void actionperformed(actionevent e) JButton cmd = (JButton)e.getSource(); Integer value = (Integer)cmd.getClientProperty("clickCounter"); value += 1; JOptionPane.showMessageDialog(MainView.this, String.format( "%s has been clicked %d times", cmd.gettext(), value)); cmd.putclientproperty("clickcounter", value); Zauważ, że klasa MainView implementuje interfejs PropertyChangeListener i dlatego może być zarejestrowana jako detektor dla PropertyChangeEvent. W przeciwnym razie najważniejszą metodą jest createbutton (), która tworzy przycisk. Zwróć uwagę, jak zdefiniować właściwość klienta dla przycisku, co oznacza, że przycisk ma licznik, który od samego początku ma wartość 0. Obsługa zdarzeń przycisku ma typ ClickHandler (klasa wewnętrzna) i wyświetla pierwszą z powyższych skrzynki wiadomości. W tym miejscu należy zwrócić uwagę, jak odwoływać się do właściwości clickcounter klienta, a także, że moduł obsługi zdarzeń zlicza tę właściwość za pomocą 1. Gdy tak się dzieje, przycisk uruchamia właściwość PropertyChangeEvent, co w tym przypadku oznacza, że wykonywana jest metoda propertychange () elementu MainView ( MainView jest zarejestrowany jako detektor), który otwiera drugie okno dialogowe. ĆWICZENIE 1 Utwórz kopię projektu PropertiesProgram. Musisz teraz zmienić program, aby działał dokładnie tak samo, ale zamiast kojarzyć właściwość klienta z przyciskami, powinieneś napisać typ CounterButton, który rozszerza JButton o licznik właściwości, a okno powinno wówczas zawierać 9 składników CounterButton. Zauważ, że oznacza to, że metoda setcounter () powinna odpalić PropertyChangeEvent.

6 2.2 ROZMIAR I LOKALIZACJA Jak opisano w książce Java 2, rozmiar komponentu jest określony przez trzy właściwości typu Wymiar: 1. preferowany rozmiar 2. minimumsize 3. maximumsize ale w jakim stopniu te właściwości są używane, jak wyjaśniono w Javie 2, całkowicie określa bieżący menedżer układu. W rzeczywistości wolno pisać menedżera układu, który całkowicie ignoruje te właściwości. Należy bezwzględnie wspomnieć, że dokładny rozmiar i położenie komponentu w oknie jest określane przez bieżącego menedżera układu. Możesz także zdefiniować rozmiar i lokalizację komponentu za pomocą setbounds (), ale tutaj powinieneś pamiętać, że menedżer układu pomija wartości ustawione za pomocą setbounds () (lub setsize () i setlocation ()). Jeśli chcesz umieścić komponenty w ten sposób, usuń menedżera układu, ustawiając go na null. Jeśli chodzi o rozmiar i lokalizację komponentu, zwróć uwagę na następujące metody: - getwidth () - getheight () - getsize () - getbounds () - getlocation () która zwraca aktualny rozmiar i lokalizację komponentu. Możesz także zdefiniować wyrównanie komponentu w kontenerze, w którym się on znajduje, oraz podstawowe metody - setalignmentx () - setalignmenty () i obie metody mają parametr zmiennoprzecinkowy, gdzie 0 oznacza lewy lub górny, 1 oznacza prawy lub dolny, a wartość między tymi skrajnościami wskazuje stopień, w jakim składnik ma być dostosowany. Na przykład oznacza 0,5 wyśrodkowany. Jedynymi menedżerami układu używającymi tych właściwości są BoxLayout lub OverlayLayout. Jako przykład tego wszystkiego pokażę program, który otwiera następujące okno:

7 Okno ma 8 przycisków, a po kliknięciu przycisku pojawia się okno komunikatu. Okno ma BorderLayout, który składa się z pięciu paneli: - u góry FlowLayout z przyciskiem - po lewej stronie GridLayout z dwoma przyciskami - na dole FlowLayout z przyciskiem - po prawej stronie BoxLayout z trzema przyciskami - wyśrodkuj FlowLayout za pomocą jednego przycisku Kod górnego panelu to: private JPanel top() JPanel panel = new JPanel(new FlowLayout()); JButton cmd = new JButton("Top"); cmd.setpreferredsize(new Dimension(150, 50)); cmd.addactionlistener(e -> show(cmd)); panel.add(cmd); panel.addmouselistener(new MouseAdapter() public void mouseclicked(mouseevent e) show(panel); ); return panel; Jeśli klikniesz górny przycisk, otrzymasz wynik:

8 a to oznacza, że bieżąca szerokość i wysokość przycisku zależy od jego preferowanego rozmiaru i odpowiada to, że komponenty układu FlowLayout, tak że rozmiary są ich preferowanym rozmiarem. Kliknięcie w górnym panelu poza przyciskiem powoduje wyświetlenie wyniku: Preferowany rozmiar panelu to , co jest określane przez preferowany rozmiar przycisku, ale FlowLayout domyślnie wstawia margines 5 (odstęp między komponentami na 5 pikselach). Bieżąca wysokość panelu wynosi 60, co określa zewnętrzny układ BorderLayout, ponieważ wysokość PÓŁNOCNA to wysokość elementu, czyli panelu. Z drugiej strony szerokość jest szerokością okna, która wynosi 500, a zatem bieżąca szerokość górnego panelu będzie wynosić 500. Kod lewego panelu to: private JPanel left() JPanel panel = new JPanel(new GridLayout(2, 1)); JButton cmd1 = new JButton("A"); JButton cmd2 = new JButton("B"); cmd1.setpreferredsize(new Dimension(50, 50)); cmd2.setpreferredsize(new Dimension(70, 50)); cmd1.addactionlistener(e -> show(cmd1)); cmd2.addactionlistener(e -> show(cmd2)); panel.add(cmd1); panel.add(cmd2); return panel; Po kliknięciu przycisku A pojawi się okno komunikatu: Bieżąca szerokość wynosi 70, co jest faktycznie określane przez preferowany rozmiar dolnego przycisku, ponieważ szerokość komponentu WEST w BorderLayout jest określana przez preferowany rozmiar komponentu. Tutaj jest to składnik GridLayout, a jego szerokość jest określona przez największy z dwóch składników. Gdy bieżąca wysokość wynosi 84, dzieje się tak, ponieważ składnik GridLayout dzieli całkowitą wysokość na dwie równe części. Należy pamiętać, że preferowany rozmiar przycisku jest całkowicie ignorowany. Jeśli klikniesz przycisk B, otrzymasz:

9 Następnie u dołu znajduje się kod panelu: private JPanel bund() JPanel panel = new JPanel(new FlowLayout()); JButton cmd = new JButton("A button at the bottom"); cmd.addactionlistener(e -> show(cmd)); panel.add(cmd); panel.addmouselistener(new MouseAdapter() public void mouseclicked(mouseevent e) show(panel); ); return panel; W szczególności zwróć uwagę, że tym razem przycisk nie zdefiniował żadnego preferowanego rozmiaru. Kliknięcie przycisku daje wynik: Podobnie jak w pierwszym przypadku, aktualny rozmiar przycisku zależy od jego preferowanego rozmiaru, ale należy pamiętać, że preferowany rozmiar przycisku zależy od tekstu i bieżącej czcionki. Kliknięcie w dolnym panelu, ale poza przyciskiem pojawi się: a wyjaśnienie jest takie samo jak w przypadku górnego panelu. Prawy panel ma następujący kod: panel private JPanel right() JPanel panel = new JPanel(); panel.setlayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

10 panel.add(createcmd("button 1", 100, 30)); panel.add(createcmd(" Button 2", 120, 40)); panel.add(createcmd(" Button 3", 150, 50)); panel.addmouselistener(new MouseAdapter() public void mouseclicked(mouseevent e) show(panel); ); return panel; private JButton createcmd(string text, int width, int height) JButton cmd = new JButton(text); cmd.setpreferredsize(new Dimension(width, height)); cmd.setminimumsize(new Dimension(width, height)); cmd.setmaximumsize(new Dimension(width, height)); cmd.addactionlistener(e -> show(cmd)); cmd.setalignmentx(component.right_alignment); return cmd; i tutaj przede wszystkim powinieneś zauważyć preferowany rozmiar trzech przycisków. Kliknięcie przycisków powoduje:

11 Oznacza to, że we wszystkich trzech przypadkach bieżący rozmiar przycisku jest taki sam, jak jego preferowany rozmiar, a następnie BoxLayout określa rozmiar komponentów przy użyciu ich preferowanych rozmiarów. Jeśli klikniesz na dole panelu, otrzymasz a zatem rozmiar prawego panelu jest największą szerokością elementów, a także wysokością okna. Należy zauważyć, że preferowana wysokość panelu to 120, co jest sumą wysokości trzech przycisków, a BorderLayout ignoruje tę wartość (EAST używa wysokości okna). Z tyłu znajduje się środkowy panel: private JPanel center() JPanel panel = new JPanel(new FlowLayout()); JButton cmd = new JButton("Center"); cmd.setpreferredsize(new Dimension(100, 50)); cmd.addactionlistener(e -> show(cmd)); panel.add(cmd); panel.addmouselistener(new MouseAdapter() public void mouseclicked(mouseevent e) show(panel); ); return panel; Po kliknięciu przycisku otrzymasz: co pokazuje, że bieżący rozmiar przycisku jest jego preferowanym rozmiarem, tak jak w FlowLayout. Kliknięcie poza przyciskiem, otrzymasz

12 i z tego widać, że ŚRODEK w BorderLayout ignoruje preferowany rozmiar panelu. Przykład pokazuje zatem, że różni się sposób użycia preferowanego rozmiaru. 2.3 OBSŁUGA ZDARZEŃ Zdarzenia pojawiają się na ogół po naciśnięciu klawisza na klawiaturze lub gdy coś dzieje się z myszą (kliknięcie przycisku lub poruszenie myszą). Komponenty Swing mogą uruchamiać wiele różnych rodzajów zdarzeń, które są typami zdefiniowanymi w java.awt.event lub javax.swing.event, a wiele z nich jest specyficznych dla komponentów. Każdy typ zdarzenia definiuje co najmniej obiekt, który wywołał dane zdarzenie, ale często zawiera także inne informacje o stanie obiektu. Istnieją jednak również zdarzenia, które nie są wywoływane przez komponent. Aby obiekt mógł otrzymywać powiadomienia o zdarzeniu, obiekt musi zostać zarejestrowany jako nasłuchujący tego zdarzenia. Aby było to możliwe, klasa obiektu musi implementować interfejs nasłuchiwania (na przykład ActionListener), a następnie obiekt może zostać zarejestrowany (na przykład za pomocą addactionlistener ()). Klasa abstrakcyjna JComponent definiuje chronione pole typu EventListenerList, zwane listenerlist, które dziedziczą wszystkie komponenty. Typ EventListenerList to tablica par w postaci XXEvent / XXListener, gdzie XX jest typem zdarzenia. Ta tablica zawiera detektory komponentu i to, które zdarzenia są nasłuchiwane, a zatem komponent może wysyłać powiadomienia do detektorów, gdy wystąpi zdarzenie. Gdy zamiast kolekcji używana jest tablica, ma ona na celu zwiększenie wydajności podobnej do kolekcji takiej jak ArrayList, która jest stosunkowo złożoną klasą. W rzeczywistości oznacza to, że nowa tablica musi być tworzona (i kopiowana) za każdym razem, gdy dodawany jest detektor, ale wciąż jest tańsza niż przy użyciu kolekcji, ponieważ jest ograniczona liczba rejestratorów dla danego komponentu. Wszystkie zdarzenia są obsługiwane przez procedury obsługi zdarzeń przez zarejestrowane obiekty nasłuchiwania i jak opisano w książce Java 8, te procedury obsługi muszą być wykonywane przez wątek wywołujący zdarzenia, który jest wątkiem, który zajmuje się lokalizacją i rysowaniem poszczególnych komponentów. Wątek jest połączony z kolejką zdarzeń FIFO oczekujących na przetwarzanie, a poszczególne zdarzenia są przetwarzane w kolejności, w jakiej są wstawiane do kolejki, a następne nie będzie przetwarzane, dopóki poprzednie nie zostanie zakończone. W przeciwnym razie można zaryzykować zmianę stanu komponentu, gdy na przykład zostanie on przerysowany. Z tego powodu nie wolno wykonywać procedury obsługi zdarzeń poza wątkiem wywołującym zdarzenia, na przykład przez wywołanie metody firexx () bezpośrednio z innego wątku. Ważne jest również, aby procedury obsługi zdarzeń i kod rysujący komponenty były skuteczne, ponieważ możesz ryzykować zablokowanie systemu przed zdarzeniami w kolejce zdarzeń oczekującymi na leczenie. Aby upewnić się, że wszystkie kody powiązane z procedurami obsługi zdarzeń są wykonywane w wątku wywołującym zdarzenia, w Javie 8 pokazałem, jak klasa SwingUtilities ma dwie metody invokelater () i invokeandwait (), które dodają uruchamialny obiekt do kolejki zdarzeń systemowych. Klasycznym wzorcem zapewniającym wykonanie kodu w wątku wywołującym zdarzenia jest wykonanie kodu w następujący sposób: public void dothreadsafework() if (SwingUtilities.isEventDispatchThread())

13 // do all work here else Runnable calldothreadsafework = new Runnable() public void run() dothreadsafework(); ; SwingUtilities.invokeLater(callDoThreadSafeWork); Tutaj najpierw sprawdza się, czy kod jest wykonywany przez wątek wywołujący zdarzenia, a jeśli nie, jest definiowany obiekt uruchamialny, który wykonuje metodę, a następnie jest wysyłany do wątku wywołującego zdarzenia. Gdy SwingUtilities otrzymuje obiekt Runnable przez wywołanie invokelater (), jest on natychmiast przekazywany do metody postrunnable () w klasie SystemEventQueueUtilities. Jeśli zamiast tego obiekt jest odbierany przez wywołanie invokeandwait(), najpierw jest testowane, czy bieżący wątek nie jest zdarzeniem wywołującym wątek i, w razie potrzeby, zgłaszany jest wyjątek. W przeciwnym razie tworzony jest obiekt, który jest używany jako blokada dla krytycznego regionu, który zawiera dwie instrukcje, w którym najpierw wysyła obiekt do postrunnable (), podczas gdy drugi jest blokadą wait (). W rezultacie wątek wywołujący czeka na notify(). 2.4 RENDEROWANIE KOMPONENTÓW Kiedy maszyna wyświetla komponent na ekranie, oznacza to, że komponent musi zostać narysowany i mówi się, że Swing renderuje komponent. Jeśli komponent zmieni się później - na przykład po kliknięciu przycisku - należy go narysować ponownie. Zasadniczo komponent jest rysowany za pomocą metody paintcomponent () i czy jest to komponent niestandardowy, w którym programista jest odpowiedzialny za rysowanie komponentu, należy zastąpić tę metodę, a zastąpienie musi zawsze zaczynać się od instrukcji super. paintcomponent (). W rzeczywistości nie jest to proste, ponieważ wiele może się zdarzyć z komponentem, a na przykład komponent może być w pełni lub częściowo pokryty przez inny komponent. Jako przykład pokażę prosty niestandardowy komponent. Należy od razu powiedzieć, że komponent niestandardowy jest często zapisywany w inny sposób niż pokazuje poniższy przykład, ale wrócę do niego później. Jeśli otworzysz program BirdsProgram, wyświetli się następujące okno:

14 Tutaj obraz jest komponentem, podobnie jak wszystkie inne komponenty w oknie. Jest to bardzo prosty element, który ma ustalony rozmiar i pokazuje obraz, na którym narysowano ramkę i narysowano tekst na górze obrazu. Składnik ma właściwości, dzięki czemu można określić szerokość ramki i zmienić tekst. Możesz także zmienić czcionkę, w której rysowany jest tekst, a na koniec możesz zmienić kolor ramki i tekstu. Po kliknięciu obrazu składnik wywołuje akcję ActionEvent, którą mogą zarejestrować zarejestrowani słuchacze. Kod komponentu jest następujący: package birdsprogram; import java.awt.*; import java.awt.event.*; import java.awt.geom.rectangle2d; import javax.swing.*; public class TheBirds extends JComponent private static ImageIcon birds = createimageicon("/birdsprogram/birds.jpg", 320, 180); private float borderwidth = 5; private String text = "Birds in Thy"; public TheBirds() setbackground((color.black)); setfont(new Font("Liberation Serif", Font.BOLD, 36)); addmouselistener(new public void paintcomponent(graphics g)

15 super.paintcomponent(g); Graphics2D g2d = (Graphics2D)g; g2d.drawimage(birds.getimage(), 0, 0, this); float width = borderwidth / 2; Rectangle2D rect = new Rectangle2D.Double(width, width, 320 borderwidth, 180 borderwidth); g2d.setpaint(getbackground()); g2d.setstroke(new BasicStroke(borderWidth)); g2d.draw(rect); FontMetrics fm = g2d.getfontmetrics(); int w = fm.stringwidth(text); int h = fm.getascent(); g.drawstring(text, 160 (w / 2), 90 + (h / 4)); public float getborderwidth() return borderwidth; public void setborderwidth(float borderwidth) this.borderwidth = borderwidth; repaint(); public String gettext() return text; public void settext(string text) this.text = text; repaint();

16 @Override public void setfont(font font) public void setbackground(color color) public Dimension getpreferredsize() return new Dimension(320, public Dimension getminimumsize() return public Dimension getmaximumsize() return public void setpreferredsize(dimension dimension)

17 throw new public void setminimumsize(dimension dimension) throw new public void setmaximumsize(dimension dimension) throw new UnsupportedOperationException(); public void addactionlistener(actionlistener listener) listenerlist.add(actionlistener.class, listener); public void removeactionlistener(actionlistener listener) listenerlist.remove(actionlistener.class, listener); private class ClickHandler extends MouseAdapter public void mouseclicked(mouseevent e)

18 ActionListener[] listeners = listenerlist.getlisteners(actionlistener.class); for (ActionListener listener : listeners) listener.actionperformed( new ActionEvent(TheBirds.this, ActionEvent.ACTION_PERFORMED, "")); public static ImageIcon createimageicon(string path, int width, int height) java.net.url imgurl = MainView.class.getResource(path); if (imgurl!= null) return new ImageIcon(new ImageIcon(imgURL, ""). getimage().getscaledinstance(width, height, Image.SCALE_SMOOTH), ""); return null; Po pierwsze, należy zauważyć, że klasa dziedziczy JComponent, a zatem jest komponentem. Oznacza to między innymi, że komponent ma wszystkie funkcje zapewniane przez JComponent. Klasa definiuje zmienną statyczną, która odnosi się do obrazu. Należy zauważyć, że klasa używa metody createimageicon () do załadowania obrazu i nadaje mu rozmiar , który jest również rozmiarem komponentu. Klasa definiuje również dwie zmienne instancji, przy czym pierwsza określa szerokość ramki, a druga to tekst do narysowania na obrazie. Konstruktor określa kolor tła (na czarny). Zauważ, że jest to możliwe, ponieważ klasa TheBirds jest JComponent i dlatego ma metodę setbackground (). Kolor tła jest używany do ramki i tekstu. Podobnie konstruktor definiuje czcionkę, która jest czcionką używaną do rysowania tekstu. Na koniec konstruktor przypisuje moduł obsługi zdarzenia do komponentu, tak aby łapał kliknięcie myszą. Następnie istnieje metoda paintcomponent (), która jest metodą, którą należy wykonać, gdy konieczne jest narysowanie komponentu, czyli wtedy, gdy otwiera się okno i wyświetla się komponent. Należy zauważyć, że metoda nigdy nie jest wywoływana jawnie, ale jest wywoływana przez system wykonawczy, gdy konieczne jest narysowanie okna. Metoda ma parametr typu Graphics, który jest abstrakcyjną klasą podstawową, która określa sposób rysowania komponentów na różnych urządzeniach, takich jak ekran. Można myśleć o obiekcie graficznym jako o kanwie, na której element może rysować, a klasa udostępnia różnorodne narzędzia do rysowania. Ponadto klasa definiuje właściwości w postaci czcionki i koloru, obszaru cięcia i układu współrzędnych. Lewy górny róg to (0,0), a dodatnie wartości są skierowane w prawo, podczas gdy dodatnie wartości y są skierowane w dół. Metoda paintcomponent () rozpoczyna się od wywołania metody paintcomponent () klasy podstawowej, a następnie wykonania typu rzutowania parametru na obiekt Graphics2D. Klasa Graphics2D wywodzi się z Graphics (i została wprowadzona wraz ze Swingiem po wprowadzeniu Java

19 2). Dostępne są ulepszone narzędzia do rysowania, dlatego w praktyce zawsze wykonuje się ten typ rzutowania. Przykładem narzędzia do rysowania jest drawimage (), a trzecia instrukcja używa tej metody do rysowania obrazu. Następne pięć instrukcji określa i rysuje ramkę. Polega na określeniu, za pomocą którego pióra narysować kolor (jak gruba linia) należy narysować, i na końcu zdefiniowaniu prostokąta określającego ramkę. Rysowanie figur geometrycznych to smakołyki wyszczególnione w następnej książce, ale wymaga to pewnych obliczeń, aby narysować ramkę w fizycznej granicy komponentu o wymiarach pikseli. Ostatnie instrukcje w paintcomponent () służą do rysowania tekstu, a wyzwaniem jest narysowanie tekstu na środku komponentu. Reszta kodu dotyczy przede wszystkim definiowania metod dostępu do właściwości. Po pierwsze, istnieją dwie zmienne instancji borderwidth i text. W tym miejscu należy zauważyć, że dwie metody ustawione kończą się na repaint (). Ta instrukcja wymusza przerysowanie komponentu. Metoda uruchamia zdarzenie, które jest wstawiane do kolejki zdarzeń, a wątek wysyłki zdarzeń przerysuje następnie komponent, gdy upłynie czas. Istnieją również przesłonięcia dwóch metod setfont () i setbackground (), które wykonują jedynie wywołanie odpowiedniej metody w klasie bazowej. Zauważ, że tutaj nie jest wymagana odmalowanie (), ponieważ zajmuje się nią klasa podstawowa. Na koniec również nadpisuję metody getpreferredsize (), getminimumsize () i getmaximumsize (), więc zwracają stały rozmiar. To ze względu na menedżerów układu, ponieważ często używają tych metod. Ponieważ rozmiar jest ustalony, nie ma sensu stosowanie odpowiednich zestawów metod (nie miałyby one żadnego efektu), a zatem są one nadmiernie przereklamowane, aby zgłosić wyjątek. Alternatywnie można zamiast tego zastąpić metody pustymi metodami. Z powrotem istnieje zarządzanie zdarzeniami, które musi koniecznie implementować metody addactionlistener () i removeactionlistener (). Należy tutaj zauważyć, że listenerlist jest zdefiniowany jako chroniony w klasie bazowej i dlatego można do niego odwoływać się z klasy pochodnej TheBirds. Zwróć również uwagę, w jaki sposób listenerlist jest wykorzystywany w klasie ClickHandler do wysyłania powiadomień do dowolnych detektorów. Jeśli chodzi o główną aplikację, która otwiera powyższe okno, nie wspomnę o tym tutaj, ponieważ nie dodaje nic nowego, ale mogę wspomnieć, że ma ona BorderLayout i pokazuje komponent TheBirds zamknięty w FlowLayout. Powyższy komponent rysuje tekst przy użyciu określonej czcionki, a w rzeczywistości czcionki są dość złożone. Biorąc pod uwagę poniższy rysunek, powinien on ilustrować dwie linie narysowane określoną czcionką. W pełni narysowane linie są linią bazową, a gdy drawstring () rysuje tekst, współrzędne y odnoszą się do linii bazowej. Część powyżej linii podstawowej nazywa się wejściem, a część poniżej linii podstawowej nazywa się zniżaniem. Pomiędzy liniami jest odległość, a wysokość to odległość między dwiema liniami bazowymi. Powyższa funkcja paintcomponent () używa klasy FontMetrics, która generalnie zwraca szereg informacji o bieżącej czcionce. Klasa ma również metodę o nazwie stringwidth (), która zwraca długość łańcucha mierzoną w stosunku do bieżącej czcionki. W powyższym przykładzie wartości te są używane do rysowania tekstu na środku komponentu. Kluczem do sprawdzania poprawności i renderowania komponentów jest usługa o nazwie RepaintManager. Odpowiada za wysyłanie zdarzeń do kolejki wysyłania zdarzeń, która odbywa się za pomocą dwóch metod: odmalowywanie () i ponowne

20 sprawdzanie poprawności (), które zamykają zdarzenia w obiektach uruchamialnych i wysyłają je do invokelater (). Różnica polega na tym, że funkcja repaint () nakazuje przerysowanie komponentu, a zatem funkcja paintcomponent () musi zostać wykonana ponownie, a funkcja revalidate () informuje menedżera układu, że składniki muszą być ponownie układem, aby ich lokalizacja i rozmiar zostać przeliczonym. Komponenty Swing używają domyślnego podwójnego buforowania, czyli techniki, w której zamiast rysować bezpośrednio na urządzeniu fizycznym, rysunek jest wykonywany w buforze pamięci. Można myśleć o tym jako o pamięciowym przedstawieniu rysunku takiego jak obrazek, a po narysowaniu obrazu jest on wysyłany do urządzenia fizycznego jako dziura, co jest bardzo szybkie i dla nas ludzi oznacza, że otrzymujemy stabilny obraz. W przeciwnym razie na skomplikowanych ekranach może się okazać, że proces rysowania zajmuje dużo czasu. Chcę pokazać inny przykład niestandardowego komponentu. Jeśli otworzysz program LabelProgram, pojawi się następujące okno: W górnym rzędzie znajduje się szereg elementów składających się z przycisków i przycisków opcji. W dolnym rzędzie znajduje się etykieta i pole wprowadzania oraz jest to niestandardowy składnik o nazwie InputField. Komponenty w górnym rzędzie służą do definiowania różnych właściwości komponentu niestandardowego: - tekst na etykiecie komponentu - gdzie tekst powinien być pokazany (północ, zachód, południe lub wschód) - z tekstem - preferowany rozmiar pola wprowadzania - czcionka na tekst - czcionka w polu wprowadzania Komponent to JPanel z dwoma komponentami: JPanel i JTextField: package labelprogram; import javax.swing.*; import java.beans.*; import java.awt.*; public class InputField extends JPanel

21 public static final String NORTH = BorderLayout.NORTH; public static final String SOUTH = BorderLayout.SOUTH; public static final String EAST = BorderLayout.EAST; public static final String WEST = BorderLayout.WEST; private final JTextField field = new JTextField(); private final JLabel caption = new JLabel(); private String position; public InputField(String text) this(text, 100, WEST); public InputField(String text, int width) this(text, width, WEST); public String getcaption() return caption.gettext(); public Font getcaptionfont() return caption.getfont(); public Font gettextfont() return field.getfont();

22 public int getcaptionwidth() return caption.getpreferredsize().width; public Dimension getfieldsize() return field.getpreferredsize(); public String gettext() return field.gettext(); public String getposition() return position; public void setcaption(string text) String oldvalue = caption.gettext(); caption.settext(text); firepropertychange("caption", oldvalue, text); public void setcaptionfont(font font) Font oldvalue = caption.getfont();

23 public InputField(String text, int width, String position) setlayout(new BorderLayout(5, 0)); caption.sethorizontalalignment(jlabel.right); caption.settext(text); add(caption, position); add(field); this.position = position; fi Dimension(width, fi caption.setfont(font); revalidate(); firepropertychange("captionfont", oldvalue, font); public void settextfont(font font) Font oldvalue = caption.getfont(); field.setfont(font); field.setpreferredsize(new field.getgraphics().getfontmetrics().getheight() + 3)); Dimension(field.getPreferredSize().width, revalidate(); firepropertychange("textfont", oldvalue, font); public void setcaptionwidth(int width) Integer oldvalue = getcaptionwidth(); caption.setpreferredsize( new Dimension(width, caption.getpreferredsize().height)); revalidate(); firepropertychange("captionwidth", oldvalue, new Integer(width));

24 public void setposition(string position) String oldvalue = this.position; this.position = position; remove(caption); add(caption, position); if (position.equals(west)) caption.sethorizontalalignment(jlabel.right); else caption.sethorizontalalignment(jlabel.left); revalidate(); firepropertychange("position", oldvalue, position); public void setfieldsize(dimension size) Dimension oldvalue = field.getpreferredsize(); field.setpreferredsize(size); revalidate(); firepropertychange("fieldsize", oldvalue, public Dimension getpreferredsize() if (position.equals(east) position.equals(west)) int height = Math.max(caption.getPreferredSize().height, field.getpreferredsize().height); int width = caption.getpreferredsize().width + field.getpreferredsize().width; return new Dimension(width, height);

25 else int height = caption.getpreferredsize().height + field.getpreferredsize().height; int width = Math.max(caption.getPreferredSize().width, field.getpreferredsize().width); return new Dimension(width, public Dimension getminimumsize() return public Dimension getmaximumsize() return public void setpreferredsize(dimension size) throw new public void setminimumsize(dimension size)

26 throw new public void setmaximumsize(dimension size) throw new public void addpropertychangelistener(propertychangelistener listener) listenerlist.add(propertychangelistener.class, public void removepropertychangelistener(propertychangelistener listener) listenerlist.remove(propertychangelistener.class, protected void fi name, Object oldvalue, Object newvalue) PropertyChangeEvent event = new PropertyChangeEvent(this, name, oldvalue, newvalue); for (PropertyChangeListener listener : listenerlist.getlisteners(propertychangelistener.class)) listener.propertychange(event);

27 Kod jest obszerny, ale w zasadzie prosty i powinieneś zauważyć, że wymaga trochę kodu, aby napisać dobry niestandardowy komponent - i więcej niż pokazuje ten przykład. Klasa dziedziczy JPanel z BorderLayout, gdzie jest JLabel PÓŁNOCNY, ZACHODNI, POŁUDNIOWY lub WSCHODNI. CENTRUM to JTextField. Składnik implementuje siedem właściwości, ale tylko jedna jest zdefiniowana jako zmienna w klasie InputField. Pozostałe są reprezentowane jako właściwości dotyczące dwóch komponentów, z których składa się komponent niestandardowy. Należy pamiętać, że metody właściwości komponentu przeprowadzają ponowną walidację (), ponieważ zmiany właściwości komponentu oznaczają, że etykieta lub pole wejściowe zmieniają rozmiar, a być może także lokalizację. Zwróć także uwagę na metodę setposition (), co oznacza, że składnik etykiety musi zostać umieszczony w innym miejscu. Wymaga to, aby najpierw został usunięty z pojemnika, a następnie umieszczony we właściwym miejscu. Zauważ również, że ustawione metody odpalają PropertyChangeEvent. Klasa powinna zatem mieć metody, aby nasłuchiwania mogły zarejestrować się w celu otrzymywania powiadomień PropertyChange i ewentualnie obliczać nowe lokalizacje. Jednym z wyzwań związanych ze składnikami niestandardowymi jest rozmiar, w którym getpreferredsize () zwraca wartość, więc menedżerowie układu odpowiednio traktują składniki. W takim przypadku rozmiar zależy od dwóch wewnętrznych komponentów i należy zwrócić uwagę na sposób implementacji metody getpreferredsize (). Poniżej znajduje się kod okna korzystającego ze składnika: package labelprogram; import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.border.*; import java.beans.*; public class MainView extends JFrame implements PropertyChangeListener private InputField component = new InputField("Label"); public MainView() super("labelprogram"); setsize(700, 300); setlocationrelativeto(null); createview(); setdefaultcloseoperation(exit_on_close); component.addpropertychangelistener(this); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout(0, 20)); panel.setborder(new EmptyBorder(20, 20, 20, 20)); panel.add(top(), BorderLayout.NORTH); panel.add(wrap(component)); add(panel);

28 private JPanel top() JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); panel.add(createbutton("caption", e - > String text = JOptionPane.showInputDialog(MainView.this, component.getcaption()); if (text!= null) component.setcaption(text); )); ButtonGroup group = new ButtonGroup(); panel.add(createradio("north", group, false, e -> component.setposition(inputfield.north))); panel.add(createradio("west", group, true, e -> component.setposition(inputfield.west))); panel.add(createradio("south", group, false, e -> component.setposition(inputfield.south))); panel.add(createradio("east", group, false, e -> component.setposition(inputfield.east))); panel.add(createbutton("width", e -> String text = JOptionPane.showInputDialog(MainView.this, "Caption height"); if (text!= null) int width = Integer.parseInt(text); component.setcaptionwidth(width); )); panel.add(createbutton("text", e -> String text1 = JOptionPane.showInputDialog(MainView.this, "Width"); String text2 = JOptionPane.showInputDialog(MainView.this, "Height"); if (text1!= null && text2!= null) int width = Integer.parseInt(text1); int height = Integer.parseInt(text2); component.setfieldsize(new Dimension(width, height)); )); panel.add(createbutton("label", e -> new FontView(new FontListener() public void fontchanged(fontevent e) component.setcaptionfont(e.getfont()); ); )); panel.add(createbutton("field", e -> new FontView(new FontListener() public void fontchanged(fontevent e) component.settextfont(e.getfont()); ); )); return panel; private JRadioButton createradio(string text, ButtonGroup group, boolean checked, ActionListener listener) JRadioButton cmd = new JRadioButton(text); cmd.setselected(checked); cmd.addactionlistener(listener); group.add(cmd);

29 return cmd; private JButton createbutton(string text, ActionListener listener) JButton cmd = new JButton(text); cmd.addactionlistener(listener); return cmd; private JPanel wrap(jcomponent component) JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); panel.add(component); return public void propertychange(propertychangeevent e) JOptionPane.showMessageDialog(this, e.getpropertyname() + "\nfrom: " + e.getoldvalue() + "\nto: " + e.getnewvalue()); Tutaj również nie ma wiele do wyjaśnienia, ale należy zauważyć, że klasa implementuje interfejs PropertyChangeListener, ponieważ powinna otrzymywać powiadomienia o zmianach właściwości w komponencie obiektu. Oznacza to, że klasa musi implementować metodę propertychange () metody, która w tym przypadku otwiera okno komunikatu pokazujące zmianę. Dla celów praktycznych nie ma to oczywiście większego znaczenia, a celem jest także pokazanie, jak uchwycić zmiany właściwości komponentu. Klasa (program) wykorzystuje proste okno dialogowe do zdefiniowania czcionki. To bardzo proste okno dialogowe (i nie chcę tutaj wyświetlać kodu), ale problem 1 poniżej dotyczy ulepszenia programu w tym momencie. 2.5 FOKUS I KLAWIATURA Komponenty Swing są umieszczane w pojemniku, a jeden z nich ma fokus, dzięki czemu zdarzenia związane z klawiaturą są wysyłane do tego komponentu. Ogólny cykl ustawiania ostrości odbywa się od lewej do prawej i od góry do dołu. Przełączasz ostrość za pomocą TAB i CTRL-TAB, a jeśli chcesz zmienić ostrość w przeciwnym kierunku, użyj SHIFT-TAB i CTRL-SHIFT-TAB. Fokus jest kontrolowany za pomocą instancji klasy FocusManager. Patrząc z programisty, nie ma w nim tak wielu wyzwań, ale komponent wysyła zdarzenie, gdy zostanie skupione i ponownie, gdy straci skupienie:

30 1. focusgained (FocusEvent e), gdy składnik zostanie ustawiony na focus 2. focuslost (FocusEvent e), gdy składnik traci fokus Gdy komponent ma foccus i klawisz jest wciśnięty na klawiaturze, komponent (w zależności od danego komponentu) może wysłać KeyEvent. Te zdarzenia można zarejestrować, kojarząc KeyListener. W przeciwieństwie do innych zdarzeń, KeyEvent jest wysyłany, prawdopodobnie przed wykonaniem odpowiedniej operacji. Po naciśnięciu klawisza generowane są trzy zdarzenia KeyEvent: 1. KEY_PRESSED, który pojawia się po naciśnięciu klawisza. Klucz jest określony za pomocą właściwości keycode, a getkeycode () może uzyskać kod wirtualny reprezentujący klucz, taki jak KeyEvent.VK_ENTER. Należy pamiętać, że klawisze kombinacji, takie jak CTRL-C, uruchamiają dwa zdarzenia KEY_PRESSED, a kody wirtualne to KeyEvent. VK_CTRL i KeyEvent.VK_C. KeyEvent ma również właściwość keychar, która zawiera kod Unicode. 2. KEY_RELEASED są identyczne ze zdarzeniami KEY_PRESSED i odpalają po zwolnieniu klucza, ale nie są uruchamiane tak często jak zdarzenia KEY_PRESSED. 3. KEY_TYPED to zdarzenia, które strzelają między KEY_PRESSED i KEY_RELEASED, ale nie jest dołączony kod klucza. Te zdarzenia nie są uruchamiane dla kluczy, które nie mają reprezentacji Unicode, takich jak PAGE UP i PRINT SCREEN. Przytrzymanie klawisza (dla kluczy z reprezentacją Unicode) krótko generuje sekwencje zdarzeń KEY_PRESSED i KEY_TYPED. Każdy KeyEvent ma tak zwane modyfikatory, które wskazują stan klawiszy SHIFT, CTRL, ALT i META w momencie naciśnięcia klawisza. Jest to liczba całkowita, która jest bitową LUB następujących stałych - InputEvent.SHIFT_MASK - InputEvent.CTRL_MASK - InputEvent.ALT_MASK - InputEvent.ALT_GRAPH_MASK - InputEvent.META_MASK - InputEvent.BUTTON1_MASK - InputEvent.BUTTON2_MASK - InputEvent.BUTTON3_MASK Wartość można określić za pomocą getmodifiers () i można przetestować pojedyncze klucze za pomocą isshiftdown (), iscontroldown (), isaltdown () i ismetadown (). W ten sposób można kontrolować komponenty za pomocą klawiatury, rejestrując obiekty KeyListener. Ponieważ może być bardzo obszerny (wiele do napisania), Swing zdefiniował alternatywny sposób używania obiektów KeyStroke, ponieważ nazywane są również akceleratorami klawiatury. KeyStroke

31 hermetyzuje kod klucza, wartość modyfikatora i wartość logiczną, która wskazuje, czy jest to naciśnięcie klawisza (fałsz), czy zwolnienie klawisza (prawda). Klasa KeyStroke ma 5 metod statycznych, które tworzą obiekty KeyStroke: - getkeystroke (char keychar) - getkeystroke (int keycode, int modyfikatory) - getkeystroke (int keycode, int modyfikatory, boolean onkeyrelease) - getkeystroke (reprezentacja ciągu) - getkeystroke (KeyEvent anevent) Do zarejestrowania detektora dla klawiatury dla JComponent służy: registerkeyboardaction (ActionListener action, KeyStroke stroke, int war) Parametr ActionListener musi zdefiniować metodę actionperformed (), aby wykonać niezbędne operacje odpowiadające parametrowi KeyStroke. Ostatni parametr określa, w jakich warunkach KeyStroke jest poprawny: 1. JComponent.WHEN_FOCUSED, ActionListener jest wywoływany tylko wtedy, gdy aktywny jest składnik, do którego odnosi się obiekt KeyStroke. 2. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, ActionListenery komponentu są wywoływane tylko wtedy, gdy komponent, którego dotyczy obiekt KeyStroke, znajduje się na wyższym poziomie (przodku) niż komponent, który jest aktywny. 3. JComponent.WHEN_IN_FOCUSED_WINDOW, ActionListener jest wywoływany tylko wtedy, gdy składnik, do którego odnosi się obiekt KeyStroke, znajduje się w oknie, które jest aktywne (JFrame, JDialog, JWindow). Na przykład, jeśli chcesz zdefiniować ActionListener dla ALT-H, niezależnie od tego, który komponent w ramce JFrame z ramką nazwy, która jest aktywna, możesz napisać coś takiego: KeyStroke keystroke = KeyStroke.getKeyStroke (KeyEvent.VK_H, InputEvent.ALT_MASK, false); frame.getrootpane (). registerkeyboardaction (listener, keystroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); gdzie detektor jest nazwą rzeczywistej ActionListener. Każdy JComponent utrzymuje tablicę skrótów ze wszystkimi klawiszami KeyStrokes jako właściwościami klienta. Gdy KeyStroke zostanie zarejestrowany za pomocą registerkeyboardaction (), jest dodawany do tej struktury danych. Można zarejestrować tylko jeden ActionListener dla każdego KeyStroke, a jeśli istnieje już ActionListener dla określonego KeyStroke, nowy zastąpi poprzedni. Możesz określić bieżące powiązania KeyStroke za pomocą metody getregisteredkeystrokes () i usunąć wszystkie powiązania za pomocą resetkeyboardactions ().

32 Przez akcję zasadniczo rozumiesz instancję ActionListener z właściwościami powiązanymi z Hashtable, w rzeczywistości podobnie jak właściwości klienta w JComponent. Zazwyczaj obiektów akcji używa się do rejestrowania działań klawiatury. Jako mały przykład program KeyStrokeProgram otwiera następujące okno: Okno ma cztery przyciski. Do każdego z dwóch górnych przycisków dołączone są dwa skróty, a do każdego z dwóch dolnych przypisany jest jeden skrót. Kliknięcie jednego z przycisków lub naciśnięcie klawisza skrótu powoduje otwarcie okna komunikatu. Różnica polega na tym, że dla dwóch górnych przycisków klawisze skrótów są przypisane jako WHEN_FOCUSED, a zatem przyciski muszą mieć fokus, aby klawisze działały. W przypadku dwóch dolnych przycisków klawisze skrótów są przypisane jako WHEN_IN_FOCUSED_WINDOW, a zatem działają, gdy fokus ma tylko element okna lub element nadrzędny. Aby to zilustrować, kliknięcie myszą daje panel zawierający przyciski, fokus, a jeśli to zrobisz, tło panelu tło staje się czerwone. Kod okna jest pokazany poniżej: package keystrokeprogram; import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.border.*; public class MainView extends JFrame private Action actionlistener = new AbstractAction() public void actionperformed(actionevent e) JButton source = (JButton)e.getSource(); JOptionPane.showMessageDialog(MainView.this, source.gettext()); ; public MainView()

33 setsize(600, 250); setlocationrelativeto(null); createview(); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new GridLayout(2, 2, 20, 20)); panel.setborder(new EmptyBorder(20, 20, 20,20)); panel.setbackground(color.blue); JButton cmd1 = new JButton( "<html><h2> Buttom 1</h2><center>CTRL-ALT-C<br/>SHIFT V</center></html>"); JButton cmd2 = new JButton( "<html><h2> Buttom 2</h2><center>ENTER<br/>SHIFT ESC</center></html>"); JButton cmd3 = new JButton("<html><h2>Buttom 3</h2><center>F4</center></html>"); JButton cmd4 = new JButton("<html><h2> Buttom 4</h2> <center>shift-ctrl-altgr<br/>space</center></html>"); JComponent.WHEN_FOCUSED, "CRTL-ALT-C-KEY", assignkey(cmd1, KeyStroke.getKeyStroke("control alt C")); assignkey(cmd1, JComponent.WHEN_FOCUSED, "SHIFT-V- KEY", KeyStroke.getKeyStroke("shift V")); assignkey(cmd2, JComponent.WHEN_FOCUSED, "ENTER-KEY", KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true)); assignkey(cmd2, JComponent.WHEN_FOCUSED, "SHIFT-ESC-KEY", KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK, false)); assignkey(cmd3, JComponent.WHEN_IN_FOCUSED_WINDOW, "F4-KEY", KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0)); assignkey(cmd4, JComponent.WHEN_IN_FOCUSED_WINDOW, "SPACE-KEY", KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_MASK InputEvent.CTRL_MASK InputEvent.ALT_GRAPH_MASK, true)); panel.add(cmd1); panel.add(cmd2); panel.add(cmd3); panel.add(cmd4); panel.addmouselistener(new MouseAdapter() public void mouseclicked(mouseevent e) panel.requestfocus(); ); panel.addfocuslistener(new FocusListener() public void focusgained(focusevent e) panel.setbackground(color.red); public void focuslost(focusevent e) panel.setbackground(color.blue);

34 ); add(panel); private void assignkey(jcomponent component, int condition, String name, KeyStroke keystroke) component.getinputmap(condition).put(keystroke, name); component.getactionmap().put(name, actionlistener); ĆWICZENIE 2 Powinieneś napisać program o nazwie FocusProgram, który otwiera okno, jak pokazano poniżej. Okno ma 25 pól wejściowych, a pole z fokusem jest pokazane z czerwonym tłem i białym tekstem. Pola bez fokusa muszą mieć białe tło lub czarny tekst. Jeśli musi być możliwe przechodzenie z pola do pola za pomocą klawiszy strzałek, a jeśli pole jest aktywne i wpiszesz Enter, program powinien otworzyć okno komunikatu: które pokazują indeksy dla wiersza i kolumny oraz wartość wprowadzoną w polu. Aby rozwiązać ćwiczenie, pola wejściowe nie powinny być komponentami JTextField, ale komponentem rozszerzonym z JTextField: class InputField extends JTextField implements FocusListener, KeyListener

35 private int row; private int col; public InputField(int row, int col) public void keyreleased(keyevent e) public void keypressed(keyevent e) switch (e.getkeycode()) case KeyEvent.VK_LEFT: moveleft(); e.consume(); break; public void keytyped(keyevent e) private void moveleft(). to jest klasa wewnętrzna. PROBLEM 1

36 Zacznij od utworzenia kopii programu LabelProgram. Program posiada okno dialogowe o nazwie FontView, za pomocą którego można wybrać czcionkę. To bardzo proste okno dialogowe, które należy ulepszyć, aby zwiększyć łatwość obsługi. Musisz napisać komponent, który możesz wywołać FontChooser, którego można użyć do wybrania parametrów czcionki. Na przykład możesz napisać klasę FontChooser, która ma metodę opendialog (), która otwiera okno dialogowe do wybierania czcionki, a klasa może następnie mieć metodę, która zwraca wybraną czcionkę po kliknięciu przycisku OK. Po napisaniu klasy i przetestowaniu jej usuń klasę FontView z projektu i zamiast tego użyj nowej klasy.gdy uważasz, że Twój FontChooser działa poprawnie, weź kopię najnowszej wersji biblioteki PaLib (wersja z książki Java 6) i przenieś klasę FontChooser do biblioteki. Projekt LabelProgram może mieć odniesienie do PaLib. W razie potrzeby usuń to odniesienie i odwołaj się do nowej wersji PaLib. Usuń klasę FontChooser z projektu i spróbuj ponownie program, aby teraz używał klasy w PaLib. 3 UKŁAD Okno składa się z paneli, w których można umieścić komponenty. Patrząc z programisty, okno to JFrame, JDialog lub JWindow (okno bez paska tytułu), z których wszystkie mają jeden panel komponentów. Istnieje specjalny panel o nazwie rootpane, który ma typ JRootPane i do którego rzadko trzeba się odwoływać. Zawiera dwa panele, zwane odpowiednio warstwami i szybą. Oto ostatni JPanel, którego generał jest niewidoczny (przezroczysty), ale można go użyć do umieszczenia czegoś w oknie, które znajduje się przede wszystkim. Drugi ma typ JLayeredPane i jest również panelem, do którego nie ma bezpośredniego odniesienia, ale ma dwa panele, z których pierwszy jest specjalny i nazywa się JMenuBar i jest używany do menu okna, podczas gdy drugi jest JPanel i nazywa się contentpane i dotyczy pozostałej zawartości okna (patrz poniżej). Dlatego w oknie contentpane okna chcesz dodawać komponenty, ale ogólnie nie musisz o tym myśleć, ponieważ jeśli (na przykład w konstruktorze JFrame lub w metodzie wywoływanej z konstruktora napiszesz coś w rodzaju następujący: setlayout(new FlowLayout); add(component); automatycznie odnosi się do contentpane okna - przynajmniej dzisiaj, ale wcześniej trzeba było pisać getcontentpane (). setlayout (new FlowLayout); getcontentpane (). add (component); Panel ma menedżera układu, który określa sposób umieszczania komponentów w panelu. Domyślnie JFrame i JDialog mają BorderLayout, a JPanel ma FlowLayout. Oprócz menedżera układu JPanel mógł dołączyć granicę, z której korzystałem wiele razy w postaci EmtyBorder. Swing określa wiele opcji granic. Otwarcie programu BorderProgram daje okno z 12 komponentami JLabel zorganizowanymi w GridLayout z 6 wierszami i 2 kolumnami.

37 Każda etykieta ma ramkę i różnią się w zależności od typu i parametrów, z którymi tworzony jest obiekt Granica. Kod pokazano poniżej i nie ma wiele do wyjaśnienia, ale powinieneś obserwować, jak tworzone są różne obiekty Border: package borderprogram; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class MainView extends JFrame private static ImageIcon donnert = palib.gui.tools.createimageicon("/swing04/donnert.gif", 15, 15); public MainView() settitle("borders"); getcontentpane().setbackground(color.yellow); ((JPanel)getContentPane()).setBorder(new EmptyBorder(5, 5, 5, 5)); setlayout(new GridLayout(6, 2, 5, 5)); add(createpanel("raised BevelBorder", new BevelBorder(BevelBorder.RAISED))); add(createpanel("lowered BevelBorder", new BevelBorder(BevelBorder.LOWERED))); add(createpanel(

38 "Black LineBorder, thickness = 5", new LineBorder(Color.black, 5))); add(createpanel("emptyborder with thickness of 10", new EmptyBorder(10,10,10,10))); add(createpanel("raised EtchedBorder", new EtchedBorder(EtchedBorder.RAISED))); add(createpanel("lowered EtchedBorder", new EtchedBorder(EtchedBorder.LOWERED))); add(createpanel("raised SoftBevelBorder", new SoftBevelBorder(SoftBevelBorder.RAISED))); add(createpanel("lowered SoftBevelBorder", new SoftBevelBorder(SoftBevelBorder.LOWERED))); add(createpanel("matteborder", new MatteBorder(donnert))); add(createpanel("titledborder using MatteBorder", new TitledBorder(new MatteBorder(donnert), "Title"))); add(createpanel("titledborder using LineBorder", new TitledBorder(new LineBorder(Color.blue, 10), "Title"))); add(createpanel("titledborder using EmptyBorder", new TitledBorder(new EmptyBorder(10,10, 10, 10), "Title"))); pack(); setdefaultcloseoperation(exit_on_close); setvisible(true); private JPanel createpanel(string text, Border border) JPanel panel = new JPanel(); panel.setborder(border); panel.add(new JLabel(text)); return panel; Pamiętaj o tym podczas pisania setlayout(new GridLayout(6, 2, 5, 5)); odnosi się do panelu treści okna i to samo dotyczy pisania add (createpanel ( RAISED BevelBorder, nowy BevelBorder (BevelBorder.RAISED))); Z drugiej strony znajduje się w instrukcji (która ustawia kolor tła dla panelu zawartości okna): getcontentpane (). setbackground (Color.yellow); konieczne, aby odwołać się do okienka zawartości okna, w przeciwnym razie ustawisz kolor tła okna głównego okna, co nie ma żadnego efektu. Coś podobnego dotyczy instrukcji ((JPanel)getContentPane()).setBorder(new EmptyBorder(5, 5, 5, 5)); w panelu zawartości okna zdefiniuj pustą ramkę wokół domyślnego menedżera układu. Zauważ, że do okna nie przypisano żadnego rozmiaru za pomocą setsize (). Zamiast tego konstruktor wywołuje metodę: pack();

39 Oznacza to, że rozmiar okna jest dostosowywany do preferowanego rozmiaru bieżących składników, określonego przez 12 składników etykiety, a GridLayout umieszcza składniki w komórkach o tym samym rozmiarze 3.1 MENADŻER UKŁADU Menedżery układu są omówione w książce Java 2, w której pokazałem, jak działa większość menedżerów, i chociaż jest ich więcej, nie będę kojarzyć dodatkowych komentarzy z klasycznymi menedżerami układu w tym miejscu. Za pomocą menedżerów układu omówionych w Javie 2 można w rzeczywistości zaprojektować dowolny układ okna - choć niekoniecznie jest to proste. Z tego powodu, a może dlatego, że masz specjalne potrzeby, możesz być zainteresowany napisaniem własnego menedżera układu, a poniżej pokażę dwa przykłady, jak to zrobić. Menedżer układu jest w zasadzie klasą, która implementuje interfejs LayoutManager lub interfejs pochodny o nazwie LayoutManager2. Różnica polega na tym, czy jest to menedżer układu, w którym wywoływane są tak zwane ograniczenia lokalizacji komponentów, co robisz na przykład w BorderLayout i GridBagLayout. Interfejs LayoutManager definiuje zasadniczo dwie metody: - preferowany rozmiar (nadrzędny kontener), który określa preferowany rozmiar rzeczywistego kontenera - layoutcontainer (element nadrzędny kontenera), który układa komponenty w kontenerze Jeśli otworzysz program LayoutProgram, pojawi się następujące okno: Jest to przykład typowego okna dialogowego, w którym można wprowadzić nazwisko i adres osoby, a następnie kliknąć OK, osoba ta zostanie dodana do pola listy. W tym przykładzie zostały utworzone dwie osoby. Pole kombi zawiera kody pocztowe. Jeśli klikniesz dwukrotnie nazwę w polu listy, dane

40 osoby zostaną wstawione w pola, w których można je edytować, a także możesz usunąć tę osobę. Przykład powinien pokazywać coś o układzie, a wprowadzone dane nie zostaną zapisane. Celem jest, aby klasa była prosta. Oprócz powyższego okna program ma dwie klasy, w których Osoba reprezentuje osobę z właściwością dla każdego elementu danych, który można wprowadzić. Ponadto istnieje klasa Zipcodes, które pobierają kody pocztowe z bazy danych. Główne okno nie zawiera niczego nowego do tego, co zostało powiedziane o Swing. Poniżej znajduje się część kodu, która tworzy okno: private void createview() JPanel panel = new JPanel(new BorderLayout(0, 20)); panel.setborder(new EmptyBorder(20, 20, 20, 20)); panel.add(createtop(), BorderLayout.NORTH); panel.add(createbottom(), BorderLayout.SOUTH); panel.add(createcenter()); add(panel); private JScrollPane createcenter() JList list = new JList(model); list.addmouselistener(new ClickHandler()); return new JScrollPane(list); private JPanel createtop() JPanel panel = new JPanel(new GridLayout(6, 1, 0, 10)); panel.add(createline("fist name", txtfirstname)); panel.add(createline("last name", txtlastname)); panel.add(createline("address", txtaddress)); panel.add(createline("zip code", lstzipcode = createlist())); panel.add(createline("phone.", txtphone)); panel.add(createline(" ", txt )); return panel;

41 private JPanel createbottom() JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); panel.add(createbutton("remove", new RemoveHandler())); panel.add(createbutton("ok", new OkHandler())); panel.add(createbutton("clear", new ClearHandler())); return panel; private JButton createbutton(string text, ActionListener listener) JButton cmd = new JButton(text); cmd.addactionlistener(listener); return cmd; private JPanel createline(string text, JComponent component) JPanel panel = new JPanel(new BorderLayout()); JLabel label = new JLabel(text); label.setpreferredsize(new Dimension(100, 22)); panel.add(label, BorderLayout.WEST); panel.add(component); return panel; Należy zauważyć, że układ składa się z BorderLayout, że CENTRUM ma JScrollPane z polem listy, podczas gdy PÓŁNOC i POŁUDNIE mają JPanel. POŁUDNIE to JPanel z FlowLayout, które układają trzy przyciski, podczas gdy NORTH to JPanel z GridLayout z 6 rzędami. Każdy wiersz zawiera BorderLayout z etykietą i składnikiem, którym jest JTextField lub JComboBox. Jeśli spojrzysz na panel u góry, jest to typowy układ okna dialogowego, w którym użytkownik musi coś wpisać. Jest to układ składający się z linii zawierających JLabel i inny komponent. Sytuacja wydaje się tak często, że można rozważyć napisanie specjalnego menedżera układu, który mógłby ułatwić zaprojektowanie takiego okna dialogowego. To jest dokładnie temat przykładu LayoutProgam1. Przykład otwiera to samo okno, a kod jest prawie taki sam. W głównym oknie usunięto metodę createline(), a metodę createtop() zmieniono na: private JPanel createtop ()

42 Panel JPanel = nowy JPanel (nowy DialogLayout (10, 10)); panel.add (nowy JLabel ( Imię )); panel.add (txtfirstname); panel.add (nowy JLabel ( Nazwisko )); panel.add (txtlastname); panel.add (nowy JLabel ( Adres )); panel.add (txtaddress); panel.add (nowy JLabel ( kod pocztowy )); panel.add (lstzipcode = createlist ()); panel.add (nowy JLabel ( Telefon )); panel.add (txtphone); panel.add (nowy JLabel ( )); panel.add (txt ); panel powrotny; Tutaj 12 komponentów jest dodawanych bezpośrednio do panelu, który ma menedżera układu typu DialogLayout. To jest przykład niestandardowego menedżera układu. Może być stosowany w pojemniku zawierającym pary składników formy label1, komponent1 label2, komponent2 i dodawane do kontenera w tej kolejności, co jest niezbędnym warunkiem dla tego menedżera układu. Menedżer układu umieści następnie komponenty w dwóch kolumnach, w których pierwsza kolumna (zwana kolumną nagłówka) zawiera wszystkie komponenty etykiety, a druga kolumna zawiera inne pola. Szerokość pierwszej kolumny jest domyślnie największą szerokością wszystkich składników etykiety, ale można określić stały rozmiar. Szerokość drugiej kolumny to największa szerokość pól. Kod jest następujący: public class DialogLayout implements LayoutManager private int divider = -1; // indicats a possible fi width of the headers private int hgap = 10; // horizontal gap between components private int vgap = 5; between components // vertical gap public DialogLayout()

43 public DialogLayout(int hgap, int vgap) this.hgap = hgap; this.vgap = vgap; public void addlayoutcomponent(string name, Component comp) public void removelayoutcomponent(component comp) public Dimension preferredlayoutsize(container parent) int left = getdivider(parent); int w = 0; int h = 0; for (int k = 1 ; k < parent.getcomponentcount(); k += 2) Dimension d = parent.getcomponent(k).getpreferredsize(); w = Math.max(w, d.width); h += d.height + vgap; h -= vgap; Insets insets = parent.getinsets(); return new Dimension( left + w + insets.left + insets.right, h + insets.top + insets.bottom); public Dimension minimumlayoutsize(container parent) return preferredlayoutsize(parent);

44 public void layoutcontainer(container parent) int left = getdivider(parent); Insets insets = parent.getinsets(); int w = parent.getwidth() insets.left insets.right left; int x = insets.left; int y = insets.top; for (int k = 1 ; k < parent.getcomponentcount(); k += 2) Component component = parent.getcomponent(k); Dimension d = component.getpreferredsize(); parent.getcomponent(k 1).setBounds(x, y, left hgap, d.height); component.setbounds(x + left, y, w, d.height); y += d.height + vgap; public int gethgap() return hgap; public int getvgap() return vgap; public void setdivider(int divider) if (divider > 0) this.divider = divider; public int getdivider()

45 return divider; private int getdivider(container parent) if (divider > 0) return divider; int left = 0; for (int k = 0; k < parent.getcomponentcount(); k += 2) Dimension d = parent.getcomponent(k).getpreferredsize(); left = Math.max(left, d.width); left += hgap; return left; public String tostring() return getclass().getname() + " [hgap=" + hgap + ", vgap=" + vgap + ", divider=" + divider + "]"; Ważne jest, aby zwrócić uwagę na metody preferredlayoutsize() i layoutcontainer (). Obie metody są dość proste, ale wymaga pewnych obliczeń, aby określić preferowany rozmiar kontenera, a w rzeczywistości te same obliczenia są wykonywane w layoutcontainer (), gdzie komponenty są umieszczane w kontenerze. Należy również zwrócić uwagę na sposób umieszczania poszczególnych składników za pomocą setbounds (). Zwróć także uwagę na metodę pomocniczą getdivider () używaną do obliczania szerokości kolumny nagłówka PROBLEM 2 Utwórz kopię projektu LayoutProgram1, ponieważ możesz wywołać LayoutProgram2. Program powinien otworzyć okno pokazane poniżej, które jest prawie identyczne z poprzednimi oknami, z wyjątkiem wierszy po obu stronach pola listy. Zadaniem jest napisanie ulepszonej wersji menedżera układu DialogLayout. Idea tego menedżera układu polega na tym, że potrzebne jest okno dialogowe (jak pokazano poniżej), a następnie można je utworzyć, po prostu wypełniając komponenty w kontenerze we właściwej kolejności. Menedżer układu nie powinien poprawnie obsługiwać wszystkich komponentów, ale wiele okien dialogowych faktycznie składa się tylko z komponentów, jak pokazano w poniższym przykładzie, a następnie można łatwo zaprojektować okno dialogowe za pomocą DialogLayout.

46 Wszystkie komponenty powinny być dodane do panelu z DialogLayout w sekwencji (jak w powyższym przykładzie), ale komponenty są umieszczane w grupach w kolejności, w jakiej są dodawane do kontenera na podstawie następujących reguł, gdzie są trzy grouls: 1. COMP_LBL, który obejmuje pary składające się z JLabel i innego komponentu - czyli w taki sam sposób, jak w poprzedniej wersji DialogLayout 2. COMP_PAN, który zawiera JPanel, JScrollPane i DialogSeparator (komponent niestandardowy, który jest poziomą linią pokazaną w oknie). 3. COMP_CMD, który obejmuje AbstractButton (JButton, JCheckBox, JRadioButton) Po dodaniu komponentów grupa rozciąga się do momentu zmiany typu grupy. Składniki w poszczególnych grupach są przedstawione w następujący sposób: 1. COMP_LBL: Komponenty są układane parami w taki sam sposób jak poprzedni DialogLayout 2. COMP_PAN: Komponenty są ułożone pionowo pod sobą 3. COMP_CMD: Elementy są ułożone poziomo i pochylone w lewo, w środku lub w prawo W odniesieniu do wielkości elementów zastosowanie mają następujące zasady:

47 1. w COMP_LBL pole with og oznacza resztę kontenera, jeśli preferowana szerokość pola jest mniejsza niż 10, a także używana jest preferowana szerokość 2. w COMP_PAN szerokość jest szerokością pojemnika 3. w COMP_CMD używana jest preferowana szerokość komponentów Musisz także napisać prosty niestandardowy komponent DialogSeparator, który rysuje linię w panelu. Celem jest jedynie uzyskanie efektu wizualnego między komponentami w grupie COMP_BIG. Gdy program zostanie ukończony i będzie działał zgodnie z oczekiwaniami, możesz skopiować menedżera układu do biblioteki klas PaLib. Jest to zalecane, ponieważ klasa ma pewne praktyczne zainteresowania. Nazwałem klasę FlexLayout. Pamiętaj również, aby skopiować składnik DialogSeparator. VARIABLEGRIDLAYOUT Zanim zostawię tę sekcję menedżerom układów, wspomnę o VariableGridLayout, czyli starszym menedżerze układów firmy Sun, który jest nieaktualny od wielu lat. Miałem jednak wiele przyjemności z tego menedżera, więc następujące. VariableGridLayout to po prostu menedżer układu, który dziedziczy GridLayout, a zatem jest to GridLayout, ale z rozszerzeniem, że wiersze i kolumny niekoniecznie mają tę samą wysokość / szerokość. Po utworzeniu VariableGridLayout możesz określić, ile całkowitej wysokości ma być zastosowane do określonego wiersza, i w ten sam sposób możesz określić, ile całkowitej szerokości ma być zastosowane dla konkretnej kolumny. Jak wspomniano, VariableGridLayout jest przestarzały i nie jest już częścią interfejsu API Java. Na szczęście kod jest publicznie dostępny i dodałem go do mojej biblioteki pod nazwą VarGridLayout. Program VarGridProgram definiuje okno z 12 przyciskami rozmieszczonymi za pomocą VarGridLayout (przyciski nie mają żadnej funkcji): PROBLEM 3 Musisz napisać klasyczny program, który symuluje kalkulator matematyczny. Program musi otworzyć okno pokazane poniżej. Maszyna musi mieć 10 rejestrów wyników pośrednich, a pola po lewej stronie muszą wyświetlać zawartość tych rejestrów - jeśli zawartość jest inna. Zawartość wyświetlacza jest

48 przechowywana w rejestrze za pomocą przycisku sto, a zawartość rejestru można wstawić za pomocą przycisku rcl. Wyświetlane jest pole nad przyciskami, a górne pole dotyczy historii. Znaczenie przycisków powinno być zrozumiałe, być może nie przycisków C i CL, gdzie pierwsze usuwa ekran, a drugie resetuje całą maszynę. Wszystkie rejestry, pola wyświetlania i historii to pola JTextField i JTextArea, ale żadne z tych pól nie może być edytowalne. Oznacza to, że powinny być zdefiniowane jako nieedytowalne. Pola można wstawiać tylko za pomocą przycisków urządzenia. Z drugiej strony do każdego przycisku należy przypisać klawisz skrótu (sam decydujesz, który z nich), aby można było korzystać z urządzenia wyłącznie za pomocą klawiatury. Niektóre przyciski pokazują ikony, które można znaleźć w ikonach reżysera. Oprócz interfejsu użytkownika największym wyzwaniem jest umiejętność parsowania i oceny wyrażeń matematycznych. Tutaj powinieneś mieć świadomość, że problem został rozwiązany w projekcie Calc w książce Java 3. Dlatego możesz ponownie wykorzystać klasy z tego projektu do rzeczywistego problemu. 4 ELEMENTY SWING Swing zapewnia wiele dostępnych komponentów, a poniżej wymieniono najczęściej: JButton JPanel JCheckBox JRadioButton ButtonGroup JComboBox JComponent JLabel JList JMenuBar JMenuItem JMenu JRadioButtonMenuItem JCheckBoxMenuItem JPopupMenu JScrollPane JScrollBar JTextArea JTextField JPasswordField JTextPane JEditorPane JSpinner JSlider JToogleButton JProgressBar JFormattedTextField JTable JTree JToolTip JToolBar JSeparator JDesktopPanel JInternalFrame JOptionPane JColorChooser JSplitPane JTabbedPane Wiele z tych komponentów zostało wcześniej wykorzystanych i omówionych i nie należy ich tutaj dalej przetwarzać, a ogólnie poszczególne komponenty są używane w ten sam sposób. W szczególności chciałbym przypomnieć JTable, o którym mowa w książce Java 6. Niektóre komponenty są używane specjalnie do wprowadzania tekstu, gdzie JTextField i JTextArea zostały wcześniej omówione, ale inne są omówione w rozdziale Edytuj tekst w dalszej części tego rozdziału książka. W tym rozdziale chciałbym przyjrzeć się dwóm składnikom: JProgressBar i JTree.

49 4.1 JPROGRESSBAR Pasek postępu to składnik, który wizualnie pokazuje postęp w procesie, na przykład w celu pobrania pliku z serwera WWW. Pasek postępu pokaże użytkownikowi, że coś się dzieje, a program zostanie zatrzymany i czeka. Na przykład następujące okno zawiera pasek postępu który znajduje się w górnej części okna po prawej stronie przycisku. Okno zawiera także pole listy i pole wprowadzania. Kliknięcie przycisku powinno symulować rozpoczęcie zadania, które wymaga czasu, a pasek postępu pokaże pęd pracy: W dolnym polu wprowadzania możesz wpisać tekst - i nic więcej - a intencją jest pokazanie, że możesz pracować z oknem podczas wykonywania zadania. Oczywiste jest, że proces musi rozpocząć zadanie we własnym wątku, a wątek musi następnie okresowo aktualizować zarówno pasek postępu, jak i pole listy. Zasadniczo jest to dość proste, ale gdy zadanie jest wykonywane w innym wątku niż wątek dyspersyjny Swing, konieczne jest wykonanie kilku kroków w celu zapewnienia poprawnej aktualizacji interfejsu użytkownika. package progressprogram1; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.beans.*; import javax.swing.border.*; public class MainView extends JFrame

50 private DefaultListModel model = new DefaultListModel(); private JButton cmdstart; private JProgressBar progressbar; private JTextField txtfield; public MainView() super("progress"); setsize(400, 300); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout(0, 10)); panel.setborder(new EmptyBorder(10, 10, 10, 10)); panel.add(createtop(), BorderLayout.NORTH); panel.add(txtfield = new JTextField(), BorderLayout.SOUTH); panel.add(new JScrollPane(createList())); add(panel); private JPanel createtop() JPanel panel = new JPanel(new BorderLayout(10, 0)); panel.add(cmdstart = createbutton(), BorderLayout.WEST); panel.add(progressbar = createprogress()); return panel; private JButton createbutton() JButton cmd = new JButton("Start"); cmd.addactionlistener(new ActionHandler()); return cmd; private JProgressBar createprogress() JProgressBar bar = new JProgressBar(0, 100); bar.setvalue(0); bar.setstringpainted(true);

51 return bar; private JList createlist() JList list = new JList(model); return list; class ActionHandler implements ActionListener public void actionperformed(actionevent e) cmdstart.setenabled(false); model.clear(); txtfield.settext(""); setcursor(cursor.getpredefinedcursor(cursor.wait_cursor)); Task task = new Task(); task.addpropertychangelistener(new PropertyChangeHandler()); task.execute(); txtfield.requestfocus(); class PropertyChangeHandler implements PropertyChangeListener public void propertychange(propertychangeevent e) if ("progress" == e.getpropertyname()) progressbar.setvalue((integer) e.getnewvalue()); model.addelement((string.format( "Completed %d%% of the task", ((Task)e.getSource()).getProgress())));

52 class Task extends SwingWorker<Void, Void> public Void doinbackground() Random random = new Random(); int progress = 0; setprogress(0); while (progress < 100) try Thread.sleep(random.nextInt(1000)); catch (Exception ex) progress += random.nextint(10); setprogress(math.min(progress, 100)); return null; public void done() cmdstart.setenabled(true); setcursor(null); model.addelement("done!"); Jeśli chodzi o układ okna, nie jest niczym nowym, ale klasa tworzy JProgressBar i dodaje go do okna. Powinieneś zauważyć, w jaki sposób tworzony jest komponent, w którym określasz wartość minimalną i maksymalną, a pasek postępu ma cały czas wartość z tego zakresu. Zwróć uwagę zwłaszcza na oświadczenie bar.setstringpainted (true);

53 co oznacza, że pasek postępu pokaże wartość (tekst). Klasa ma trzy klasy wewnętrzne, dwie procedury obsługi zdarzeń i klasę Zadanie reprezentujące zadanie. Klasa pochodzi od SwingWorker. Ta klasa została opisana w ostatnim rozdziale książki, ale jej celem jest ułatwienie aktualizacji interfejsu użytkownika z innego wątku. SwingWorker to klasa abstrakcyjna, a istnieją dwie abstrakcyjne metody do zaimplementowania. Metoda doinbackground () uruchamia wątek, w tym przypadku metoda symuluje pracę, która wymaga czasu. Klasa ma powiązaną właściwość o nazwie postęp, dlatego klasa wysyła powiadomienia PropertyChange do detektorów, gdy ta właściwość zostanie zmieniona. Metoda done () to kolejna metoda abstrakcyjna, która jest wykonywana po zakończeniu wątku. Należy szczególnie zauważyć, że zadanie klasy nie zna paska postępu, a zadanie klasy wykonuje tylko sporadycznie właściwość obsługi zdarzeń właściwośćchange (). Jeśli spojrzysz na procedurę obsługi zdarzeń actionperformed (), która jest wykonywana, gdy użytkownik kliknie przycisk, więc wyłącza przycisk, usuwa model (dla pola listy) i pola wejściowego, ustaw kursor, a następnie utworzył zadanie obiekt. Następnie procedura obsługi zdarzeń rejestruje detektor jako obiekt PropertyChangeHandler, po czym zadanie (wątek) jest uruchamiane za pomocą metody execute (). Powoduje to, że za każdym razem, gdy obiekt Task wykonuje setprogress (), wówczas wykonywana jest procedura obsługi zdarzeń propertychange (), ale w wątku dyspozytora. W rezultacie pasek postępu i model pola listy są aktualizowane. Program ProgressProgram2 ma ten sam interfejs użytkownika i działa w zasadzie w ten sam sposób, ale są dwie różnice. Pasek postępu jest wyświetlany w inny sposób, a zadanie wykonuje coś innego. Pasek postępu pokazuje tym razem nie tekst i procent wskazujący, jak daleko jest osiągnięte zadanie, ale pokazuje animację z prostokątem, który zmienia się od lewej do prawej iz powrotem. Czasami obliczenie procentu wykonanego zadania może być trudne (niemożliwe), dlatego ten pasek postępu może być lepszy. Zadanie jest tym razem zdefiniowane przez następującą klasę: class Task extends SwingWorker<Void, Void> private static final int N = 5; public Void doinbackground() setprogress(0); progressbar.setindeterminate(true); long n = Long.MAX_VALUE; for (int i = 0; i < N; ++i, n -= 2) while (n > 0 &&!isprime(n)) n -= 2; prime = n; setprogress(100 * (i + 1) / N); return null;

54 public void done() cmdstart.setenabled(true); progressbar.setindeterminate(false); model.addelement("done!"); private boolean isprime(long n) if (n == 2 n == 3 n == 5 n == 7) return true; if (n < 11 n % 2 == 0) return false; for (long t = 3, m = (long)math.sqrt(n) + 1; t <= m; t += 2) if (n % t == 0) return false; return true; Odnotuj najpierw instrukcję: progressbar.setindeterminate (true); która jest instrukcją rozpoczynającą animację. Zadanie polega na ustaleniu 5 dużych liczb pierwszych, a celem jest jedynie pokazanie zadania, które wymaga czasu - prawdziwe zadanie, które wykorzystuje procesor maszyny. W przeciwnym razie nie ma tak wiele nowych, a aplikacja działa w zasadzie dokładnie tak samo jak w pierwszym przykładzie. Na koniec przykładowy ProgressProgram3, który działa dokładnie tak samo jak ProgrssProgram2, ale różnica polega na tym, że klasa Task tym razem nie dziedziczy SwingWorker. Zamiast tego implementuje interfejs Runnable, a wątek musi mieć pewność, że aktualizacje interfejsu użytkownika zostaną wykonane w wątku programu rozsyłającego zdarzenia: class Task implements Runnable private static final int N = 5; public void run() try updatestate(false); updatebar(0); progressbar.setindeterminate(true); long n = Long.MAX_VALUE; for (int i = 0; i < N; ++i, n -= 2) while (n > 0 &&!isprime(n)) n -= 2; updateprime(n); updatebar(100 * (i + 1) / N);

55 updatestate(true); catch (Exception ex) finally progressbar.setindeterminate(false); private void updatebar(int value) try SwingUtilities.invokeAndWait(() -> progressbar.setvalue(value)); catch (Exception ex) private void updateprime(long prime) try SwingUtilities.invokeAndWait(() -> model.addelement(prime)); catch (Exception ex)

56 private void updatestate(boolean onoff) try SwingUtilities.invokeAndWait(() -> cmdstart.setenabled(onoff)); catch (Exception ex) private boolean isprime(long n) if (n == 2 n == 3 n == 5 n == 7) return true; if (n < 11 n % 2 == 0) return false; for (long t = 3, m = (long)math.sqrt(n) + 1; t <= m; t += 2) if (n % t == 0) return false; return true; Zasadniczo ten program działa w ten sam sposób, ale daje programiście większą swobodę w odniesieniu do korzystania z paska postępu, ale odwrotnie, przykład oznacza również, że jest wiele rzeczy, o które programista musi dbać. 4.2 JTREE JTree to komponent, który może wizualnie wyświetlać dane hierarchiczne, a tak naprawdę znasz go z JFileChooser, gdzie komponent służy do przeglądania systemu plików. Rzeczywisty komponent Swing musi wizualizować hierarchię, a dane, które mają być wizualizowane, pochodzą z modelu danych i podobnie jak na przykład komponent JTable, JTree używa kilku klas pomocniczych, tak jak w tym przypadku można je znaleźć w javax.swing. drzewo. JTree jest złożoną klasą z wieloma właściwościami i ustawieniami, a poniżej zilustruję sposób użycia komponentu. Chciałbym zacząć od aplikacji, która otwiera następujące okno:

57 Okno pokazuje JTree w JScrollPane i hierarchię (drzewo), która w tym przypadku składa się tylko z listy nazw, ale taka, że wszystkie nazwy są węzłami potomnymi pod katalogiem głównym. Po kliknięciu katalogu głównego drzewa możesz zwinąć drzewo: i oczywiście możesz ponownie rozwinąć drzewo. Kod jest pokazany poniżej: package treeprogram1; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class MainView extends JFrame private Object[] model = "Gorn dengamle", "Harald Blåtand", "Svend Tveskæg", "Harald d. 2.", "Knud den Store", "Hardeknud", "Magnus den Gode", "Svend Estridsen", "Harald Hen", "Knud den Hellige", "Oluf Hunger", "Erik Ejegod", "Niels" ; public MainView() super("jtree 1"); setsize(300, 200); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true);

58 private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(20, 20, 20, 20)); JTree tree = new JTree(model); tree.setrootvisible(true); panel.add(new JScrollPane(tree)); add(panel); W createview () tworzony jest komponent JTree, który jest inicjowany za pomocą modelu danych, który jest tablicą obiektów. W rzeczywistości konstruktor w JTree tworzy model danych, który jest bardzo płaskim modelem danych, w którym wszystkie węzły są umieszczone na liście poniżej katalogu głównego. Zwróć także uwagę na instrukcję, która mówi o pojawieniu się katalogu głównego. Oznacza to, że nie musisz pokazywać korzenia, jeśli nie chcesz. Domyślnie JTree wyświetla hierarchię obiektów, a dla każdego obiektu wyświetlany jest wynik funkcji tostring () obiektu. W tym przypadku model danych został zdefiniowany jako tablica obiektów Object, a konstruktor w klasie JTree automatycznie utworzy model, który jest drzewem składającym się z obiektów TreeNode. Istnieje kilka konstruktorów, a wśród innych konstruktorów, które jako parametry mają odpowiednio Hashtable i Vector. Pokażę przykład, jak stworzyć JTree oparty na wektorze. Wektor jest zasadniczo taki sam jak ArrayList, ale jest starszą klasą kolekcji, która istniała od narodzin języka. Od tego czasu klasa została zmodernizowana w wersji ogólnej, ale użyję oryginalnej wersji. Należy powiedzieć, że na ogół nie zaleca się używania klasy Vector, ale zamiast ArrayList, ale konstruktor w JTree obsługuje tylko Vector, dlatego też. Klasa Vector jest bezpieczna dla wątków, dlatego ArrayList ma lepszą wydajność. Przykład nazywa się TreeProgram2 i otwiera następujące okno: Tym razem wyświetla hierarchię, w której poszczególne gałęzie można zamknąć i otworzyć, klikając myszką. Pamiętaj, że katalog główny nie jest wyświetlany. Program jest napisany w następujący sposób:

59 package treeprogram2; import java.util.*; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class MainView extends JFrame public MainView() super("jtree 2"); setsize(300, 200); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(20, 20, 20, 20)); JTree tree = new JTree(createVector("", "Gorm den Gamle", "Harald Blåtand", createvector("svend Tveskæg", "Harald d. 2.", createvector("knud den Store", "Hardeknud", "Magnus den Gode")), createvector("svend Estridsen", "Harald Hen", "Knud den Hellige", "Oluf Hunger", "Erik Ejegod", "Niels"))); tree.setrootvisible(false); panel.add(new JScrollPane(tree)); add(panel); private Vector createvector(string name, Object objects) Vector v = new java.util.vector() public String tostring() return name; ; for (Object obj : objects) v.add(obj); return v;

60 Metoda createvector () tworzy Vector, który jest zbiorem obiektów, których JTree może użyć do stworzenia modelu danych. Wektor sam jest przedmiotem i dlatego może być elementem innego wektora, tworząc w ten sposób hierarchię. Metoda tworzy sam Vector na podstawie anonimowej klasy (klasa dziedzicząca Vector) w celu zastąpienia metody tostring (). Konstruktor ma wartość, którą musi zwrócić tostring (), a w przeciwnym razie obiekty, które klasa powinna zawierać. Hierarchia jest zdefiniowana w konstruktorze JTree, a kod nie jest tak naprawdę czytelny, więc może lepiej byłoby napisać coś takiego: Vector v1 = createvector("knud den Store", "Hardeknud", "Magnus den Gode"); Vector v2 = createvector("svend Tveskæg", "Harald d. 2."); v2.add(v1); Vector v3 = createvector("svend Estridsen", "Harald Hen", "Knud den Hellige", "Oluf Hunger", "Erik Ejegod", "Niels"); Vector v4 = createvector("", "Gorm den Gamle", "Harald Blåtand"); v4.add(v2); v4.add(v3); JTree tree = new JTree(v4); Gdy konstruktor w klasie JTree buduje model danych, jest to obiekt typu TreeModel, który składa się z hierarchii obiektów TreeNode. Oba typy są interfejsami i oczywiście intencją jest napisanie własnego modelu, ale w zdecydowanej większości przypadków można użyć klas domyślnych, które są częścią API Swing. Model jest implementowany przez klasę DefaultTreeModel, ale z poziomu programisty najważniejsza jest klasa DefaultMutableTreeNode, która implementuje interfejs TreeNode (i jego interfejs podrzędny, zwany MutableTreeNode). Typowym zastosowaniem JTree jest zbudowanie hierarchii składającej się z obiektów DefaultMutableTreeNode, a następnie wykorzystanie tej hierarchii jako parametru konstruktora w klasie JTree. Program TreeProgram3 otwiera okno pokazane poniżej. W oknie znajduje się JTree, który pokazuje przegląd kart do gry. Dwukrotne kliknięcie określonej karty (węzła liścia) spowoduje wyświetlenie karty w komponencie JLabel. Aby napisać program, należy rozwiązać dwa problemy: 1. zbudować model danych jako hierarchię pokazującą karty do gry 2. Aby przechwycić, kliknij dwukrotnie element w drzewie Projekt definiuje następującą klasę, która reprezentuje odpowiednio kartę do gry o nazwie i obrazie: package treeprogram3; import javax.swing.*; public class Card

61 private String name; private ImageIcon img; public Card(String name, ImageIcon img) this.name = name; this.img = img; public ImageIcon getimage() return img; public String tostring() return name; Nazwa to tekst zwracany przez klasę tostring (), a zatem tekst wyświetlany w składniku JTree. Po udostępnieniu tej klasy model danych może mieć następującą strukturę (gdzie pokazałem tylko niewielką część metody): private TreeModel createmodel() MutableTreeNode root = new DefaultMutableTreeNode("The cards"); MutableTreeNode diam = new DefaultMutableTreeNode("Diamonds"); MutableTreeNode hear = new DefaultMutableTreeNode("Hearts"); MutableTreeNode spad = new DefaultMutableTreeNode("Spades"); MutableTreeNode club = new DefaultMutableTreeNode("Clubs"); diam.insert(createnode("diamond two", "/swing14/images/ru2.gif"), 0); diam.insert(createnode("diamond three", "/swing14/images/ru3.gif"), 1); diam.insert(createnode("diamond four", "/swing14/images/ru4.gif"), 2);

62 root.insert(diam, 0); root.insert(hear, 1); root.insert(spad, 2); root.insert(club, 3); return new DefaultTreeModel(root); Klasa DefaultMutableTreeNode definiuje węzeł dla drzewa i można określić obiekt, który ma być powiązany z tym węzłem. Jest to wartość tostring () tego obiektu wyświetlana w drzewie. Metoda rozpoczyna się od utworzenia 5 węzłów, z których pierwszy będzie użyty jako root, a cztery pozostałe będą musiały zostać użyte dla każdego z czterech kolorów. Klasa DefaultMutableTreeNode ma metodę insert (), a następnie jest używana do kojarzenia węzłów potomnych z węzłami reprezentującymi kolory. Jednym z przykładów jest diam.insert (createnode ( Diament dwa, /treeprogram3/images/ru2.gif ), 0); który wiąże kartę diament 2 z węzłem diamentów, a zatem metoda ma dodatkowo 51 odpowiednich instrukcji. Metoda createnode () tworzy i zwraca DefaultMutableTreeNode, do którego dołączony jest obiekt Card. Oznacza to między innymi, że obraz tej karty musi zostać załadowany z pliku jar programu, co odbywa się w taki sam sposób, jak pokazano w poprzednich przykładach. Na koniec cztery węzły do kolorów są dodawane w węźle głównym, a metoda zwraca model do JTree. Następnie następuje dwukrotne kliknięcie myszą iw tym celu MouseListener jest dołączony do komponentu JTree: class MouseHandler extends MouseAdapter public void mousepressed(mouseevent e) int row = tree.getrowforlocation(e.getx(), e.gety()); if(row!= -1 && e.getclickcount() == 2) TreePath path = tree.getpathforlocation(e.getx(), e.gety()); MutableTreeNode node = (MutableTreeNode)path.getLastPathComponent(); if (node.isleaf()) Card card = (Card)((DefaultMutableTreeNode)node).getUserObject(); lblcard.seticon(card.getimage());

63 Pierwsza instrukcja w mousepressed () określa indeks w wybranym wierszu. Jeśli wybrany jest wiersz i jest to podwójne kliknięcie, ścieżka (w hierarchii drzewa) jest określana do klikniętego węzła. Taka ścieżka jest reprezentowana przez TreePath, która jest listą węzłów od katalogu głównego do bieżącego węzła. Na przykład, jeśli kliknięcie jest na piku siódmym, lista - obiekt TreePath - składa się z rdzenia, węzła reprezentującego pik, a węzeł reprezentował pik siedem. Gdy dostępny jest obiekt TreePath, określa się ostatni węzeł na ścieżce, który jest węzłem podwójnie klikniętym. Ponieważ po kliknięciu węzła dla konkretnej karty trzeba tylko coś zrobić, zostanie sprawdzone, czy kliknięty węzeł jest węzłem liścia. W takim przypadku określany jest obiekt Card przypisany do węzła, który służy do aktualizacji JLabel przy użyciu tej karty. JTree rodzi się z TreeCellRenderer, który określa, jak należy narysować komponent, i dokładnie powie użyte ikony i tekst. Jeśli otworzysz program TreeProgram4, pojawi się następujące okno: Oznacza to, że komponent JTree wyświetla teraz obraz tej karty, a także pokazuje, że wybrany jest węzeł. Poza tym jest to ten sam program, co TreeProgram3, ale program definiuje własny TreeCellRenderer, który w tym przypadku jest klasą wewnętrzną: public class LeafCellRenderer extends DefaultTreeCellRenderer private JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); private JLabel lblicon = new JLabel(); private JLabel lbltext = new JLabel();

64 public LeafCellRenderer() lblicon.setpreferredsize(new Dimension(28, 36)); lbltext.sethorizontalalignment(jlabel.left); lbltext.setfont(tree.getfont()); panel.setbackground(color.white); panel.add(lblicon); public Component gettreecellrenderercomponent(jtree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasfocus) DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; if (!node.isleaf()) return super.gettreecellrenderercomponent(tree, value, selected, expanded, leaf, row, hasfocus); preparerenderer((card)node.getuserobject(), selected); return panel; private void preparerenderer(card card, boolean selected) lblicon.seticon(scale(card.getimage())); lbltext.setforeground(selected? Color.red : Color.black); lbltext.settext(card.tostring()); private ImageIcon scale(imageicon icon) return new ImageIcon( icon.getimage().getscaledinstance(24, 32, java.awt.image.scale_smooth)); Zauważ, że klasa pochodzi od DefaultTreeCellRenderer, który jest domyślnym rendererem używanym przez JTree. Klasa definiuje panel z dwoma komponentami JLabel, które są używane odpowiednio dla ikony i tekstu. Te komponenty są inicjowane w konstruktorze. Klasa musi zastąpić metodę gettreecellrenderercomponent (), która jest metodą renderującą element w drzewie. Metoda najpierw sprawdza, czy jest węzłem liścia, a jeśli nie, wywołuje metodę klasy bazowej, a drzewo jest

65 renderowane jako domyślne. W przeciwnym razie metoda wywołuje metodę preparrenderer (), która inicjuje dwa składniki etykiety. Aby wszystko działało, drzewo musi używać TreeCellRenderer: tree.setcellrenderer (new LeafCellRenderer ()); a następnie drzewo zastosuje niestandardowy TreeCellRenderer. Możesz także edytować poszczególne węzły w JTree i możesz to zrobić za pomocą polecenia tree.seteditable (true); Składnik korzysta z DefaultTreeCellEditor, a dwukrotne kliknięcie węzła otwiera składnik JTextField, umożliwiając edycję tekstu. Może nie tak często mieć zainteresowanie, ale oczywiście istnieją przykłady i możesz zdefiniować własnego TreeCellEditor dokładnie tak, jak w TreeCellRender i dołączyć go do drzewa. DANIA Baza danych padata zawiera trzy tabele region, gmina i kod pocztowy zawierający informacje o duńskich regionach i gminach, a także kody pocztowe stosowane przez gminy. Przykład DenmarkProgram pokazuje te dane w JTree. Ponadto program ma funkcję wyszukiwania, w której można wyszukiwać tekst, i wybierane są wszystkie węzły w drzewie, w których tekst zawiera szukany ciąg. Poniżej znajduje się przykład okna aplikacji, w której wyszukiwane było hasło Skive: Program ma trzy proste klasy modeli 1. Region

66 2. Gmina 3. Kod pocztowy gdzie pierwsza zawiera listę wszystkich gmin w tym regionie, a druga zawiera listę wszystkich kodów pocztowych używanych przez tę gminę. Nie chcę tutaj pokazywać tych klas modeli. Istnieje również klasa modelowa o nazwie Dania, która zawiera tylko listę wszystkich regionów. Wreszcie istnieje model klasy, który w konstruktorze odczytuje tabele bazy danych i buduje strukturę drzewa z obiektem typu Dania jako korzeniem. Klasa zawiera obiekty regionu, a każdy obiekt regionu zawiera obiekty gminy, które z kolei zawierają obiekty kodu pocztowego. Nie chcę też tutaj pokazywać klasy Model, która składa się wyłącznie z metod odczytujących odpowiednie tabele bazy danych. Z tyłu znajduje się widok główny programu, w którym istnieje kilka koncepcji dotyczących komponentu JTree, o których powinieneś wiedzieć. Kod jest następujący: package denmarkprogram; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.border.*; public class MainView extends JFrame private Model model = new Model(); private JTree tree; private JTextField txttext = new JTextField(); public MainView() super("denmark"); setsize(500, 500); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout(0, 20)); panel.setborder(new EmptyBorder(20, 20, 20, 20)); tree = new JTree(createTreeModel()); tree.setrootvisible(true); panel.add(new JScrollPane(tree)); panel.add(createbottom(), BorderLayout.SOUTH); add(panel);

67 private JPanel createbottom() JButton cmd = new JButton("Search"); cmd.addactionlistener(this::select); JPanel panel = new JPanel(new BorderLayout(20, 0)); panel.add(cmd, BorderLayout.EAST); panel.add(txttext); return panel; private void select(actionevent e) TreeNode root = (TreeNode)tree.getModel().getRoot(); collapse(getpath(root)); String text = txttext.gettext().trim(); tree.clearselection(); ArrayList<TreePath> paths = new ArrayList(); for (int i = 0; i < root.getchildcount(); ++i) select(paths, (DefaultMutableTreeNode)root.getChildAt(i), text); TreePath[] array = new TreePath[paths.size()]; tree.setselectionpaths(paths.toarray(array)); private void select(arraylist<treepath> paths, DefaultMutableTreeNode node, String text) if (node.getuserobject().tostring().contains(text)) paths.add(getpath(node)); for (int i = 0; i < node.getchildcount(); ++i) select(paths, (DefaultMutableTreeNode)node.getChildAt(i), text); private void collapse(treepath root) TreeNode node = (TreeNode) root.getlastpathcomponent(); if (node.getchildcount() >= 0) for (Enumeration e = node.children(); e.hasmoreelements(); ) TreePath path = root.pathbyaddingchild((treenode) e.nextelement()); collapse(path);

68 tree.collapsepath(root); private TreePath getpath(treenode node) ArrayList<Object> nodes = new ArrayList(); nodes.add(node); for (node = node.getparent(); node!= null; node = node.getparent()) nodes.add(0, node); return new TreePath(nodes.toArray()); private TreeNode createtreemodel() DefaultMutableTreeNode root = new DefaultMutableTreeNode(model.getDenmark()); int n1 = 0; for (Region r : model.getdenmark().getregions()) MutableTreeNode node1 = new DefaultMutableTreeNode(r); int n2 = 0; for (Municipality m : r.getmunicipalities()) MutableTreeNode node2 = new DefaultMutableTreeNode(m); int n3 = 0; for (Zipcode z : m.getzipcodes()) node2.insert(new DefaultMutableTreeNode(z), n3++); node1.insert(node2, n2++); root.insert(node1, n1++); return root; Program definiuje trzy zmienne, które są obiektem Model, a tym samym danymi dla drzewa. Ponadto istnieje zmienna dla komponentu JTree i wpisywanego tekstu do wyszukiwania fi W odniesieniu do projektu okna nie ma w nim żadnych nowych elementów, dlatego nie należy go tutaj komentować. Ostatnia metoda buduje model danych drzewa w oparciu o obiekt modelu, a tym samym dane odczytane w bazie danych. Model danych jest zbudowany jako hierarchia składająca się z węzłów typu

69 MutableTreeNode. Pierwszy węzeł reprezentuje korzeń drzewa, a tym samym obiekt duński. Do tego węzła tworzony jest węzeł regionu dla każdego regionu i węzeł dla gmin regionu, a na koniec dla każdej gminy węzeł dla każdego kodu pocztowego używanego przez tę gminę. Metodę tę stosuje się w createview () do utworzenia obiektu JTree i należy zauważyć, że argumentem konstruktora jest zatem TreeNode dla katalogu głównego drzewa. Poszczególne węzły w JTree mogą być przywoływane na kilka sposobów, ale najważniejsze jest TreePath, czyli lista węzłów prowadzących od katalogu głównego do konkretnego węzła. W tym miejscu należy zwrócić uwagę na metodę getpath (), która określa metodę TreePath dla określonego węzła. Zakłada się tutaj, że wszystkie węzły niebędące katalogiem głównym mają odniesienie nadrzędne do węzła, z którego ten węzeł jest dzieckiem. Następnie należy zwrócić uwagę na metodę collapse (), która zwija część drzewa. Metoda ma parametr TreePath i zwija tę część drzewa, która ma węzeł ścieżki jako root. W szczególności należy zwrócić uwagę na sposób odwoływania się do węzła reprezentowanego przez ścieżkę TreePath. Zauważ też, że metoda jest rekurencyjna i jak zwinąć węzeł główny za pomocą instrukcji tree.collapsepath (root); Jeśli zamiast tego zastąpisz to stwierdzenie tree.expandpath (root); metoda całkowicie rozszerzy drzewo. Z tyłu znajduje się moduł obsługi zdarzenia dla przycisku. Zaczyna się od określenia korzenia drzewa jako węzła, a następnie zwija całe drzewo. Następnie tworzona jest ArrayList dla obiektów TreePath i wykonywana jest pętla nad wszystkimi węzłami potomnymi do katalogu głównego. Każda iteracja wywołuje metodę select (), która tworzy obiekt TreePath dla wszystkich węzłów pasujących do szukanego ciągu. Ta metoda jest również rekurencyjna, ale wynik jest taki, że ArrayList zawiera wszystkie węzły, które mają być reprezentowane przez TreePath. Lista jest wreszcie używana do oznaczania poszczególnych węzłów tree.setselectionpaths (paths.toarray (tablica)); co oznacza, że poszczególne węzły są wybrane i drzewo jest rozwinięte, aby wszystkie wybrane węzły były widoczne. 5. KOMPONENTY OKREŚLONE PRZEZ UŻYTKOWNIKA W pierwszych rozdziałach tej książki wspomniałem o niestandardowych komponentach, a nawet podałem dwa przykłady, aw tym rozdziale dołączę dodatkowe uwagi na temat tworzenia dobrych

70 niestandardowych komponentów. Jak opisano w poprzednim rozdziale, Swing rodzi się z wieloma komponentami, które oferują większość tego, co może być potrzebne do zaprojektowania graficznego interfejsu użytkownika, ale z drugiej strony mogą istnieć specjalne potrzeby, w których chcesz opracować własne komponenty, i, z drugiej strony okno może zawierać panele, które będą używane w wielu miejscach, dlatego warto opracować własne komponenty. W każdym razie motywacją do napisania niestandardowego komponentu będzie zawsze potrzeba elementu graficznego, który może być używany w wielu miejscach, a zwykle nawet w zupełnie innych programach niż bieżący. Zasadniczo pisanie niestandardowych komponentów za pomocą Swinga jest łatwe, ale jeśli zrobisz to poprawnie, a zatem napiszesz komponenty, które zachowują się jak każdy inny komponent Swing, nie jest to całkiem proste i istnieje kilka wymagań, które musisz zapewnić, aby komponent niestandardowy był zgodny. Komponenty Swing są zaprojektowane zgodnie ze zmodyfikowanym wzorcem MVC, który można opisać jako widok i kontroler wbudowane we wspólny komponent zwany delegatem interfejsu użytkownika: Kiedy wspomniałem powyżej, że pisanie niestandardowych komponentów nie jest proste, niestandardowe komponenty powinny być zgodne z tym samym wzorem, co własne komponenty Swing, więc zacznę trochę o architekturze komponentów swing i ich konstrukcji. 5.1 LOOK-AND-FEEL Delegat interfejsu użytkownika pochodzi z klasy ComponentUI, która jest klasą abstrakcyjną, która opisuje w zasadzie sposób, w jaki delegat interfejsu użytkownika i komponent powinny się komunikować, i bez żadnego wyjaśnienia klasa (definiuje) ma następujące metody: - static ComponentUI CreateUI(JComponent c) - installui(jcomponent c) - uninstallui(jcomponent c) - update(graphics g, JComponent c) - paint(graphics g, JComponent c) - getpreferredsize(jcomponent c) - getminimumsize(jcomponent c) - getmaximumsize(jcomponent c)

71 Większość delegatów interfejsu użytkownika jest pisanych, aby znać model komponentu (lub modele jako komponent mogą mieć wiele obiektów modelu) za pośrednictwem zdefiniowanego interfejsu. Swing ma wiele rodzin delegatów interfejsu użytkownika, a każda rodzina zawiera klasę (która implementuje ComponentUI) dla większości komponentów Swing, a taka rodzina nazywa się look-andfeel (lub PLAF dla plug-and-look-and-look). javax.swing.plaf zawiera klasy abstrakcyjne, wszystkie pochodzące z ComponentUI i javax. swing.plaf.basic składa się z klas, które rozszerzają klasy abstrakcyjne z javax.swing.plaf o określone klasy. Te klasy delegatów interfejsu użytkownika są używane jako elementy składowe wszystkich wyglądających rodzin. Istnieją następujące rodziny, odpowiednio Windows, Motif i Metal: 1. com.sun.java.swing.plaf.windows.windowslookandfeel 2. com.sun.java.swing.plaf.motif.motiflookandfeel 3. javax.swing.plaf.metal.metallookandfeel gdzie ten ostatni jest domyślny (i istnieje również MacLookAndFell, ale musi być zainstalowany). Jednak danego wyglądu można użyć tylko wtedy, gdy jest on na właściwej platformie, a na przykład pierwszego z powyższych nie można użyć na komputerze z systemem Linux. Wygląd i elementy interfejsu użytkownika definiują sposób wyświetlania komponentów Swing na ekranie i zazwyczaj przy wsparciu z bieżącej platformy, a tak naprawdę można zmienić wygląd w czasie wykonywania. Po otwarciu programu PLAFProgram pojawi się następujące okno: który ma 9 komponentów ułożonych za pomocą GridLayout. Wygląd i styl okna to Metal, który jest domyślny, ale jeśli klikniesz przycisk Motyw, okno zmieni się na: Wynika to z faktu, że wygląd zmienia się z Metal na Motif. Jeśli następnie kliknę przycisk Windows, otrzymam wyjątek, ale dzieje się tak, ponieważ mój program działa na komputerze z systemem Linux. Zamiast tego, klikając przycisk Dialog, program otwiera okno komunikatu:

72 gdzie wygląd wynika z tego, że wygląd to Motif. Przykład pokazuje, że sposób wyświetlania okien i komponentów programu zależy od bieżącego wyglądu. Kod programu jest następujący: package plafprogram; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class MainView extends JFrame public MainView() super("plaf Program"); setsize(500, 220); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new GridLayout(3, 3, 20, 20)); panel.setborder(new EmptyBorder(20, 20, 20, 20)); JLabel lbl = new JLabel("Look & Feel"); panel.add(lbl); panel.add(new JTextField()); panel.add(new JCheckBox("Check box")); panel.add(new JRadioButton("Radio button")); panel.add(new JComboBox()); panel.add(createbutton("dialog", e -> JOptionPane.showMessageDialog(this, "Hello World"))); panel.add(createbutton("metal", this::metal)); panel.add(createbutton("motif", this::motif)); panel.add(createbutton("windows", this::windows)); add(panel); private JButton createbutton(string text, ActionListener listener)

73 JButton cmd = new JButton(text); cmd.addactionlistener(listener); return cmd; private void metal(actionevent e) try UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); SwingUtilities.updateComponentTreeUI(this); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); private void motif(actionevent e) try UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); SwingUtilities.updateComponentTreeUI(this); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); private void windows(actionevent e)

74 try UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.windowslookandfeel"); SwingUtilities.updateComponentTreeUI(this); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); i tutaj powinieneś oczywiście przede wszystkim zauważyć, jak zmienić wygląd, a zwłaszcza, jak nazywane są poszczególne rodziny delegatów interfejsu użytkownika. Widok wyglądu zapewnia oczywiście, że programy z użytkownikiem graficznym interfejs wygląda jak inne programy na konkretnej platformie (Windows, Mac) i może być ważne, jeśli musisz napisać własne niestandardowe komponenty od zera, ponieważ może być konieczne napisanie delegatów interfejsu użytkownika do indywidualnego wyglądu. Wreszcie, oznacza to, że w zasadzie możesz napisać własny wygląd (co nie jest dość proste), określając w ten sposób, jak powinny zachowywać się komponenty. 5.2 KNOB Przykład KnobProgram otwiera okno z ośmioma komponentami umieszczonymi w oknie za pomocą FlowLayout:

75 Istnieją cztery komponenty JLabel i cztery komponenty Knob. Jest to niestandardowy komponent, który podobnie jak JScrollBar i JSlider hermetyzuje wartość w przedziale czasowym. Możesz zmienić wartość, przeciągając mały okrąg (magenta). Cztery komponenty etykiety pokazują wartość komponentu Gałka i są aktualizowane za każdym razem, gdy wartość jest zmieniana. Składnik pokrętła ma również dolną i górną wartość między 0 a 1, wskazując obszar niebieski (zimny), obszar czerwony (gorący) i obszar żółty (normalny). Jeśli zmienisz obszar przez przeciągnięcie pokrętła, komponent wyzwoli zdarzenie. Poniżej pokażę, w jaki sposób komponent jest rozwijany. W tej sekcji zacznę od prostej wersji, która nie jest w pełni kompatybilna z własnymi komponentami Swing, ale poprawię ją w następnej sekcji. Punktem wyjścia dla niestandardowego komponentu jest klasa, która dziedziczy JComponent - lub inny komponent, a zwłaszcza kontener jako JPanel. Zaczynając od opracowania komponentu pochodzącego z JComponent, możesz powiedzieć, że zaczynasz od zera. Komponent może zwykle wywoływać zdarzenia, a zatem będzie miał skojarzone detektory, dla których mogą być wysyłane powiadomienia. W takim przypadku powiadomienie musi zostać wysłane, gdy właściwości komponentu (jest ich pięć) zostaną zmienione, ale ponieważ klasa dziedziczy JComponent, ma już wymaganą logikę dla PropertyChangeEvent, aby obiekty PropertyChangeListener mogły zostać zarejestrowane jako detektory. Klasa musi zatem zająć się tylko podnoszeniem zdarzeń dla nowych pięciu właściwości. Jak wspomniano powyżej, klasa musi również uruchomić zdarzenie, gdy wartość

76 komponentu zmienia się między granicami trzech obszarów (zimny, normalny i gorący) i dla tego zdefiniowałem następujące zdarzenie: package knobprogram; import java.util.*; public class KnobEvent extends EventObject private int value; private int from; private int to; public KnobEvent(Knob source, int value, int from, int to) super(source); this.value = value; this.from = from; this.to = to; public int getvalue() return value; public int getfrom() return from; public int getto() return to; gdzie parametrami zdarzenia jest bieżąca wartość komponentu, stan, który zmienił się i stan, który został zmieniony na. Należy zauważyć, że klasa pochodzi z EventObject. Jest to klasa podstawowa dla wszystkich zdarzeń, a jej konstruktor wymaga jednego parametru typu Object reprezentującego obiekt (obiekt źródłowy), który wywołał zdarzenie. Klasa jest trywialna i ma tylko metodę get dla obiektu źródłowego, ale jest częścią konwencji komponentu Swing, że wszystkie obiekty zdarzeń muszą być bezpośrednio lub pośrednio uzyskane z EventObject. Ponieważ klasa Knob może uruchamiać zdarzenia

77 typu KnobEvent, muszą istnieć inne obiekty, które można zarejestrować jako detektory, dlatego zdefiniowano również prosty interfejs detektora: package knobprogram; import java.util.*; public interface KnobListener extends EventListener void knobchanged(knobevent e); W tym miejscu należy również zauważyć, że ten interfejs rozszerza EventListener, który jest niczym innym jak zwykłym interfejsem znaczników, i znowu jest konwencją w Swing, że wszyscy nasłuchujący powinni implementować ten interfejs. Powodem jest to, że obiekt nasłuchiwania można zatem dodać do kolekcji za pomocą nasłuchiwania. Następnie jest sam składnik, który składa się z pięciu właściwości: 1. min, co jest najmniejszą wartością składnika 2. max, co jest największą wartością składnika 3. wartość, czyli bieżąca wartość 4. niższa, która jest proporcją dla zimnego obszaru i wynosi od 0 do 1 5. górna, która jest proporcją zimnego i normalnego obszaru i jest wartością od 0 do 1 - jest zatem dolną granicą dla gorącego obszaru. Kod komponentu jest pokazany poniżej w całości: package knobprogram; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; public class Knob extends JComponent public static final int HOT = 3; public static final int NORMAL = 2; public static final int COLD = 1; private int min; private int max; private double upper; private double lower; private int value;

78 private int state; private boolean marked = false; private Ellipse2D knob = null; private double height; private double width; private double t; private double m; private double w; public Knob() this(0, 100, 0); public Knob(int min, int max, int value) this(min, max, 0.25, 0.75, value); public Knob(int min, int max, double lower, double upper, int value) this.min = min; this.max = max; this.lower = lower; this.upper = upper; this.value = value; addmouselistener(new MouseHandler()); addmousemotionlistener(new MouseMoveHandler()); setstate(); public void paintcomponent(graphics g) double pos = calculateposition(); Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint(renderinghints.key_antialiasing, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setpaint(color.red); g2d.fill(new Ellipse2D.Double(w, 0, w, w));

79 g2d.fill(new Rectangle2D.Double(w, t, w, m * (1 upper))); g2d.setpaint(color.orange); g2d.fill(new Rectangle2D.Double(w, t + m * (1 upper), w, m * (upper lower))); g2d.setpaint(color.blue); g2d.fill(new Ellipse2D.Double(0, height width, width, width)); g2d.fill(new Rectangle2D.Double(w, t + m * (1 lower), w, m * lower + width / 2)); g2d.setpaint(color.magenta); g2d.fill(knob = new Ellipse2D.Double(width * 2 / 3, pos t, w, w)); public void setvalue(int value) int old = this.value; this.value = value; repaint(); firepropertychange("value", old, value); old = state; setstate(); if (old!= state) fireevent(old, state); public int getvalue() return value; public int getminimum() return min; public void setminimum(int min) firepropertychange("minnimum", this.min, min);

80 this.min = min; repaint(); public void setmaximum(int max) firepropertychange("maximum", this.max, max); this.max = max; repaint(); public int getmaximum() return max; public void setupper(double upper) firepropertychange("upper", this.upper, upper); this.upper = upper; repaint(); public double getupper() return upper; public void setlower(double lower) firepropertychange("lower", this.lower, lower); this.lower = lower; repaint();

81 public double getlower() return lower; public int getstate() return state; public void addknoblistener(knoblistener listener) listenerlist.add(knoblistener.class, listener); public void removeknoblistener(knoblistener listener) listenerlist.remove( KnobListener.class, listener ); protected void fireevent(int from, int to) Object[] listeners = listenerlist.getlistenerlist(); for (int i = 0; i < listeners.length; i += 2) if (listeners[i] == KnobListener.class) ((KnobListener)listeners[i + 1]).knobChanged( new KnobEvent(this, value, from, to)); private void calculate() Dimension dim = getsize(); width = dim.getwidth(); height = dim.getheight(); w = width / 3; t = w / 2;

82 m = height width t; private double calculateposition() calculate(); return (m * value + t * min t * max m * max) / (min max); private int calculatevalue(double y) calculate(); double z = ((y t) * min + (t + m y) * max) / m; return Math.min(Math.max((int)z, min), max); private void setstate() int state; double minvalue = (max min) * lower + min; double maxvalue = (max min) * upper + min; if (value < minvalue) state = COLD; else if (value > maxvalue) state = HOT; else state = NORMAL; if (this.state!= state) this.state = state; class MouseHandler extends public void mousepressed(mouseevent e) marked = knob.contains(e.getx(), e.gety());

83 @Override public void mousereleased(mouseevent e) marked = public void mouseexited(mouseevent e) marked = false; class MouseMoveHandler extends public void mousedragged(mouseevent e) if (marked) setvalue(calculatevalue(e.gety())); Jest wiele szczegółów, ale klasa pochodzi od JComponent, który faktycznie mówi, że jest to komponent. Najpierw zdefiniowane są trzy stałe, które wskazują, w którym z trzech obszarów znajduje się bieżąca wartość. Istnieje sześć zmiennych dla właściwości, przy czym pierwsze pięć to pięć właściwości, które można zmienić za pomocą ustawionej metody, a ostatni stan informuje o bieżącym z trzech obszarów. Istnieje siedem dodatkowych zmiennych instancji, które są zmiennymi pomocniczymi i służą do: - oznaczone oznacza, że przycisk myszy jest wciśnięty, aby można było przeciągnąć pokrętło

84 - gałka jest liczbą (elipsą), która obecnie reprezentuje gałkę - wysokość to bieżąca wysokość elementu - szerokość to bieżąca szerokość komponentu - t jest wysokością nad termometrem (półkole) - m oznacza powierzchnię trzech termometrów (zimny, normalny i ciepły) - w jest jedną trzecią bieżącej szerokości elementu i wskazuje szerokość obszarów termometru Te zmienne są inicjowane za każdym razem, gdy rysowany jest komponent i za każdym razem, gdy poruszane jest pokrętło. Klasa ma trzy konstruktory, które inicjują sześć właściwości. Konstruktory są ogólnie trywialne, ale należy zauważyć, że definiują one, że komponent nasłuchuje myszy. Procedury obsługi zdarzeń są zdefiniowane jako klasy wewnętrzne na końcu kodu. Te dwie klasy są trywialne i nie ma wiele do wyjaśnienia, ale powinieneś zauważyć, co robią klasy, a zwłaszcza jak przypisują wartości do zaznaczonej zmiennej. Następnie są metody get i set dla właściwości klasy. Istnieje sześć metod get, które oczywiście są trywialne, i istnieje pięć metod set, w których setvalue () jest najważniejszy. Metoda może być wywoływana przez użytkowników, ale jest również wywoływana z metody obsługi zdarzeń MouseMoveHandler, a zatem za każdym razem, gdy pokrętło jest poruszane. Najpierw bieżąca wartość komponentu jest zapisywana w zmiennej lokalnej, po czym komponent jest aktualizowany. Następnie składnik musi zostać przerysowany, co następuje przez wywołanie repaint (). Następnie metoda uruchamia PropertyChangeEvent dla wartości właściwości. Następnie sprawdzane jest, czy istnieje zmieniony stan (jeśli wartość reprezentuje inny obszar), a jeśli tak jest, uruchamiany jest KnobEvent, co występuje w metodzie fireevent (). Cztery pozostałe metody zestawu działają trochę tak samo, ale są prostsze. Najpierw uruchamiana jest właściwość PropertyChangeEvent dla tej właściwości, która jest następnie aktualizowana, a składnik jest przerysowywany. Zauważ, że klasa ma metody, dzięki czemu obiekty KnobListener mogą rejestrować się jako detektory KnobEvent i być może ponownie zostać niezarejestrowane. Wracamy tylko do rysowania komponentu, a tematem tego jest temat w następnej książce, ale pokrótce dzieje się tak. Gdy trzeba narysować komponent, wywoływana jest metoda paintcomponent (). Jest wywoływany, gdy system wykonawczy zobaczy, że komponent jest nieprawidłowy (musi zostać przerysowany), i dzieje się tak na przykład podczas tworzenia okna lub wykonywania metody odmalowania (). Pamiętaj, że ustawione metody to robią i zapewniają, że okno zostanie przerysowane po zmianie właściwości komponentu. Metoda paintcomponent () ma obiekt Graphics reprezentujący obszar rysowania i udostępniający niektóre narzędzia do rysowania. W takim przypadku dzieje się: 1. wywoływana jest metoda replaceposition (), która inicjuje zmienne pomocy i zwraca pozycję y dla pokrętła 2. Obiekt Graphics jest rzutowany na obiekt Graphics2D, który ma lepsze narzędzia do rysowania 3. jakość rysunku jest określona 4. kolor jest zdefiniowany jako czerwony 5. rysowany jest okrąg 6. dla gorącego obszaru rysowany jest prostokąt

85 7. kolor jest zdefiniowany jako pomarańczowy 8. narysowany jest prostokąt dla normalnego obszaru 9. kolor jest zdefiniowany jako niebieski 10. rysowany jest okrąg - dolny okrąg z rtęcią 11. narysowany jest prostokąt dla zimnego obszaru 12. kolor jest zdefiniowany jako magenta 13. gałka jest wyciągnięta Jest też aplikacja testowa, która używa komponentu Knob i nie chcę tutaj wyświetlać kodu, ale należy pamiętać, że komponent jest używany w taki sam sposób, jak inne komponenty. 5.3 LEPSZY KNOB Komponent niestandardowy jest często opracowywany w sposób opisany powyżej, a jako komponenty w ramach własnych aplikacji jest to również doskonała metoda, ale metoda ta nie jest całkowicie zgodna z zaleceniami dotyczącymi opracowania komponentu Swing, częściowo dlatego, że komponent powinien wspierać wygląd. W przypadku programowania praktycznego największym problemem związanym z komponentem Knob, jak opisano powyżej, jest to, że nie rozdziela interfejsu użytkownika i modelu, co utrudnia utrzymanie kodu. W tej sekcji przyjrzę się innej wersji komponentu Knob, w której różnica polega na tym, że kod jest napisany inaczej. Zasadniczo składa się z komponentu Swing 1. Klasa komponentu, która określa, jak utworzyć komponent, jak można go zmienić i jak odczytać jego stan. 2. Model komponentu składający się z interfejsu defi i domyślnej implementacji. Jest to model, który implementuje logikę komponentu i wysyła powiadomienia do słuchaczy. 3. Tzw. Delegat interfejsu użytkownika, który reprezentuje układ komponentu, zarządzanie zdarzeniami dotyczącymi myszy i klawiatury, i wreszcie delegat interfejsu użytkownika, który narysuje komponent. Projekt KnobProgram1 jest tym samym programem, co projekt KnobProgram, ale składnik Knob jest napisany zgodnie z powyższym wzorem. Zasadniczo jest to ten sam kod, ale jest po prostu inaczej zorganizowany. Komponent może nadal wywoływać KnobEvent oraz dwa typy KnobEvent i KnobListener są całkowicie niezmienione w stosunku do pierwszej wersji komponentu. Zacznę od modelu, który jest zdefiniowany przez następujący interfejs: package knobprogram; import java.beans.*; public interface KnobModel public int getminimum();

86 public int getmaximum(); public int getvalue(); public int getstate(); public double getlower(); public double getupper(); public void setminimum(int minimum); public void setmaximum(int maximum); public void setvalue(int value); public void setlower(double lower); public void setupper(double upper); public void addpropertychangelistener(propertychangelistener listener); public void removepropertychangelistener(propertychangelistener listener); public void addknoblistener(knoblistener listener); public void removeknoblistener(knoblistener listener); Interfejs określa zatem właściwości komponentu oraz zakres, w jakim istnieją metody pobierania i ustawiania tych właściwości. Ponadto interfejs określa, które detektory mogą zarejestrować się jako odbiorcy powiadomień z komponentu. Oto obiekty PropertyChangeListener i KnobListener. Model jest implementowany przez klasy DefaultKnobModel. Nie chcę tutaj pokazywać ogólnej klasy, ale początek jest następujący: public class DefaultKnobModel implements KnobModel public static final double epsilon = ; private EventListenerList listenerlist = new EventListenerList(); public static final int HOT = 3; public static final int NORMAL = 2; public static final int COLD = 1; private int min; private int max; private double upper; private double lower; private int value;

87 private int state; Tutaj możesz zobaczyć, że zmienne zostały przeniesione z klasy Knob, ale tylko te zmienne, które reprezentują stan komponentu, ale nie zmienne użyte do narysowania komponentu. Zwróć także uwagę, że model ma własną kolekcję dla detektorów i zwróć uwagę na typ, którym jest EventListenerList. Klasa ma odpowiednie konstruktory - w tym przypadku trzy w taki sam sposób jak oryginalna klasa Knob. Reszta kodu polega na implementacji metod zdefiniowanych przez interfejs, a tutaj są to szczególnie ustawione metody, o których należy pamiętać, ponieważ będą wyzwalać pożądane zdarzenia. Następnie jest delegat interfejsu użytkownika i zgodnie ze wzorcem rozwoju komponentu niestandardowego zaczyna się od następującej klasy: package knobprogram; import javax.swing.plaf.*; public class KnobUI extends ComponentUI public static final String UI_CLASS_ID = "KnobID"; Klasa dziedziczy ComponentUI i nie zawiera wiele, a jedynie definiuje stałą, dzięki czemu klasa ma unikalną nazwę pod względem wyglądu. Mając tę klasę na miejscu, możesz napisać delegata interfejsu użytkownika, który jest klasą, która narysuje komponent i obsłuży zdarzenia dla myszy i klawiatury. Poniżej pokazałem początek klasy, ale nie pokazałem nic na temat rysowania komponentu, a także klas wewnętrznych, które definiują procedury obsługi zdarzeń dla myszy, ponieważ jest to ten sam kod, co w poprzedniej wersji. public class BasicKnobUI extends KnobUI private MouseHandler mousehandler = null; private MouseMoveHandler motionhandler = null; private KnobModel model = null; private boolean marked = false; private Ellipse2D knob = null; private double height; private double width; private double t; private double m; private double w; public BasicKnobUI(JComponent component) model = ((Knob)component).getModel(); public static ComponentUI createui(jcomponent c) return new BasicKnobUI(c);

88 public void installui(jcomponent component) Knob knob = (Knob)component; knob.addmouselistener(mousehandler = new MouseHandler()); knob.addmousemotionlistener(motionhandler = new MouseMoveHandler()); public void uninstallui(jcomponent component) Knob knob = (Knob)component; if (mousehandler!= null) knob.removemouselistener(mousehandler); if (motionhandler!= null) knob.removemousemotionlistener(motionhandler); Należy zauważyć, że zmienne używane do rysowania interfejsu użytkownika zostały przeniesione do tej klasy. W przeciwnym razie powinieneś zauważyć metody, które zostały zaimplementowane i bez próby wyjaśnienia, co one właściwie robią, są to metody, które oznaczają, że komponent obsługuje wygląd i działanie, a zatem jest częścią wzorca tworzenia niestandardowego komponentu. Z tyłu znajduje się sam komponent, a zatem klasyczne pokrętło, które teraz stało się bardzo cienką klasą: package knobprogram; import javax.swing.*; import java.beans.*; public class Knob extends JComponent implements PropertyChangeListener private KnobModel model; public Knob() init(new DefaultKnobModel()); public Knob(KnobModel model)

89 init(model); public Knob(int min, int max, int value) init(new DefaultKnobModel(min, max, value)); public Knob(int min, int max, double lower, double upper, int value) init(new DefaultKnobModel(min, max, lower, upper, value)); private void init(knobmodel model) setmodel(model); updateui(); public String getuiclassid() return KnobUI.UI_CLASS_ID; public void setmodel(knobmodel model) KnobModel old = this.model; if (old!= null) old.removepropertychangelistener(this); if (model == null) this.model = new DefaultKnobModel(); else this.model = model; this.model.addpropertychangelistener(this); firepropertychange("model", old, this.model); public KnobModel getmodel()

90 return model; public KnobUI getui() return (KnobUI) ui; public void setui(knobui ui) super.setui(ui); public void updateui() if (UIManager.get(getUIClassID())!= null) setui((knobui)uimanager.getui(this)); else setui(new BasicKnobUI(this)); public void propertychange(propertychangeevent e) fi e.getoldvalue(), e.getnewvalue()); repaint(); public int getminimum() return model.getminimum( ); public void setminimum(int min)

91 int old = getminimum(); if (min!= old) model.setminimum(min); firepropertychange("minimum", old, min); public int getmaximum() return model.getmaximum(); public void setmaximum(int max) int old = getmaximum(); if (max!= old) model.setmaximum(max); firepropertychange("maximum", old, max); public int getvalue() return model.getvalue(); public void setvalue(int val) int old = getvalue(); if (val!= old)

92 model.setvalue(val); firepropertychange("value", old, val); public int getstate() return model.getstate(); public double getlower() return model.getlower(); public void setlower(double lower) double old = getlower(); if (Math.abs(lower old) > DefaultKnobModel.epsilon) model.setlower(lower); firepropertychange("lower", old, lower); public double getupper() return model.getupper(); public void setupper(double upper)

93 double old = getupper(); if (Math.abs(upper old) > DefaultKnobModel.epsilon) model.setupper(upper); firepropertychange("upper", old, upper); public void addknoblistener(knoblistener listener) model.addknoblistener(listener); public void removeknoblistener(knoblistener listener) model.removeknoblistener(listener); W rzeczywistości nie ma wiele do wyjaśnienia, a klasa jest pod wieloma względami tylko opakowaniem modelu. Ponieważ jest to model, który utrzymuje właściwości klasy, Pokrętło klasy (czyli klasa znana użytkownikom) musi delegować obsługę do modelu. Program testowy został zmodyfikowany, dzięki czemu ma teraz tylko jeden komponent typu Pokrętło, ale dodano kilka przycisków, aby można było modyfikować właściwości komponentu:

94 W przypadku wprowadzenia wartości w polu wprowadzania i kliknięcia przycisku wartość zmienia się dla odpowiedniej właściwości. Należy zauważyć, że program nie obsługuje niedozwolonych wartości i wywołuje wykonanie, jeśli wartości nie można poprawnie przekonwertować. ĆWICZENIE 3 Utwórz kopię projektu KnobProgram1. Składnik pokrętła ma kilka wad i można przypisywać losowe wartości do jego właściwości, co jest niefortunne. Powinno to być wymagane 1. minimum jest mniejsze niż maksimum 2. dolna jest mniejsza lub równa górnej 3. Dolna i górna musi mieć wartość od 0 do 1 4. wartość musi być większa lub równa minimum i mniejsza lub równa maksimum Musisz zmienić komponent, aby sprawdzić poprawność zmian tych właściwości. W przypadku niedozwolonych wartości komponent nie powinien zgłaszać wyjątków, ale zamiast tego obsługiwać błędy poprzez automatyczne przydzielanie właściwości rozsądnej wartości. Następnie należy rozwinąć model o właściwość: private boolean vertical = true;

95 Jeśli ta właściwość ma domyślną wartość true, komponent musi być wyświetlany jak poprzednio, ale jeśli wartość zostanie zmieniona na false, komponent powinien być wyświetlony poziomo. Na przykład możesz zmienić program testowy, aby otworzyć następujące okno: 5.4 DATEPICKER Chcę zakończyć ten rozdział innym przykładem niestandardowego komponentu. Tym razem komponent jest inny, ponieważ jest to klasa, która dziedziczy JPanel, i zamiast rysować interfejs użytkownika komponentu, jest to kontener zawierający inne komponenty Swing. Składnik to tak zwany DatePicker, który jest przykładem składnika, w którym użytkownik może wybrać datę. Istnieje wiele podobnych składników w Internecie, a poniższy jest inny przykład. Jeśli otworzysz program testowy, pojawi się następujące okno:

96 który pokazuje trzy składniki typu DatePicker. Oznacza to, że obiekt DatePicker pokazuje tylko tekstowe pole tekstowe z datą i przyciskiem po prawej stronie. Kliknięcie przycisku powoduje wyświetlenie następującego okna podręcznego: gdzie możesz nawigować po kalendarzu i wybrać datę, klikając go. Kliknięcie czerwonej ikony na pasku narzędzi powoduje zamknięcie wyskakującego okienka bez wybierania daty. Aby napisać komponent, jasne jest, że duża część kodu jest ponownie wykorzystywana z projektu kalendarza w książce Java 8. Podstawą kalendarza jest także klasa Date z tego projektu, a klasa ta nie jest dalej omawiana. Komponent ma również model, który jest w dużej mierze tym samym modelem, co w projekcie Kalendarz. Nie należy tego również wyjaśniać dalej i jest to bardzo prosta klasa. Z tyłu znajduje się faktyczny składnik DatePicker, który jest obszerną klasą, której kodu nie chcę tutaj wyświetlać, i gdzie jest wiele powtórzeń z projektu kalendarza. Zachęcamy do przestudiowania kodu i dokładnego sprawdzenia, które właściwości są zdefiniowane. Należy tutaj zauważyć, że istnieje data właściwości używana do odczytywania wartości składnika i ewentualnie jego zmiany, a jego typem jest Kalendarz. Wreszcie jest program testowy, który pokazano poniżej: package datecomponent; import java.util.*; import java.awt.*; import javax.swing.*; import javax.swing.border.*; import java.beans.*; public class MainView extends JFrame implements PropertyChangeListener private JLabel lblvalue1 = new JLabel(); private JLabel lblvalue2 = new JLabel(); public MainView() super("datecomponent"); setsize(600, 300); createview(); setlocationrelativeto(null);

97 setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); panel.setborder(new EmptyBorder(20, 20, 20, 20)); DatePicker dp1 = new DatePicker(); dp1.setforeground(color.red); dp1.addpropertychangelistener(this); panel.add(dp1); DatePicker dp2 = new DatePicker(); dp2.setpreferredsize(new Dimension(150, 50)); dp2.setbackground(color.blue); dp2.setforeground(color.white); panel.add(dp2); DatePicker dp3 = new DatePicker(); dp3.setfont(new Font("Arial", Font.BOLD, 24)); dp3.setdate(new GregorianCalendar(2020, 11, 31)); panel.add(dp3); public void propertychange(propertychangeevent e) if (e.getnewvalue() instanceof Calendar) JOptionPane.showMessageDialog(this, gettext((calendar)e.getnewvalue())); private String gettext(calendar date) return String.format("%02d-%02d-%04d", date.get(calendar.date), date.get(calendar.month) + 1, date.get(calendar.year));

98 W tym miejscu należy przede wszystkim zwrócić uwagę na sposób tworzenia komponentów DatePicker i dodawania ich do okna oraz, że odbywa się to w taki sam sposób, jak w przypadku innych komponentów. Należy również pamiętać, że program jest zarejestrowany jako PropertyChangeListener dla pierwszego składnika i otrzymuje powiadomienia o zmianie tego składnika. Składnik DatePicker może mieć praktyczne znaczenie i chcę go dodać do mojej biblioteki klas PaLib. Wykonuje następujące kroki: 1. Utworzyłem pakiet palib.gui.images i do tego skopiowałem ikony, których używa komponent (jest ich 7). 2. Klasa CalendarException jest kopiowana z pakietu palib.util. 3. Data klasy została skopiowana do pakietu palib.util. Daje to problem z nazewnictwem w klasie Narzędzia, ponieważ odnosi się do klasy java.util.date sam z nazwą Date. Ten problem należy rozwiązać w klasie Narzędzia, zmieniając wszystkie odwołania na Data na pełną nazwę java.util.date. 4. Utworzyłem pakiet palib.gui.models i tutaj utworzyłem klasę o nazwie PickerModel. Treścią tej klasy jest model komponentu (model klasy w projekcie DateComponent). 5. Klasa DatePicker jest kopiowana do palib.gui. To znowu powoduje problemy z nazwami dla daty i te problemy muszą zostać rozwiązane poprzez odniesienie daty do pełnej nazwy palib.util.date. 6. W klasie DatePicker wszystkie nazwy ikon (referencji) muszą zostać zmienione na ikony, które są częścią biblioteki. Po tych zmianach biblioteka jest aktualizowana, a projekt DateComponent można zmodyfikować (do DateComponent1), więc wszystkie klasy odnoszące się do komponentu są usuwane, a następnie projekt używa PaLib. Jako ostatni komentarz. Podczas nazywania klas należy ogólnie starać się nie używać nazw, których używa także Java. Nie jest to dozwolone, ale łatwo prowadzi do tego rodzaju problemów, jak w powyższym przypadku. 6.SCHOWEK Korzystanie ze schowka, a tym samym kopiowanie i wklejanie jest oczekiwaną funkcją we współczesnych programach, przynajmniej tak długo, jak długo są to programy przetwarzające tekst. Zasadniczo możliwe jest wybranie jakiegoś obiektu, takiego jak tekst, i zapisanie ich w schowku, a następnie odzyskanie go, a może nawet w zupełnie innym programie. W większości programów jest to coś, z czym programiści nie muszą się odnosić, ponieważ wiele komponentów (takich jak komponenty do edycji tekstu) ma wbudowaną funkcjonalność. Istnieją również sytuacje, w których konieczne jest zaprogramowanie kopiowania i wklejania (na przykład, jeśli piszesz niestandardowe komponenty), dlatego ten rozdział. Zasadniczo jest to dość proste, ale jeśli badasz, w jaki sposób jest ono wdrażane, nie jest to wcale proste, a jest tego kilka przyczyn. Schowek to oczywiście pewna pamięć, w której można coś zapisać, ale schowek jest pojęciem spoza programu i czymś administrowanym przez system operacyjny, a nie przez maszynę wirtualną. Innym problemem jest to, że dane to nie tylko dane, a nawet tekst to nie tylko tekst. Na przykład, jeśli pisanie tekstu w edytorze tekstu zawiera tekst, wiele znaków kontrolnych i inne informacje o formatowaniu, które również muszą zostać zapisane, a następnie ponownie wstawione w celu wklejenia. Dlatego konieczne jest również zapisanie informacji

99 o tym, jaki rodzaj danych jest przechowywany w schowku. Wreszcie, jeśli musisz skopiować i wkleić między dwoma różnymi programami, mogą one nie znać swoich formatów danych. Ogólnie można od razu stwierdzić, że kopiowanie i wklejanie danych między dwoma różnymi programami niekoniecznie przynosi znaczące wyniki. Chcę zacząć od prostego programu, który otwiera następujące okno: Kliknięcie przycisku Kopiuj powoduje skopiowanie tekstu do schowka, a kliknięcie przycisku Wklej powoduje otwarcie okna komunikatu z tekstem skopiowanym do schowka (tekst jest pobierany ze schowka). W ten sposób program pokazuje, jak zapisać dane w schowku i odzyskać te dane. Kod jest następujący: package theclipboard; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; import java.awt.datatransfer.*; public class MainView extends JFrame private static Random rand = new Random(); private String[] names = "Gorm den Gamle", "Harald Blåtand", "Svend Tveskæg" ; private Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); public MainView() super("the clipboard"); setsize(400, 200); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new FlowLayout()); panel.setborder(new EmptyBorder(20, 20, 20, 20)); JButton cmdcopy = new JButton("Copy");

100 JButton cmdpaste = new JButton("Paste"); cmdcopy.addactionlistener(this::copy); cmdpaste.addactionlistener(this::paste); panel.add(cmdcopy); panel.add(cmdpaste); add(panel); private void copy(actionevent e) cb.setcontents(new StringSelection(names[rand.nextInt(names.length)]), null); private void paste(actionevent e) try JOptionPane.showMessageDialog(this, gettransferdata(dataflavor.stringflavor)); cb.getcontents(dataflavor.stringflavor). for (DataFlavor df : cb.getavailabledataflavors()) JOptionPane.showMessageDialog(this, df.getmimetype()); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); Klasa definiuje tablicę z trzema ciągami, a to znaczy, że kliknięcie Kopiuj zapisuje losowy jeden z tych ciągów w schowku, dzięki czemu można zauważyć, że nie jest to ten sam ciąg, który jest zapisywany za każdym razem. W szczególności należy zwrócić uwagę na sposób definiowania odwołania do schowka Schowek cb = Toolkit.getDefaultToolkit (). GetSystemClipboard (); jak w Javie jest reprezentowany przez Schowek klasy, ale poza tym najważniejsze są oczywiście dwie procedury obsługi zdarzeń. Zapisujesz dane za pomocą metody setcontents (), która ma dwa parametry. Pierwszy to dane do zapisania, a drugi to właściciel. Nie jest tak często używany, ale ma typ ClipboardOwner (czyli interfejs), co oznacza, że właściciel może otrzymać powiadomienie, jeśli zawartość schowka zostanie nadpisana (na przykład z innej aplikacji). W tym przykładzie wartość parametru ma wartość NULL, co oznacza po prostu, że nie są wysyłane powiadomienia o treści.

101 Aby dane mogły zostać zapisane w schowku - i ponownie odczytane - muszą zostać zamknięte w strukturze danych typu Transferable. Jest to interfejs i istnieje kilka konkretnych klas, które implementują ten interfejs, a StringSelection jest przykładem Transferable, który został specjalnie zaprojektowany do przesyłania tekstu. W takim przypadku łańcuch jest kapsułkowany w obiekcie StringSelection. Następnie jest funkcja obsługi zdarzeń paste (). Dane pobierane są ze schowka za pomocą metody getcontents (), w której parametrem jest DataFlavor, który informuje, co to jest za rodzaj rodzaju danych do pobrania. Klasa DataFlavor ma pewne predefiniowane smaki, a stringflavor to przykład, który mówi, że dane to StringSelection. Jest to zbywalny, który ma metodę gettranferedata (), która zwraca określone dane, a ta metoda ma również parametr DataFlavor jako parametr. W rzeczywistości instrukcja, która pobiera dane ze schowka, może być zapisana w następujący sposób: cb.getcontents (null).gettransferdata (DataFlavor.stringFlavor) Schowek klasy ma metodę getavailabledataflavours (), która zwraca tablicę obiektów DataFlavor, która informuje, jak można interpretować zawartość schowka. Czasami (na przykład, jeśli jest to tekst), treść może być interpretowana na kilka sposobów (tekst w stylu, HTML, zwykły tekst) i dlatego może istnieć więcej obiektów DataFlavor, w których obiekt, który określa większość informacji, będzie pierwszy w tablicy. Ostatnia pętla for w module obsługi zdarzeń paste () iteruje wszystkie obiekty DataFlavor, a są dwa. W szczególności, jeśli getcontents () wskazuje niepoprawny DataFlavor lub jeśli schowek jest pusty, otrzymasz wyjątek. 6.1 RODZAJE MIME Podczas zapisywania danych w schowku dane zostaną zdefiniowane przez klasę, taką jak Java.lang.String, i dopóki istnieje tylko potrzeba przesyłania danych między aplikacjami Java, wystarczy powiedzieć DataFlavor, który typ, ale dane powinny zostać przeniesione do innych programów, które oczywiście nie znają klas Javy, i istnieje potrzeba innego sposobu zdefiniowania formatów danych. Istnieje zatem potrzeba określenia niezależnych od platformy i neutralnych językowo formatów danych. W tym celu wykorzystywany jest MIME, który jest standardem internetowym, który umożliwia dołączanie różnych dokumentów do poczty elektronicznej Typ MIME składa się z typu nośnika, który można uznać za kategorię nadrzędną, a także podtyp, który definiuje bardziej szczegółowy typ w ramach kategorii. Typy są oddzielone znakiem /, a przykładem może być text/plain który wskazuje zwykły tekst bez informacji o formatowaniu. Na przykład istnieje wiele innych rodzajów MINE image / gif image / jpg text / html Możesz także zdefiniować własne typy MIME, które muszą składać się ze znanego typu nośnika, a także podzbioru rozpoczynającego się od x-, co oznacza, że jest to typ niezarejestrowany. Wreszcie jest application / octet-stream

102 typ ogólny określający dane binarne w nieznanym formacie. Oprócz typu i podtypu, typ MIME może również powiązać inne informacje, na przykład dotyczące kodowania tekstu. Składnia jest pokazana w poniższych przykładach - text/plain;charset=unicode - text/plain;charset=ascii - text/plain;charset=iso Podczas tworzenia aromatu danych możesz określić typ MIME, na przykład: DataFlavor df = new DataFlavor ( text / htm ); Jako kolejny przykład programu korzystającego ze schowka program ClipBytes kopiuje tablicę bajtów do schowka. Program ma dokładnie taki sam interfejs użytkownika, jak powyższy program, i działa również w ten sam sposób, po prostu kopiując do tablicy tablicę 10 losowych bajtów. Podobnie, jeśli klikniesz przycisk Wklej, tablica jest odczytywana ze schowka i wyświetlana w oknie komunikatu. Kod jest pokazany poniżej, gdzie pokazałem tylko tę część kodu, która jest inna: public class MainView extends JFrame implements ClipboardOwner private static Random rand = new Random(); private Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); public MainView() private void public void lostownership(clipboard clipboard, Transferable contents) JOptionPane.showMessageDialog(this, "The clipboard data is invalid");

103 private void copy(actionevent e) cb.setcontents(new DataHandler(create(10), "application/octet-stream"), this); private void paste(actionevent e) try Transferable data = cb.getcontents(null); show((byte[])data.gettransferdata(flavor)); DataFlavor flavor = data.gettransferdataflavors()[0]; catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); private byte[] create(int n) byte[] arr = new byte[n]; rand.nextbytes(arr); return arr; private void show(byte[] arr) StringBuilder builder = new StringBuilder(); for (byte b : arr) builder.append(b); builder.append(' '); JOptionPane.showMessageDialog(this, builder.tostring());

104 Po pierwsze, zauważ, że klasa implementuje teraz interfejs ClipboardOwner. Ten interfejs definiuje pojedynczą metodę o nazwie lostownership (), i ta metoda jest wykonywana, jeśli zapisałeś coś w schowku, a inny program go nadpisuje. W takim przypadku nie dzieje się nic poza wyświetleniem okna komunikatu. Łatwo jest przetestować tę funkcję, uruchamiając program i zapisując coś w schowku (naciśnij przycisk Kopiuj). Jeśli następnie otworzysz inny program i stąd zapiszesz coś w schowku, zauważysz, że wyświetla się okno komunikatu. Do końca programu istnieją dwie pomocnicze metody, w których pierwsza tworzy tablicę bajtów z losowymi wartościami bajtów, podczas gdy druga wyświetla okno komunikatu zawierające zawartość takiej tablicy. Następnie są procedury obsługi zdarzeń. Metoda copy () używa typu DataHandler, który jest zbywalny. Oprócz enkapsulacji danych, taki obiekt będzie miał również typ MIME i dlatego może być używany do przesyłania różnych typów danych. W tym przypadku zastosowano ogólny typ MIME, który stwierdza, że przesyłane są surowe dane, których nie należy interpretować. Zwróć także uwagę na ostatni parametr setcontents (), gdzie jest to ClipboardOwner. Metoda paste () musi pobierać dane za pomocą getcontents (), ale musisz określić DataFlavor, a wynikiem jest obiekt możliwy do przeniesienia. Z tego obiektu pierwszy DataFlavor (jest tylko jeden) służy do pobierania danych z obiektu zbywalnego jako tablicy bajtów. 6.2 OBIEKTY SERIALIZOWANE Możliwe jest również zapisywanie obiektów w schowku poprzez serializację, a pokazuję, jak to zrobić w następnym przykładzie. Chcę zapisać obiekty następującego typu: class King implements Serializable private String name; private int from; private int to; public King(String name, int from, int to) this.name = name; this.from = from; this.to = to; public String getname() return name; public int getfrom() return from;

105 public int getto() return to; public String tostring() return name + String.format(" [%d %d]", from, to); Klasa nie wymaga objaśnienia, ale należy pamiętać, że można ją szeregować, a także że istnieją zmienne instancji, zarówno typu String, jak i typów prostych. Aby przenieść dane, musisz użyć Zbywalnego, a oto kilka opcji, ale jako przykład pokażę, jak wdrożyć własny typ Zbywalny. Transferable to interfejs, który definiuje trzy metody, dlatego zadaniem jest napisanie klasy, która implementuje ten interfejs: class SerialTransferable implements Transferable private Serializable obj; SerialTransferable(Serializable obj) this.obj = public DataFlavor[] gettransferdataflavors() DataFlavor[] flavors = new DataFlavor[2]; try flavors[0] = new DataFlavor("application/x-java-serialized-object; class=" + obj.getclass().getname()); flavors[1] = DataFlavor.stringFlavor; return flavors;

106 catch (ClassNotFoundException e) return new public boolean isdataflavorsupported(dataflavor flavor) return DataFlavor.stringFlavor.equals(flavor) "application".equals(flavor.getprimarytype()) && "x-java-serialized-object".equals(flavor.getsubtype()) flavor.getrepresentationclass().isassignablefrom(obj.getclass()); public Object gettransferdata(dataflavor flavor) throws UnsupportedFlavorException if (!isdataflavorsupported(flavor)) throw new UnsupportedFlavorException(flavor); if (DataFlavor.stringFlavor.equals(flavor)) return obj.tostring(); return obj; Klasa ma odwołanie do obiektu, który ma być serializowany, który jest inicjowany w konstruktorze. Pierwsza metoda musi zwrócić obiekty DataFlavor, których można użyć, i definiuje dwa. Pierwszy jest zdefiniowany jako typ MIME do serializacji obiektu: "application / x-java-serialized-object; class =" + obj.getclass (). getname () gdzie typem jest aplikacja, a podtypem jest obiekt serializowany x-java. Na koniec skojarzony jest parametr, który jest typem obiektu. Drugi DataFlavor to DataFlavor.stringFlavor, co oznacza, że zawartość schowka może być interpretowana jako ciąg znaków. Należy zwrócić uwagę na sekwencję dwóch obiektów DataFlavor, co oznacza, że obiekt DataFlavor do serializacji jest podstawowym.

107 Następna metoda powinna przetestować, czy dany DataFlavor może być użyty do pobrania tego obiektu, i może być, jeśli jest to DataFlavor.stringFlavor lub czy typ i podtyp są poprawne i jest to poprawny typ klasy. Wreszcie istnieje ostatnia metoda, która z DataFlavor zwraca enkapsulowany obiekt danych. Jeśli jest to DataFlavor.stringFlavor, wynik jest zwracany jako obiekt dostring (), a jeśli jest to prawnie DataFlavor, to sam obiekt. Następnie jest program i zasadniczo jest to ten sam program, co w dwóch pierwszych przykładach tej części: public class MainView extends JFrame private static Random rand = new Random(); private King[] kings = new King("Gorm den Gamle", 936, 958), new King("Harald Blåtand", 958, 987), new King("Svend Tveskæg", 987, 1014) ; private Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); public MainView() private void createview() private void copy(actionevent e) cb.setcontents( new SerialTransferable(kings[rand.nextInt(kings.length)]), null); private void paste(actionevent e) try

108 DataFlavor fl = new DataFlavor("application/x-java-serialized-object; class=clipobject.king"); if (cb.isdataflavoravailable(flavor)) show((king)cb.getdata(flavor)); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); private void show(king king) JOptionPane.showMessageDialog(this, String.format("%s\n%d %d", king.getname(), king.getfrom(), king.getto())); Kiedy obiekt jest zapisywany w schowku, jest to losowy obiekt King i należy pamiętać, że jest on zamknięty w obiekcie Transferable typu SerialTransferable. Gdy obiekt zostanie pobrany w metodzie paste (), zdefiniowany jest DataFlavor poprawnego typu. Jeśli obiekt można przekonwertować za pomocą tego elementu DataFlavor, jest on pobierany ze schowka za pomocą metody getdata (), która jest metodą, która nie zwraca obiektu zbywalnego w schowku, ale dane, które obiekt kapsułkuje. ĆWICZENIE 4 Musisz napisać aplikację, która otworzy okno, jak pokazano poniżej. Możesz po prostu zrobić prosty projekt. Moje okno ma stały rozmiar, a komponenty są układane bez użycia menedżera układu, a zatem w stałej pozycji

109 W pierwszym polu wprowadzania wprowadź liczbę jednostek (int), aw drugim polu - cenę jednostkową (podwójną). Po kliknięciu przycisku OK program musi obliczyć całkowitą kwotę jako liczbę jednostek pomnożoną przez cenę jednostkową i wstawić wynik w dolnym polu. Kliknięcie przycisku Wyczyść wyczyści wszystkie trzy pola. Po kliknięciu przycisku Kopiuj zawartość dwóch górnych pól musi zostać użyta do utworzenia instancji obiektu następującego typu: class Values implements Serializable private int units; private double price; public Values(int units, double price) this.units = units; this.price = price; public int getunits() return units; public double getprice() return price; i obiekt musi zostać zapisany w schowku. Przycisk Wytnij powinien w zasadzie działać w ten sam sposób, ale powinien również usunąć dwa górne pola. Na koniec przycisk Wklej musi zainicjować dwa górne pola wartościami obiektu Values zapisanymi w schowku - jeśli taki obiekt istnieje. 6.3 OBRAZY W SCHOWKU Chcę zamknąć ten rozdział o schowku za pomocą aplikacji, która zapisuje część obrazu w schowku. Jest to program, w którym można zaznaczyć prostokątny obszar obrazu, który można zapisać w schowku i wkleić go ponownie do obrazu, ale wymaga on pewnej wiedzy na temat przetwarzania obrazu omówionego w następnej książce, aby można było zignorować szczegóły przetwarzania zdjęć. Ważną rzeczą jest tutaj, jak skopiować dane obrazu do i ze schowka, a program nie działa tak bardzo. Jeśli otworzysz program, pojawi się okno, jak pokazano poniżej:

110 gdzie obraz jest kopiowany do projektu i jest częścią pliku jar programu. Jeśli wskażesz gdzieś na zdjęciu, przytrzymaj i przesuń mysz, aby narysować prostokąt pokazujący, co jest zaznaczone: Po kliknięciu lokalizacji prawym przyciskiem myszy pojawi się menu podręczne z trzema pozycjami menu do skopiowania, wycięcia i wklejenia. Poniższe okno pokazuje wynik, w którym wybrałem wycięcie, a następnie wkleiłem głowę orła w trzech miejscach:

111 Należy zauważyć, że obraz nie znajduje się w JScrollPane, a powodem jest to, że nieco komplikuje program, ponieważ trudniej jest obliczyć, gdzie ma zostać narysowany prostokąt. Program wygląda trochę jak poprzedni program odpowiadający strukturze danych zdefiniowanej dla pikseli do zapisania w schowku: class ImageData implements Serializable private int width; private int height; private int[] pixels; public ImageData(int width, int height, int[] pixels) this.width = width; this.height = height; this.pixels = pixels; public int getwidth() return width; public int getheight() return height;

112 public int[] getpixels() return pixels; Struktura danych składa się z tablicy dla prostokąta pikseli oraz szerokości i wysokości prostokąta. W taki sam sposób jak w poprzednim programie muszę zdefiniować Zbywalne: class ImageTransferable implements Transferable public final static DataFlavor IMAGE_FLAVOR = new DataFlavor (clipimage.imagedata.class, "Image Data"); private final static DataFlavor [] flavors = IMAGE_FLAVOR ; private ImageData data; public ImageTransferable(ImageData data) this.data = data; public DataFlavor [] gettransferdataflavors() return flavors; public boolean isdataflavorsupported(dataflavor flavor) return flavor.equals(image_flavor); public Object gettransferdata(dataflavor flavor) throws UnsupportedFlavorException

113 if (!fl throw new UnsupportedFlavorException(fl return data; Po pierwsze, definiowany jest DataFlavor, ale tym razem nie jest on definiowany za pomocą typu MIME, ale poprzez określenie klasy typu obiektów, które można przenieść do schowka, a także nazwy. Zbywalny obiekt musi tym razem obsługiwać tylko ten pojedynczy DataFlavor, a tablica zwrócona przez gettransferdataflavours () jest zdefiniowana jako tablica statyczna. Implementacja metod klasy jest trywialna. Potem jest sam program i kod dużo się wypełnia. Znaczna część kodu obejmuje rysowanie prostokąta, a także tworzenie układu pikseli, które tworzą prostokątny obszar obrazu oraz kopiowanie zapisanych pikseli na obraz. To drugie dzieje się z dwiema metodami, odpowiednio o nazwach getpixels () i setpixels (). Nie chcę tutaj wyświetlać kodu dla tych metod, ale wkrótce program działa w następujący sposób: Obraz jest wyświetlany w JLabel zakotwiczonym w lewym górnym rogu okna. Jeśli naciśniesz lewy przycisk myszy, współrzędne myszy są zapisywane jako lewy górny róg prostokąta, a podczas przeciągania myszy współrzędne myszy są zapisywane jako prawy dolny róg prostokąta, po czym rysowany jest prostokąt. Kliknięcie prawym przyciskiem myszy w dowolnym miejscu w oknie spowoduje otwarcie menu podręcznego z trzema pozycjami menu. Wszystko to wymaga trochę kodu, ale chciałbym tylko pokazać trzy moduły obsługi zdarzeń w menu podręcznym. Poniżej znajduje się metoda copy(): private Rectangle copy() Rectangle rect = getselected(); int[] pixels = getpixels(rect); ImageData data = new ImageData(rect.width, rect.height, pixels); Clipboard cb = gettoolkit().getsystemclipboard(); cb.setcontents(new ImageTransferable(data), null); return rect; Metoda rozpoczyna się od określenia wybranego prostokąta, co odbywa się za pomocą metody getselected (). Następnie metoda getpixels () służy do utworzenia tablicy ze wszystkimi pikselami obrazu w tym prostokącie, a następnie tworzy obiekt do zapisania w schowku. Obiekt ma typ ImageData i jest teraz zapisywany w schowku poprzez osadzenie go w obiekcie ImageTransferable. Gdy metoda zwraca prostokąt, dzieje się tak, ponieważ jest on również używany w metodzie cut(): private void cut() Rectangle rect = copy(); int[] pix = getpixels(rect);

114 for (int i = 0; i < pix.length; ++i) pix[i] = 0; setpixels(pix, rect); cut() must start with a copy() and then the area saved must be blanked by setting all pixels to 0. The actual image area is updated using the setpixels() method. Back there is the paste() method: private void paste() Clipboard cb = gettoolkit().getsystemclipboard(); try Transferable tf = cb.getcontents(null); if (tf.isdataflavorsupported(imagetransferable.image_flavor)) ImageData data = (ImageData)(tf.getTransferData(ImageTransferable.IMAGE_FLAVOR)); Rectangle area = new Rectangle(point.x, point.y, data.getwidth(), data.getheight()); int[] pixels = data.getpixels(); setpixels(pixels, area); end = start; repaint(); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); Metoda rozpoczyna się od odwołania się do zawartości schowka jako obiektu zbywalnego. Jeśli ten obiekt obsługuje poprawny DataFlavor, dane są pobierane jako obiekt ImageData i na ich podstawie tworzony jest prostokąt dla pikseli, które mają być aktualizowane. Rzeczywista aktualizacja odbywa się za pomocą metody setpixels (). Po ostatecznym przerysowaniu obrazu wybrany prostokąt musi zostać usunięty 7. PRZECIĄGNIJ I UPUŚĆ Przeciągnij i upuść to operacja, w której dane za pomocą myszy są przenoszone z jednej pozycji do innej pozycji. W rzeczywistości większość komponentów Swing ma wbudowane funkcje przeciągania i upuszczania, a zatem implementacja tej funkcji w programie jest stosunkowo prosta. Zasada jest trochę podobna do schowka, że dane, które mają zostać przeniesione, muszą być zawarte w obiekcie zbywalnym, a ponadto muszą zostać zaimplementowane trzy interfejsy, z których jeden definiuje komponent, na którym ma zostać wykonany ruch, drugi komponent gdzie musisz być w stanie upuścić

115 i wreszcie obiekt wskazujący wizualną reprezentację operacji. Brzmi to dużo, ale dzieje się tak dlatego, że masz dużą swobodę co do sposobu wykonania operacji, a w praktyce to, co jest potrzebne, jest ograniczone. Przeciągnij i upuść zawiera trzy funkcje 1. skopiuj (przytrzymaj lewy przycisk i przesuń mysz) 2. przesuń (przytrzymaj lewy przycisk i przesuń mysz + CTRL) 3. link (przytrzymaj lewy przycisk i przesuń mysz + CTRL + SHIFT) gdzie ostatni oznacza, że obiekt nie jest kopiowany, ale definiowane jest tylko odniesienie do oryginalnego obiektu. Te operacje są zdefiniowane jako stałe w klasie DnDConstants: 1. DnDConstants.ACTION_MOVE 2. DnDConstants.ACTION_COPY 3. DnDConstants.ACTION_REFERENCE 4. DnDConstants.ACTION_LINK 5. DnDConstants.ACTION_COPY_OR_MOVE Zasada przeciągania i upuszczania polega na tym, że za pomocą myszy można przeciągnąć jeden komponent na inny, a następnie upuścić pierwszy. Kiedy komponenty Swing obsługują przeciąganie i upuszczanie, oznacza to, że cała niezbędna logika myszy jest wbudowana, ale co się stanie po upuszczeniu, Swing oczywiście nie może wiedzieć i dlatego coś trzeba zaprogramować. Chcę zacząć od aplikacji, która otwiera następujące okno: Okno zawiera trzy etykiety, a to oznacza, że za pomocą operacji przeciągania i upuszczania można zmienić kolor tła górnej etykiety. Poniżej znajduje się przykład, w którym kolor tła został zmieniony trzykrotnie (cyfra 3 jest wyświetlana w JLabel, a jedynym celem jest pokazanie, do którego interfejsu służą jako część interfejsu API przeciągania i upuszczania):

116 Pełny kod pokazano poniżej: package dropswing; import java.awt.*; import javax.swing.*; import javax.swing.border.*; import java.awt.datatransfer.*; import java.awt.dnd.*; import java.io.*; public class MainView extends JFrame public final static DataFlavor LABEL_FLAVOR = new DataFlavor(JLabel.class, "Label Instances"); private DragSourceListener dragsource; private JPanel panel = new JPanel(null); private JLabel droplabel; private JLabel lblcount = new JLabel(); private int count = 0; public MainView() super("drag and drop"); setsize(260, 250); createview();

117 setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() addcomponent(droplabel = createlabel(), 20, 20, 220, 50); addcomponent(createlabel(color.red), 20, 100, 60, 30); addcomponent(createlabel(color.green), 100, 100, 60, 30); addcomponent(createlabel(color.blue), 180, 100, 60, 30); addcomponent(lblcount, 100, 140, 60, 60); lblcount.sethorizontalalignment(jlabel.center); lblcount.setfont(new Font("Liberation Serif", Font.BOLD, 36)); new DropTarget(dropLabel, DnDConstants.ACTION_COPY, new DnDDrop()); dragsource = new DnDSource(); add(panel); private JLabel createlabel() JLabel label = new JLabel("Drop things here"); label.setopaque(true); label.sethorizontalalignment(jlabel.center); label.setfont(new Font("Liberation Serif", Font.BOLD, 24)); return label; private JLabel createlabel(color color) JLabel label = new JLabel(); label.setopaque(true); label.setbackground(color); DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(label, DnDConstants.ACTION_COPY, new DnDGesture());

118 return label; private void addcomponent(component component, int xpos, int ypos, int width, int height) component.setbounds(xpos, ypos, width, height); panel.add(component); class DnDGesture implements DragGestureListener public void draggesturerecognized(draggestureevent e) Cursor cursor = null; JLabel label = (JLabel)(e.getComponent()); switch (e.getdragaction()) case DnDConstants.ACTION_MOVE: cursor = DragSource.DefaultMoveDrop; break; case DnDConstants.ACTION_COPY: cursor = DragSource.DefaultCopyDrop; break; case DnDConstants.ACTION_LINK: cursor = DragSource.DefaultLinkDrop; break; e.startdrag(cursor, new LabelTransferable(label), dragsource); class DnDSource implements DragSourceListener public void dragenter(dragsourcedragevent e)

119 public void dragexit(dragsourceevent e) public void dragover(dragsourcedragevent e) public void dropactionchanged(dragsourcedragevent e) public void dragdropend(dragsourcedropevent e) if (e.getdropsuccess()) lblcount.settext(string.format("%d", ++count)); class DnDDrop implements DropTargetListener public void dragenter(droptargetdragevent e) if (!e.isdataflavorsupported(label_flavor)) e.rejectdrag(); else droplabel.setborder(new LineBorder(Color.black)); public void dragexit(droptargetevent e) droplabel.setborder(null);

120 public void dragover(droptargetdragevent e) public void dropactionchanged(droptargetdragevent e) public void drop(droptargetdropevent e) try if (e.isdataflavorsupported(label_flavor)) droplabel.setbackground(((jlabel)e.gettransferable().gettransferdata( LABEL_FLAVOR)).getBackground()); droplabel.setborder(null); e.dropcomplete(true); catch (Exception ex) e.dropcomplete(false); class LabelTransferable implements Transferable

121 private DataFlavor[] flavors = LABEL_FLAVOR ; private JLabel label; public LabelTransferable(JLabel label) this.label = label; public DataFlavor[] gettransferdataflavors() return flavors; public boolean isdataflavorsupported(dataflavor flavor) return flavor.equals(label_flavor); public Object gettransferdata(dataflavor flavor) throws UnsupportedFlavorException, IOException if (flavor.equals(label_flavor)) return label; throw new UnsupportedFlavorException(flavor); Jeśli zaczynasz od góry, przeciągnij i upuść wymaga dwóch pakietów: import java.awt.datatransfer. *; import java.awt.dnd. *; gdzie pierwszy to ten sam pakiet, o którym wspominałem w poprzednim rozdziale o schowku. Drugi pakiet zawiera typy wymagane do implementacji przeciągania i upuszczania w aplikacji. Jeśli chodzi o zmienne programu, stała jest najpierw definiowana jako DataFlaver dla JLabel. Odpowiada to

122 obiektom, które ten program powinien móc przeciągać, są komponentami JLabel. Ponadto zmienna typu DragSourceListener określa, gdzie obiekt reprezentujący składnik do przeciągnięcia i który podczas operacji uruchamia wiele zdarzeń. Najważniejsze zmienne odnoszą się do komponentów interfejsu użytkownika i nie wymagają specjalnego wyjaśnienia, ale należy pamiętać, że panel nie ma menedżera układu i dlatego komponenty są umieszczone w ustalonych pozycjach w oknie. Wystarczy po prostu pozbyć się interfejsu użytkownika, ponieważ w tym przykładzie nie jest on podstawowy. Przeciągnij i upuść wymaga zdefiniowania czterech klas. Wyjaśniono je poniżej, ale w tym przykładzie wszystkie są zdefiniowane jako klasy wewnętrzne. Oprócz tych klas, kod obejmuje również projekt interfejsu użytkownika, a oto trzy rzeczy, które powinieneś zauważyć. W metodzie createview () wykonywana jest następująca metoda: new DropTarget(dropLabel, DnDConstants.ACTION_COPY, new DnDDrop()); droplabel to JLabel, który chcę upuścić, a stwierdzenie mówi mi, że powinno to być możliwe poprzez dołączenie DropTargetListener do komponentu. DnDDrop to klasa wewnętrzna, która implementuje interfejs DropTargetListener. createview () wykonuje również instrukcję dragsource = new DnDSource (); który tworzy DragSourceListener (dragsource jest zmienną instancji), a DnDSource jest klasą wewnętrzną, która implementuje interfejs DragSourceListener. Na koniec zwróć uwagę na ostatnią metodę createlabel (), która tworzy trzy komponenty JLabel, które pokazują kolor (komponenty, które muszą być możliwe do przeciągnięcia), które wykonują następującą instrukcję: DragSource.getDefaultDragSource (). CreateDefaultDragGestureRecognizer (etykieta, DnDConstants.ACTION_COPY, nowy DnDGesture ()); Instrukcja łączy DragGestureListener ze składnikiem. DndGesture to klasa wewnętrzna, która implementuje interfejs DragGestureListener, a klasa inicjuje operację przeciągania. Suma powyższego jest taka, że aby zaimplementować przeciąganie i upuszczanie w programie, trzeba 1. przypisz DropTargetListener do komponentów, do których musisz mieć możliwość upuszczenia 2. definiuje obiekt DragSourceListener 3. przypisz DragGestureListener do komponentów, które muszą być przeciągalne Z tyłu jest pisanie klas implementujących trzy interfejsy i jak wspomniano powyżej oraz klasa implementująca Transferable. Chcę zacząć od ostatniej o nazwie LabelTransferable, która implementuje interfejs Transferable. Konstruktor klasy ma parametr JLabel jako parametr i jest etykietą zawartą w operacji przeciągania.

123 Jak pokazano w poprzednim rozdziale, klasa musi implementować trzy metody, a ponieważ istnieje tylko jeden DataFlavor, wszystkie te metody są trywialne. Następnie jest klasa DnDSource, która implementuje DragSourceListener. Ten interfejs definiuje 5 programów obsługi zdarzeń, które uruchamiają zdarzenia powiązane z operacją przeciągania. Często nie implementujesz tych metod (pozostaw je puste), ale wdrożyłem jedną metodę, która jest wykonywana po zakończeniu operacji. Jeśli operacja upuszczenia została wykonana poprawnie, metoda zlicza licznik o 1 i wyświetla wartość licznika w dolnym JLabel. Celem jest pokazanie przykładu, w jaki sposób można korzystać z tych procedur obsługi zdarzeń. Zachęcamy Cię do sprawdzenia, kiedy wystąpią inne zdarzenia, ale wynika to głównie z nazwy. Klasa DnDDrop implementuje DropTargetListener, który w ten sam sposób definiuje 5 traderów zdarzeń, ale tym razem zdarzenia są wyzwalane przez komponent drop. Zaimplementowałem trzy z nich. Pierwszy oznacza, że ramka pojawia się wokół górnej etykiety, gdy mysz zostanie przeciągnięta i upuszczona na komponencie. Pokazuje użytkownikowi, że jest to miejsce, w którym można upuścić. Drugi moduł obsługi zdarzeń usuwa ramkę ponownie, jeśli mysz nie wskazuje już komponentu. Następnie zdarza się, że procedura obsługi drop () jest wykonywana podczas upuszczania - czyli po zwolnieniu myszy. Metoda sprawdza, czy obiekt, który ma zostać upuszczony, ma poprawny DataFlaver (jest składnikiem JLabel), a jeśli tak, to wykonywana jest następująca instrukcja: droplabel.setbackground (((JLabel) e.gettransferable (). gettransferdata (LABEL_FLAVOR)). getbackground ()); Metoda pobiera obiekt, który został przesłany i przekształca go w JLabel. Następnie kolor tła komponentu droplabel jest zmieniany na kolor tła przenoszonego obiektu. W następnym kroku metoda usuwa ramkę i na końcu wykonywana jest instrukcja ollowing e.dropcomplete (true); co oznacza, że operacja upuszczenia jest wykonana poprawnie. Wreszcie istnieje klasa DnDGesture, która implementuje interfejs DragGestureListener. Ten interfejs definiuje tylko jedną metodę (moduł obsługi zdarzeń), która jest metodą wykonywaną po rozpoczęciu operacji przeciągania i upuszczania. W rzeczywistości jest to metoda, która łączy to wszystko razem. Metoda zaczyna określać odniesienie do etykiety, której dotyczy operacja przeciągania. Następnie metoda zmienia kursor, aby uzyskać efekt wizualny operacji. Na koniec metoda rozpoczyna operację od określenia wybranego kursora, a także enkapsulacji bieżącego obiektu w obiekcie zbywalnym. Jak pokazano powyżej, przeciąganie i upuszczanie wymaga części, ale jest to ta sama rzecz, która powinna się zdarzyć za każdym razem. 7.1 PRZESUŃ OBRAZY Powyższe nie jest typową operacją przeciągania i upuszczania, a zatem poniższy przykład, który pokazuje lepiej, jak używać przeciągania i upuszczania w programie. W rzeczywistości program nie zawiera wiele nowych, a przeciąganie i upuszczanie jest takie samo, jak w powyższym przykładzie zaimplementowanym przy użyciu 4 klas wewnętrznych. Jeśli otworzysz program, pojawi się puste okno:

124 Jeśli otworzysz Pliki, możesz przeciągnąć obrazy (pliki JPG, GIF i PNG) do okna, a następnie pojawią się one jako ikony : Jeśli obrazy są kopiowane do okna, możesz przeciągnąć je myszką w inne miejsce. Program jest podobny do powyższego, ale tym razem są dwie operacje przeciągania i upuszczania. Możesz wskazać plik (obraz) w folderze Pliki i przeciągnąć go nad okno aplikacji. Oznacza to, że operacje przeciągania są inicjowane w innym programie (pliki programu), podczas gdy operacja upuszczania jest wykonywana w moim programie. Druga operacja, w której można przenosić obrazy, dotyczy wyłącznie programu Java, w którym operacja przeciągania jest uruchamiana na podstawie obiektu w oknie programu, a operacja upuszczania jest wykonywana na tym samym obiekcie, co powyżej. Okno programu jest bardzo proste i składa się tylko z JPanel bez manga. Panel nazywa się panelem. W createview () panel ten jest zdefiniowany jako DropTarget, więc odbiera zdarzenia z klasy DnDDrop. Oto ważna metoda

125 public void drop(droptargetdropevent e) try if (e.isdataflavorsupported(dataflavor.javafilelistflavor)) e.acceptdrop(dndconstants.action_copy); java.util.list list = (java.util.list)(e.gettransferable(). gettransferdata(dataflavor.javafilelistflavor)); for (Object obj : list) addcomponent(getimage((file)obj), e.getlocation()); e.dropcomplete(true); catch (Exception ex) e.dropcomplete(false); Jeśli przeciągniesz plik z zewnątrz (tj. Z programu Pliki), typ obiektu jest zdefiniowany przez DataFlavor o nazwie javafilelistflavor. Oznacza to, że zbywalny obiekt hermetyzuje ArrayList, a metody odnoszą się do tej listy. Obiekty listy mają typ File, a dla każdego obiektu wywoływana jest metoda addcomponent (), która dodaje etykietę JLabel do panelu, na który wskazuje mysz. Metoda rozpoczyna się od wywołania metody getimage () z obiektem File jako parametrem. Ta metoda ładuje obraz przy użyciu nazwy pliku i na tej podstawie tworzy ikonę 128 x 128 pikseli. Jednak tylko jeśli jest to plik jpg, png lub gif. W przeciwnym razie zostanie użyta ikona domyślna. Program obsługuje zatem przeciąganie losowych plików do okna. private JLabel getimage(file file) String name = file.getabsolutepath().tolowercase(); ImageIcon icon = name.endswith("png") name.endswith("jpg") name.endswith("gif")? scaleimage(new ImageIcon(fi : deficon; JLabel label = new JLabel(icon); label.settext(cut(file.getname())); label.sethorizontaltextposition(jlabel.center); label.setverticaltextposition(jlabel.bottom); return label; private static ImageIcon scaleimage(imageicon icon)

126 double w = icon.geticonwidth(); double h = icon.geticonheight(); double z = Math.min(128 / w, 128 / h); return new ImageIcon(icon.getImage().getScaledInstance( (int)(w * z), (int)(h * z), java.awt.image.scale_smooth)); Po określeniu (utworzeniu) ikony JLabel jest tworzony dla ikony, a jednocześnie nazwa pliku pojawia się pod ikoną, a następnie ikona jest dodawana do panelu: private void addcomponent(component component, Point point) component.setlocation(point); component.setsize(component.getpreferredsize()); panel.add(component); repaint(); Następnie możesz przeciągnąć pliki do programu i upuścić je w oknie. Oznacza to, że dopóki żaden ze składników klasy nie powinien być przeciągalny, nie jest konieczne implementowanie trzech interfejsów DragSourceListener, DragGestureListener i Transferable. Jeśli musisz także móc przeciągać komponenty w oknie, trzy interfejsy muszą zostać zaimplementowane. Ponieważ narysowanie JLabel musi być możliwe, obiekt Transferable można zaimplementować dokładnie w taki sam sposób, jak w poprzednim programie. Następnie jest klasa DnDSource, która implementuje interfejs DragSourceListener, gdzie konieczne jest zaimplementowanie metody dragdropend (): public void dragdropend (DragSourceDropEvent e) if ((e.getdropsuccess ()) && (e.getdropaction () == DnDConstants.ACTION_MOVE)) panel.remove (dndlabel); panel.repaint (); dndlabel = null;

127 Powodem jest to, że po przeniesieniu komponentu i upuszczeniu go utworzono kopię, a oryginalny komponent należy usunąć. Wreszcie istnieje klasa DnDGesture (), która pozostaje niezmieniona w stosunku do poprzedniego programu. PROBLEM 4 Powinieneś napisać program, który możesz wywołać DropTable. Program musi otworzyć następujące okno: U góry znajduje się JTable w JScrollPane, natomiast u dołu JPanel bez menedżera układu. Podobnie jak w poprzednim programie, możesz przeciągać pliki reprezentujące obrazy z systemu plików (program Pliki) i upuszczać je na JTable, a każdy plik wstawia wiersz w tabeli. Możesz również wziąć wiersz w tabeli i przeciągnąć go w dół nad dolnym panelem i upuścić tam, a wynikiem będzie etykieta z ikoną. Oznacza to, że komponent JTable obsługuje zarówno przeciąganie, jak i upuszczanie. Poniżej znajduje się okno, w którym upuszczono 5 ikon (obrazów) w składniku JTable, a trzy z nich przeciągnięto w dolnym panelu 8. EDYCJA TEKSTU W praktyce programowanie, edycja tekstów odgrywa ważną rolę, aw większości kontekstów nie ma wielkich wyzwań, ponieważ Swing zapewnia więcej komponentów, które można wykorzystać do wprowadzania tekstu. Do tej pory patrzyłem na komponenty JTextField i JTextArea, które służą do

128 wprowadzania pojedynczych linii tekstowych i wprowadzania wielu linii. Jak pokazały poprzednie przykłady, korzystanie z tych komponentów jest proste, ale w rzeczywistości istnieją cztery dodatkowe komponenty do wprowadzania tekstu. Jednym z nich jest JPasswordField, którego już raz użyłem i jak sama nazwa wskazuje służy do wprowadzania hasła. Zasadniczo jest to JTextField, ale z tą różnicą, że wprowadzone znaki nie pojawiają się, ale pole pokazuje tylko kropkę dla każdego wprowadzonego znaku (jeśli nie zaznaczysz, że nic nie powinno być wyświetlane). Innym polem wejściowym jest JFormattedTextField, który jest również JTextField, ale w którym określasz sposób formatowania wprowadzonych danych. Na przykład możesz wprowadzić tylko liczbę całkowitą. Alternatywą dla JTextArea jest również JTextPane, w którym można edytować stylizowany tekst i, na przykład, decydować, które kolory i czcionki mają być użyte dla części tekstu. Wreszcie istnieje JEditorPane, który używa tak zwanego EditorKit do zdefiniowania sposobu wyświetlania i edytowania tekstu. Te komponenty reprezentują hierarchię w następujący sposób: Należy pamiętać, że JTextComponent jest wspólną klasą podstawową dla 6 konkretnych komponentów do wprowadzania tekstu. W tej części zajmę się komponentami JFomattedTextField, JTextPane i JEditorPane, a także szeregiem klas używanych przez te komponenty. Komponenty nie są wcale proste, ale z drugiej strony zapewniają programistom bardzo dużą elastyczność w pracy z tekstem. Edycja tekstu jest złożona, ponieważ komponenty powinny obsługiwać zdarzenia na klawiaturze i myszy, a komponenty powinny mieć możliwość wyświetlania tekstu w stylu. Jednak zanim zacznę, pokażę program, który otwiera okno, jak pokazano poniżej, gdzie program ilustruje trochę, jak działają komponenty. package textfields; import java.awt.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.border.*; public class MainView extends JFrame public static final String text1 = "Hello world"; public static final String text2 = "The first three <font size='+1' color='blue'>danish kings</font> are

129 <br/><font size='+2' color='red'>gorm den Gamle</font>, <font size='+2' color='red'>harald Blåtand</font> and <font size='+2' color='red'>svend Tveskæg</font>."; public MainView() super("text fields"); setsize(700, 500); setlayout(new FlowLayout()); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() setlayout(new GridLayout(2, 3, 5, 5)); add(createtextfields()); add(createtextare1()); add(createtextare2()); add(createtextpane()); add(createeditorpane1()); add(createeditorpane2()); private JScrollPane createeditorpane1() JScrollPane scroll = new JScrollPane(new JEditorPane("text/plain", text2)); scroll.setborder(new TitledBorder("JEditorPane (text/plain)")); return scroll; private JScrollPane createeditorpane2() JScrollPane scroll = new JScrollPane(new JEditorPane("text/html", text2));

130 scroll.setborder(new TitledBorder("JEditorPane (text/html)")); return scroll; private JScrollPane createtextpane() JTextPane field = new JTextPane(); field.settext(text2); SimpleAttributeSet attrs1 = new SimpleAttributeSet(); StyleConstants.setForeground(attrs1, Color.red); StyleConstants.setFontSize(attrs1, 14); SimpleAttributeSet attrs2 = new SimpleAttributeSet(); StyleConstants.setForeground(attrs2, Color.blue); StyleConstants.setFontSize(attrs2, 10); StyledDocument sdoc = field.getstyleddocument( ); sdoc.setcharacterattributes(0, 16, attrs1, false); sdoc.setcharacterattributes(17, 28, attrs2, false); sdoc.setcharacterattributes(45, 12, attrs1, false); sdoc.setcharacterattributes(57, 7, attrs2, false); sdoc.setcharacterattributes(64, 4, attrs1, false); sdoc.setcharacterattributes(68, 33, attrs2, false); sdoc.setcharacterattributes(101, 14, attrs1, false); sdoc.setcharacterattributes(115, 7, attrs2, false); sdoc.setcharacterattributes(122, 2, attrs1, false); sdoc.setcharacterattributes(124, 28, attrs2, false); sdoc.setcharacterattributes(152, 14, attrs1, false); sdoc.setcharacterattributes(166, 7, attrs2, false); sdoc.setcharacterattributes(173, 5, attrs1, false); doc.setcharacterattributes(178, 28, attrs2, false); sdoc.setcharacterattributes(206, 14, attrs1, false); sdoc.setcharacterattributes(219, 7, attrs2, false); sdoc.setcharacterattributes(226, 1, attrs1, false); JScrollPane scroll = new JScrollPane(field); scroll.setborder(new TitledBorder("JTextPane")); return scroll;

131 private JScrollPane createtextare1() JScrollPane scroll = new JScrollPane(new JTextArea(text2)); scroll.setborder(new TitledBorder("JTextArea (line wrap off)")); return scroll; private JScrollPane createtextare2() JTextArea field = new JTextArea(text2); field.setlinewrap(true); field.setwrapstyleword(true); JScrollPane scroll = new JScrollPane(field); scroll.setborder(new TitledBorder("JTextArea (line wrap on)")); return scroll; private JPanel createtextfields() JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); panel.add(createtextfield()); panel.add(createpasswordfield()); panel.add(createformattedfield()); return panel; private JPanel createtextfield() JPanel panel = new JPanel(new FlowLayout()); panel.setborder(new TitledBorder("JTextField")); panel.add(new JTextField(text1)); return panel;

132 private JPanel createpasswordfield() JPanel panel = new JPanel(new FlowLayout()); panel.setborder(new TitledBorder("JPasswordField")); panel.add(new JPasswordField(text1)); panel.setpreferredsize(new Dimension(130, 50)); return panel; private JPanel createformattedfield() JFormattedTextField field = new JFormattedTextField(createFormatter()); field.setvalue(text1); field.setpreferredsize(new Dimension(80, 20)); JPanel panel = new JPanel(new FlowLayout()); panel.setborder(new TitledBorder("JFormattedTextField")); panel.add(field); panel.setpreferredsize(new Dimension(160, 50)); return panel; private MaskFormatter createformatter() try return new MaskFormatter("UUUUU"); catch (Exception ex) return null;

133 Program dzieli okno na 6 obszarów za pomocą GridLayout. W lewym górnym rogu wyświetlane są JTextField, JPasswordField i JFormattedTextField. Pierwsze dwa, nie powinienem wspomnieć, ale trzeci jest tworzony w metodzie createformattedfield () i ważne jest, aby był inicjowany za pomocą obiektu MaskFormatter utworzonego w metodzie createformatter (). MaskFormatter to klasa, która pozwala zdefiniować określony wzorzec, do którego musi się stosować treść pola JFormattedTextField. W takim przypadku wzorem jest UUUUU, co oznacza 5 wielkich liter. Pole jest inicjowane tekstem Witaj świecie, w wyniku czego pole pokazuje CZEŚĆ, a zatem pierwszych 5 liter zamienionych na wielkie litery. Powinieneś zbadać, co się stanie, jeśli wpiszesz w polu i zauważysz, że można wprowadzić tylko 5 znaków, że małe litery są automatycznie konwertowane na wielkie litery, a wszystkie znaki niebędące literami są ignorowane. Klasa MaskFormatter ma wiele opcji formatowania tekstu i zachęcamy do przeczytania pomocy, aby dowiedzieć się o możliwościach. Istnieją również inne klasy do formatowania tekstu i należy zwrócić szczególną uwagę na klasę NumberFormat. Górna linia okna pokazuje dwa dodatkowe pola, które oba są polami JTextArea. Pierwszy nie przerywa linii, dlatego cały tekst jest wyświetlany jako jedna długa linia (chyba że wstawisz bezpośrednio podział linii). Drugi JTextArea automatycznie przerwie linie, które są zbyt długie. Należy zauważyć, że tekst jest zdefiniowany jako HTML, a ponieważ JTextArea nie formatuje tekstu, wszystkie znaki są wyświetlane bez interpretacji. Pole w lewym dolnym rogu pokazuje JTextPane utworzony za pomocą metody createtextpane (). JTextPane pokazuje stylizowany tekst, co oznacza, że możesz wstawić atrybuty do tekstu, które określają sposób formatowania tekstu. W takim przypadku wprowadza się rozmiar czcionki i atrybuty koloru. Powinieneś przestudiować metodę createtextpane () i tutaj konkretnie, jak wstawiać atrybuty. Na koniec, każde z dwóch ostatnich pól w dolnym wierszu pokazuje JEditorPane. Tutaj możesz określić, w jaki sposób tekst ma być renderowany za pomocą typu MIME, i dokładnie to

134 oznacza, którego EditorKit użyć. Pierwszy wskazuje tekst / zwykły, a zatem pokazuje tekst jako zwykły tekst, podczas gdy ostatni używa text / html, a tym samym interpretuje znaczniki html i renderuje tekst jako dokument HTML. Zasadniczo 6 składników tekstowych działa w ten sam sposób, który można krótko opisać jako: Składnik ma delegata interfejsu użytkownika, który między innymi określa właściwości czcionek i kolorów. Definiuje także kursor i wyróżnik komponentu, a także InputMap i ActionMap, które odnoszą się do klawiatury i poleceń, takich jak wytnij / kopiuj / wklej, zaznacz wszystko, zaznacz od końca do końca, przewiń w dół itd. na. To także delegat interfejsu użytkownika tworzy instancję EditorKit. W przypadku JTextArea jest to DefaultEditorKit, który definiuje większość akcji komponentu. Komponent tekstowy działa na dokumencie (takim jak PlainDocument) utworzonym przez komponent lub przesłanym do konstruktora. Obiekt dokumentu reprezentuje tekst, dzieląc go na jeden lub więcej elementów. Obiekt Element reprezentuje część zawartości dokumentu, a także informacje o stylu. Na przykład JTextArea tworzy obiekt Element dla każdej linii, ale ignoruje wszystko o stylach. Dokument tekstowy rejestruje się jako nasłuchiwanie zdarzeń, które musi śledzić, i rejestruje między innymi jako DocumentListener, aby mógł się aktualizować, jeśli obiekt Document zostanie zmieniony. To delegat interfejsu użytkownika jest odpowiedzialny za rysowanie komponentu za pomocą EditorKit komponentu i ViewFactory. Ta klasa tworzy hierarchię jednego lub więcej obiektów widoku na podstawie obiektów elementu dokumentu, a hierarchia ta jest rysowana na ekranie. Ta historia niewiele mówi, ale wspomina o kilku klasach i wszystkie te klasy można zastąpić, dzięki czemu komponent tekstowy będzie działał tak, jak chcesz. Są to klasy, które sprawiają, że tekstowy interfejs API języka Java jest niesamowicie elastyczny, a przedmiotem dalszej części tego rozdziału jest podanie przykładów tego, co możesz zrobić. 8.1 JFORMATTEDTEXTFIELD Jak pokazano we wstępnym przykładzie, JFormattedTextField to JTextField, w którym formatyzator jest dołączony do komponentu i określa sposób formatowania zawartości pola. W przykładzie wprowadzającym użyłem MaskFormatter, którego można użyć, aby zapewnić, że można wprowadzać tylko niektóre znaki, ale istnieje wiele innych opcji. Typem formatera jest AbstractFormatter, a określony formater określa format i kiedy to się dzieje. W odniesieniu do tego ostatniego może się zdarzyć podczas wprowadzania, na przykład w przypadku MaskFormatter, ale w przypadku innych obiektów formatyzujących formatowanie odbywa się najpierw po utracie ostrości przez pole. Dzięki opcji dla komponentu możesz określić, co należy zrobić, jeśli dane wejściowe nie są zgodne z formatem, ale domyślnym ustawieniem jest to, że komponent formatuje zawartość tak dobrze, jak to możliwe i ignoruje to, co nie jest zgodne z formatem. W poniższym przykładzie użyto ustawienia domyślnego. Okno pokazuje 5 komponentów JFormattedTextField, a po kliknięciu OK sformatowany obiekt pojawi się w polu po prawej stronie.

135 Składnik JFormattedTextField może sformatować dowolny obiekt, a wynik zależy od formatyzatora. Oto dwie opcje. Jeśli formater nie jest określony, komponent wybierze format na podstawie typu obiektu. W przeciwnym razie używany jest formater powiązany z komponentem w konstruktorze. Kod pokazano poniżej, a użytkownik jest proszony o zapoznanie się z nim, a także o przetestowanie programu, a zwłaszcza o to, jakie obiekty formatu są używane. package formatterfields; import java.util.*; import java.text.*; import java.awt.*; import javax.swing.*; import javax.swing.border.*; public class MainView extends JFrame public MainView() super("formatted text fields"); setsize(500, 380); setlayout(new FlowLayout()); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel();

136 panel.setborder(new EmptyBorder(20, 20, 20, 20)); panel.setlayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.add(createfield("date", new Date())); panel.add(createfield("integer", new Integer(1234))); panel.add(createfield("double", NumberFormat.getInstance(), new Double(Math.PI))); panel.add(createfield("currency", NumberFormat.getCurrencyInstance(), new Double( ))); panel.add(createfield("percent", NumberFormat.getPercentInstance(), new Double(0.155))); add(panel); private JPanel createfield(string text, Object value) JFormattedTextField field = new JFormattedTextField(value); return createfield(text, field); private JPanel createfield(string text, Format format, Object value) JFormattedTextField field = new JFormattedTextField(format); field.setvalue(value); return createfield(text, field); private JPanel createfield(string text, JFormattedTextField field) field.setpreferredsize(new Dimension(120, 22)); JTextField result = new JTextField(); result.seteditable(false); result.setpreferredsize(new Dimension(220, 22)); JButton cmd = new JButton("OK"); cmd.setpreferredsize(new Dimension(60, 22)); cmd.addactionlistener(e -> result.settext(field.getvalue().tostring())); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); panel.setborder(new TitledBorder(text)); panel.add(field); panel.add(cmd);

137 panel.add(result); return panel; Zachęcamy również do zapoznania się z pomocą i innymi typami formaterów ĆWICZENIE 5 Utwórz kopię rozwiązania Obliczenia dla ćwiczenia 4 i wywołaj kopię Obliczenia 1. Otwórz go w NetBeans. Usuń wszystko, co ma związek ze schowkiem, aby sam program mógł obliczyć całkowitą kwotę. Musisz teraz zmienić trzy pola wejściowe na komponenty JFormattedTextField, w których liczba jednostek musi użyć NumberFomat, aby sformatować liczbę całkowitą, podczas gdy inne muszą również użyć NumberFormat, aby przeliczyć wartość na walutę. Gdy program działa z tymi zmianami, zmień format pola txtprice (aby wprowadzić cenę jednostkową), aby zamiast tego używał formatu dziesiętnego. Powinieneś dokładnie przestudiować klasę DecimalFormat i zbadać, jakie są możliwości - i jest ich wiele. 8.2 CARET Do każdego komponentu JTextComponent dołączany jest kursor, który wizualnie pokazuje, gdzie ma zostać wykonana następna operacja (gdzie należy wstawić znak, gdzie usunąć znak itd.). Zwykle jest to cienka linia, która miga, ale jest to obiekt typu DefaultCaret i zasadniczo zadaniem klasy jest narysowanie tego konkretnego karetki we właściwym miejscu. Klasa JTextArea (i podobnie jak inne komponenty tekstowe) ma obiekt typu DefaultCaret, dzięki czemu można napisać klasę pochodną, która inaczej rysuje daszek, a następnie powiązać obiekt tej klasy ze składnikiem. To nie jest bardzo trudne (nawet okazja ma tylko ograniczone zainteresowanie). Na przykład poniżej pokazano niestandardowy karetkę, która rysuje karetkę w następujący sposób: a klasę można zapisać jako: class ACaret extends DefaultCaret public ACaret() setblinkrate(500); protected synchronized void damage(rectangle rect) if (rect == null) return; x = rect.x; y = rect.y; width = 6;

138 height = rect.height + 1; repaint(); public void paint(graphics g) JTextComponent component = getcomponent(); if (component == null) return; Rectangle rect = null; try rect = component.modeltoview(getdot()); catch (BadLocationException e) return; if (rect == null) return; if (isvisible()) g.setcolor(color.red); g.drawline(rect.x, rect.y, rect.x, rect.y + rect.height); g.drawline(rect.x, rect.y, rect.x + 5, rect.y); g.drawline(rect.x, rect.y + rect.height, rect.x + 5, rect.y + rect.height); Konstruktor zaczyna określać, że kursor komponentu powinien mrugać co pół sekundy. W przeciwnym razie większość występuje w metodzie paint (), która jest metodą wywoływaną przez system wykonawczy, ilekroć konieczne jest jej przerysowanie (i często dzieje się tak, jeśli przenosisz ją za pomocą klawiszy strzałek lub wprowadzasz znak). Metoda musi narysować cyfrę, która występuje w ostatniej instrukcji if, ale przed obliczeniem, gdzie narysować. Tutaj klasa DefaultCaret ma to, co jest potrzebne. Metoda getdot () zwraca logiczną pozycję tekstu w karetce, a tę pozycję można przekonwertować na współrzędne względem komponentu za pomocą metody modeltoview (). Rezultatem jest prostokąt, który otacza daszek komponentów. Przy użyciu tego prostokąta można narysować figurę. Klasa ma inną metodę o nazwie damage (), która jest wywoływana przez system rutime, gdy konieczne jest przerysowanie. Klasa DefaultCaret ma chronione zmienne, które są używane do określania obszaru wymaganego do przerysowywania, a te są inicjowane przez metodę damage (). Po zainicjowaniu zmiennych zostanie wykonana odmalowanie (), co zapewni, że paint () zostanie wykonany w pewnym momencie. Następnie istnieje następująca klasa, która korzysta z powyższego niestandardowego karetki:

139 public class MainView extends JFrame public MainView() super("formatted text fields"); setsize(500, 380); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); JTextArea txt = new JTextArea(); // txt.setfont(new Font("Serif", Font.PLAIN, 24)); txt.setcaret(new ACaret()); txt.settext("gorm den Gamle\nHarald Blåtand\nSvend Tveskæg"); panel.add(new JScrollPane(txt)); add(panel); Nie ma wiele do wyjaśnienia, ale powinieneś zauważyć, jak zdefiniować, że komponent JTextArea musi używać innej karetki. Oświadczenie z komentarzem ma pokazać, że nowy kursor uwzględnia także inną czcionkę. Poniżej znajduje się przykład uruchomienia programu, w którym komentarz został usunięty, a kursor został przeniesiony:

140 8.3 PODŚWIETLANIE Komponent tekstowy ma również tak zwany zakreślacz, który jest obiektem służącym do wyświetlania wybranego tekstu. Domyślnie wyświetla podświetlony tekst z innym kolorem tła i jest wykonywany przez obiekt Zakreślacza zdefiniowany przez interfejs Zakreślacza.HighlightPainter, a tym samym interfejs wewnętrzny w interfejsie Zakreślacza. Możliwe jest samodzielne wdrożenie tego interfejsu (choć nie ma prawie żadnych powodów), ale poniższa klasa jest przykładem, w którym tekst jest podświetlony czerwonym podkreśleniem: class AHighlightPainter implements Highlighter.HighlightPainter private void paintline(graphics g, Rectangle rect, int x) g.fillrect(rect.x, rect.y + rect.height 3, x rect.x, 3); public void paint(graphics g, int p0, int p1, Shape shape, JTextComponent component) Rectangle rect0 = null; Rectangle rect1 = null; try rect0 = component.modeltoview(p0); rect1 = component.modeltoview(p1); catch (BadLocationException ex) return; if (rect0 == null rect1 == null) return; Rectangle rect2 = shape.getbounds();

141 int max = rect2.x + rect2.width; g.setcolor(color.red); if (rect0.y == rect1.y) paintline(g, rect0, rect1.x); else paintline(g, rect0, max); rect0.y += rect0.height; rect0.x = rect2.x; while (rect0.y < rect1.y) paintline(g, rect0, max); rect0.y += rect0.height; paintline(g, rect0, rect1.x); Jak się wydaje, jest to tylko kwestia zastąpienia metody paint (). Parametry to obiekt graficzny do narysowania, początkowa i końcowa pozycja podświetlanego tekstu, prostokąt przepisywania dla podświetlanego tekstu, a następnie składnik tekstowy. Nie chcę przeglądać instrukcji metody, ponieważ w zasadzie dzieje się to w ten sam sposób, co w poprzednim przykładzie z karetką, a kod jest kujonem i należy do tego, co opisuje następna książka, ale w zasadzie wystarczy dowiedzieć się, co się stanie. Należy zauważyć, że metoda jest skomplikowana przez fakt, że zaznaczony tekst może wypełniać kilka wierszy. Należy również zauważyć, że powyższy zakreślacz może być używany tylko przez komponent, w którym wszystkie linie mają tę samą wysokość, to znaczy JTextArea lub JTextField. Program testowy jest identyczny z powyższym, z tą różnicą, że kojarzysz inną karetkę: private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); JTextArea txt = new JTextArea(); txt.setlinewrap(true); txt.setwrapstyleword(true); txt.setcaret(new DefaultCaret() private Highlighter.HighlightPainter hl = new AHighlightPainter(); protected Highlighter.HighlightPainter getselectionpainter() return hl; ); txt.settext("gorm den Gamle\nHarald Blåtand\nSvend Tveskæg"); panel.add(new JScrollPane(txt)); add(panel);

142 8.4 JTEXTPANE Tematem tej sekcji jest komponent JTextPane, który jest niezwykle zaawansowanym komponentem z wieloma opcjami ustawień i wieloma pokrewnymi typami w postaci interfejsów i klas, a poniższe będą jedynie wstępem, ale wystarczającym, aby uzyskać wrażenie, co JTextPane może to zrobić i jak można używać komponentu w praktyce. Zasadniczo można myśleć o JTextPane jako o JTextArea, a zatem o komponencie, w którym można wprowadzać i edytować wiersze tekstu, ale z tą wielką różnicą, że komponent obsługuje styl tekstu. Oznacza to, że poszczególnym elementom tekstowym można przypisać obiekty stylu w postaci, na przykład, czcionki i koloru. Komponent może nawet więcej, ponieważ może również wyświetlać obrazy, a nawet inne komponenty Swing. Podobnie jak inne komponenty Swing, JTextPane jest zaprojektowany przez MVC, a model jest zdefiniowany (podobnie jak inne komponenty tekstowe) interfejsu Dokumentu, a JTextPane używa konkretnej klasy StyledDocument jako modelu. Jest to zatem model, który śledzi poszczególne elementy stylu. Model jest omówiony jako pierwszy w następnej sekcji, a ta sekcja pokaże przede wszystkim, jak używać JTextPane w programie. Chcę zacząć od aplikacji, która otwiera okno z trzema przyciskami. Nad trzema przyciskami znajduje się JTextPane w JScrollPane. Po kliknięciu przycisku Wstaw test tekst Hello World zostanie wstawiony w miejscu kursora. Kliknięcie przycisku Wstaw ikonę zamiast tego dodaje ikonę w pozycji kursora, a kliknięcie przycisku Wstaw komponent dodaje przycisk. Poniżej jest okno po tym, jak mam 1. kliknij dwukrotnie przycisk Wstaw tekst 2. kliknął przycisk Wstaw ikonę 3. cztery razy kliknął przycisk Wstaw tekst 4. dwukrotnie kliknij przycisk Wstaw komponent

143 Jeśli zamiast tego wybierzesz część treści i klikniesz jeden z przycisków, wybrane zostaną zastąpione tym, co jest wstawione. Należy pamiętać, że przyciski są prawdziwymi przyciskami, a kliknięcie jednego z nich spowoduje wyświetlenie okna komunikatu. W rzeczywistości możesz wstawić dowolny komponent Swing, a tym samym JPanel, a następnie możesz wstawić formularz. Ten przykład nie ma nic wspólnego ze stylem, ale nadal pokazuje, jakie dane składnik jest w stanie renderować. Kod pokazano poniżej i nie wypełnia się tak, jak można się spodziewać: package atextpane; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class MainView extends JFrame private static ImageIcon icon = createimage(); private int counter = 0; private JTextPane txtpane = new JTextPane(); public MainView() super("a JTextPane"); setsize(500, 300); createview();

144 setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); panel.add(new JScrollPane(txtPane)); panel.add(createbottom(), BorderLayout.SOUTH); add(panel); private JPanel createbottom() JPanel panel = new JPanel(new FlowLayout()); panel.add(createbutton("insert text", e -> txtpane.replaceselection("hello world"); txtpane.requestfocus(); )); panel.add(createbutton("insert icon", e -> txtpane.inserticon(icon); txtpane.requestfocus(); )); panel.add(createbutton("insert component", e -> txtpane.insertcomponent(createbutton("click " + (++counter), a -> JOptionPane.showMessageDialog(this, "Button " + counter))); txtpane.requestfocus(); )); return panel; private JButton createbutton(string text, ActionListener listener)

145 JButton cmd = new JButton(text); cmd.addactionlistener(listener); return cmd; private static ImageIcon createimage() java.net.url imgurl = MainView.class.getResource("/atextpane/images/linux.png"); return new ImageIcon(new ImageIcon(imgURL, ""). getimage().getscaledinstance(128, 128, Image.SCALE_SMOOTH), ""); Są zdefiniowane trzy zmienne instancji, przy czym pierwsza to ikona do wstawienia, a ostatnia metoda to ta sama metoda, z której korzystałem wiele razy, która ładuje obraz z pliku jar programu i skaluje go. Zmienny licznik jest używany do tekstu przycisków, które można wstawić, aby były od siebie znane. Wreszcie istnieje odwołanie do komponentu JTextPane, aby można było do niego odwoływać się z procedur obsługi zdarzeń trzech przycisków. Komponent jest wstawiany do okna tak jak inne komponenty, a trzy funkcje obsługi zdarzeń mają coś do zapamiętania. Tam powinieneś zauważyć następujące metody na JTextPane: - replaceselection () - inserticon () - insertcomponent () który służy odpowiednio do wstawiania tekstu, ikony i elementu. Niezależnie od nazw, wszystkie działają w ten sam sposób i zastępują treść oznaczoną tym, co wstawić - jeśli coś zostanie wybrane, a w inny sposób wstawione w pozycji daszka. Potem jest stylizacja w postaci atrybutów i stylów. Możesz dołączyć atrybuty do poszczególnych znaków, akapitów i całego dokumentu. Atrybuty znaków odnoszą się do czcionki i koloru tekstu. Atrybuty dotyczące akapitów mogą również dotyczyć wcięć i odstępów między wierszami. Styl to grupa atrybutów, których można używać w wielu miejscach w dokumencie, i może być stylem nazwanym znanym z nazwy. Jeśli zmienisz atrybut według stylu (dołączonego do dokumentu), zmieni się atrybut całego tekstu w dokumencie, który używa tego stylu. Jeśli styl jest przypisany do akapitu, jego atrybuty można zastąpić, dołączając atrybuty do części tekstu akapitu. Atrybut jest parą klucz / wartość, a rodziny atrybutów są zdefiniowane przez trzy interfejsy: AttributeSet, MutableAttributeSet i Style. Teraz pokażę program z JTextPane, który używa stylów. Program otwiera okno z JTextPane i menu:

146 Menu zawiera trzy pozycje menu - Ustaw styl - Zmień styl - Stwórz styl gdzie pierwsze dwa to dwa puste podmenu, a dolne to funkcja używana do tworzenia stylu akapitu. Jeśli wybierzesz ten element menu, pojawi się okno, jak pokazano poniżej, w którym możesz utworzyć styl nazwany, który definiuje czcionkę. W tym miejscu należy szczególnie pamiętać, że można określić, ile powietrza powinno znajdować się poza sekcją. Po utworzeniu stylu do każdego z dwóch podmenu jest dodawany element menu, a następnie można zastosować ten styl do akapitu, w którym znajduje się kursor, i można edytować styl, który otwiera to samo okno dialogowe, jak pokazano powyżej. Poniżej znajduje się przebieg programu, w którym wprowadzono 7 wierszy:

147 Zauważ, że każda linia to akapit. Następnie tworzone są 4 style, które są następnie stosowane do 7 linii. Wynik jest następujący: Potem jest kod programu, który trochę wypełnia i chcę zacząć od okna dialogowego do stylów konserwacji: class StylePanel extends JPanel private static final String[] fonts = "Monospaced", "Serif", "Sans", "SansSerif" ; private static final String[] sizes = "8", "10", "12", "14", "18", "24", "36", "48", "72" ; private JTextField txtname = new JTextField(); private JComboBox lstfont = new JComboBox(fonts); private JComboBox lstsize = new JComboBox(sizes); private JTextField txtleft = new JTextField(); private JTextField txtright = new JTextField();

148 private JTextField txttop = new JTextField(); private JTextField txtbottom = new JTextField(); private JCheckBox cmdbold = new JCheckBox("Bold"); private JCheckBox cmditalic = new JCheckBox("Italic"); public StylePanel() super(new BorderLayout(5, 5)); JPanel left = new JPanel(new GridLayout(8, 1, 0, 5)); JPanel right = new JPanel(new GridLayout(8, 1, 0, 5)); add(left, BorderLayout.WEST); add(right); left.add(new JLabel("Style Name", JLabel.LEFT)); right.add(txtname); left.add(new JLabel("Font", JLabel.LEFT)); right.add(lstfont); left.add(new JLabel("Size", JLabel.LEFT)); right.add(lstsize); left.add(new JLabel("Left margin", JLabel.LEFT)); right.add(txtleft); left.add(new JLabel("Right margin", JLabel.LEFT)); right.add(txtright); left.add(new JLabel("Top margin", JLabel.LEFT)); right.add(txttop); left.add(new JLabel("Bottom margin", JLabel.LEFT)); right.add(txtbottom); left.add(new JLabel()); JPanel panel = new JPanel(new GridLayout(1, 2)); panel.add(cmdbold); panel.add(cmditalic); right.add(panel); clear();

149 public void clear() txtname.settext(""); txtname.seteditable(true); lstfont.setselectedindex(0); lstsize.setselectedindex(2); txtleft.settext("0.0"); txtright.settext("0.0"); txttop.settext("0.0"); txtbottom.settext("0.0"); cmdbold.setselected(false); cmditalic.setselected(false); public String getstylename() String name = txtname.gettext().trim(); if (name.length( ) > 0) return name; return null; public void initstyle(style style) StyleConstants.setFontFamily(style, StyleConstants.setFontSize(style, (String)lstFont.getSelectedItem()); Integer.parseInt((String)lstSize.getSelectedItem())); StyleConstants.setLeftIndent(style, Float.valueOf(txtLeft.getText()).floatValue()); StyleConstants.setRightIndent(style, Float.valueOf(txtRight.getText()).floatValue()); StyleConstants.setSpaceAbove(style,

150 Float.valueOf(txtTop.getText()).floatValue( )); StyleConstants.setSpaceBelow(style, Float.valueOf(txtBottom.getText()).floatValue()); StyleConstants.setBold(style, cmdbold.isselected()); StyleConstants.setItalic(style, cmditalic.isselected()); public void initfields(style style) txtname.settext(style.getname()); lstfont.setselecteditem(styleconstants.getfontfamily(style)); txtname.seteditable(false); lstsize.setselecteditem(integer.tostring(styleconstants.getfontsize(style))); txtleft.settext(float.tostring(styleconstants.getleftindent(style))); txtright.settext(float.tostring(styleconstants.getrightindent(style))); txttop.settext(float.tostring(styleconstants.getspaceabove(style))); txtbottom.settext(float.tostring(styleconstants.getspacebelow(style))); cmdbold.setselected(styleconstants.isbold(style)); cmditalic.setselected(styleconstants.isitalic(style)); Nie ma wiele do wyjaśnienia na temat projektu, ale należy zauważyć, że klasa dziedziczy JPanel, a nie JDialog. Wyjaśnienie znajduje się poniżej. W przeciwnym razie dwie ostatnie metody są najbardziej interesujące. Pierwszy inicjuje obiekt Style za pomocą klasy StyleConstants. Klasa ma szereg stałych dla typowych wartości atrybutów i szereg metod statycznych, które są używane do inicjowania atrybutów w obiekcie stylu. Podobnie ostatnia metoda służy do inicjalizacji pól okna dialogowego wartościami z obiektu Style i ponownie zwraca uwagę na użycie klasy StyleConstants, która ma metody statyczne, które zwracają wartości. Następnie jest kod okna, w którym pokazałem tylko tę część kodu, która odnosi się do komponentu i stylów JTextPane: public class MainView extends JFrame private JTextPane txtpane = new JTextPane(); private StylePanel stylepanel = new StylePanel(); private JMenu setmenu; private JMenu modmenu; public MainView()

151 private void createview() createmenubar(); JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); panel.add(new JScrollPane(txtPane)); add(panel); private void createmenubar() private void createmenuitem(jmenu menu, String text, ActionListener listener) JMenuItem item = new JMenuItem(text); item.addactionlistener(listener); menu.add(item); public void create(actionevent e) stylepanel.clear(); if (JOptionPane.showConfirmDialog(this, stylepanel, "Style Editor", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION && stylepanel.getstylename().length() > 0) Style style = txtpane.addstyle(stylepanel.getstylename(), null); stylepanel.initstyle(style); createmenuitem(setmenu, stylepanel.getstylename(), this::setstyle);

152 createmenuitem(modmenu, stylepanel.getstylename(), this::modstyle); public void setstyle(actionevent e) String stylename = ((JMenuItem)e.getSource()).getActionCommand(); txtpane.setlogicalstyle(txtpane.getstyle(stylename)); public void modstyle(actionevent e) String stylename = ((JMenuItem)e.getSource()).getActionCommand(); Style style = txtpane.getstyle(stylename); stylepanel.initfields(style); if (JOptionPane.showConfirmDialog(this, stylepanel, "Style Editor", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION) stylepanel.initstyle(style); Zauważ, że istnieje zmienna instancji, której typem jest powyższe okno dialogowe (JPanel). Jeśli spojrzysz na moduł obsługi zdarzeń dotyczący tworzenia stylu, zaczyna się on od wykonania metody clear () okna dialogowego, usuwając w ten sposób wszystkie pola. Klasa Swing JOptionPane ma kilka metod, które otwierają okno komunikatu, a wcześniej użyłem showconfirmdialog (), który ma dwa przyciski jako przycisk OK i przycisk Anuluj. Metoda ma co najmniej dwa parametry, przy czym drugi ma typ Object, a ogólnie jest to tekst pojawiający się w oknie komunikatu. Funkcja, o której wcześniej nie wspominałem, polega na tym, że jeśli ten parametr może być komponentem Swing, w tym przypadku jest to JPanel, w wyniku czego powstaje okno dialogowe z przyciskiem OK i przyciskiem Anuluj. Takie podejście jest w rzeczywistości łatwą opcją, jeśli zachodzi potrzeba otwarcia prostego okna dialogowego. Należy zwrócić uwagę, w jaki sposób procedury obsługi zdarzeń dodają dwa elementy menu, jeśli zostanie kliknięty przycisk OK. Moduł obsługi zdarzeń służący do modyfikacji stylu działa zasadniczo w ten sam sposób (otwiera to samo okno dialogowe), ale nie dodaje pozycji menu do menu. Należy również zwrócić uwagę na procedurę obsługi zdarzeń, aby zastosować styl do akapitu, oraz tutaj, jak przypisać styl do akapitu. ĆWICZENIE 6 Musisz utworzyć kopię powyższego projektu (StyledText). Musisz dodać menu o nazwie Wybierz kolor, który musi mieć pozycję menu dla kolorów: - czerwony, zielony, niebieski, żółty, pomarańczowy, szary jasny Szary, purpurowy, różowy i czarny

153 Jeśli wybierzesz jedną z tych pozycji menu, tekst w akapicie, w którym wyświetlany jest kursor, musi zostać wyświetlony w wybranym kolorze. Kiedy to działa, zmień program, aby można było zaznaczyć tekst, a jeśli następnie w menu wybierzesz czcionkę lub kolor, podświetlony tekst musi być wyświetlany z wybranym stylem. Przykład może być taki, jak pokazano poniżej, gdzie jest 7 akapitów: Musisz zapoznać się z pomocą dla JTextPane, aby dowiedzieć się, jak określić wybrany obszar, aw pierwszym programie w tym rozdziale możesz zobaczyć, jak przypisać atrybuty do poszczególnych znaków zamiast akapitu. 8.4 DOKUMENT Jak wspomniano, wszystkie komponenty tekstowe wykorzystują model zdefiniowany powyżej w interfejsie dokumentu. Model reprezentuje tekst, który użytkownik edytuje, a jeśli jest to dokument w stylu, to również model reprezentuje atrybuty. Interfejs Dokument nic nie definiuje stylów, ale zamiast tego odbywa się w pod-interfejsie StyledDocument. Konkretne modele można zatem zilustrować poniższym rysunkiem, na którym AbstractDocument potwierdzony nazwą jest klasą abstrakcyjną, podczas gdy PlainDocument i DefaultStyledDocument są klasami konkretnymi. PlainDocument to model JTextField i JTextArea, natomiast DefaultStyledDocument to model JTextPane i JEditorPane. Obiekt Document dzieli dokument na hierarchię obiektów, w której każdy węzeł ma typ Element i składa się z przesunięcia i położenia końcowego w dokumencie. Ponadto element może być powiązany z atrybutem AttributeSet, a zatem styl nie jest używany na przykład przez JTextField i JTextArea. Obiekt Element opisuje w ten sposób część dokumentu i patrząc z JTextPane interesujące jest, że Element może mieć swój własny styl. Ważnym zadaniem dla AbstractDocument jest wdrożenie mechanizmu blokującego, który zapewnia, że w jednym momencie tylko jeden pisarz może korzystać z dokumentu,

154 ale żaden lub więcej nie może odczytać dokumentu. Jako prosty przykład dołączania własnego dokumentu do komponentu tekstowego program EnterProgram otwiera następujące okno: gdzie użytkownik może wprowadzić tekst, ale można wprowadzić tylko 20 znaków. Pole wejściowe jest komponentem JTextField, a wyzwanie rozwiązuje się przez powiązanie komponentu z innym modelem. Model jest zdefiniowany jako klasa pochodna klasy PlainDocument, która przesłania jedynie metodę insertstring (): class MaxDocument extends PlainDocument private int max; public MaxDocument(int max) this.max = max; public void insertstring(int offset, String str, AttributeSet attr) throws BadLocationException if (getlength() + str.length( ) > max) Toolkit.getDefaultToolkit().beep(); else super.insertstring(offset, str, attr); With this class available, the program can be written as follows: public class MainView extends JFrame private JTextField txtfield = new JTextField();

155 public MainView() super("enter text"); setsize(600, 150); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() txtfield.setdocument(new MaxDocument(20)); txtfield.setpreferredsize(new Dimension(200, 20)); JButton cmd = new JButton("OK"); cmd.addactionlistener( e -> JOptionPane.showMessageDialog(this, txtfield.gettext())); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); panel.setborder(new EmptyBorder(25, 25, 25, 25)); panel.add(new JLabel("Enter text (max 20 characters)")); panel.add(txtfield); panel.add(cmd); add(panel); Jest to prosty program, ale opcje blokowania liczby znaków, które można wprowadzić, są interesujące, ponieważ w praktyce jest to często spotykany problem. Należy jednak pamiętać, że ten sam problem można rozwiązać prościej za pomocą JFormattedTextField i odpowiedniego formatu, takiego jak: JFormattedTextField(new MaskFormatter("********************") ĆWICZENIE 7 Napisz program, który możesz wywołać ElementTree, gdy program otworzy następujące okno:

156 dzie górnym składnikiem jest JTextPane, zaś na dole JTextArea. Górny komponent jest inicjowany następującym ciągiem: Gorm den Gamle \ nharald Blåtand \ nsvend Tveskæg \ nharald d. 2. \ nknud den Store Przed wyświetleniem ciągu do każdej z pięciu nazw należy przypisać atrybuty wskazujące odpowiednio kolor i rozmiar czcionki. Jeśli klikniesz przycisk, musisz uzyskać listę w dolnym składniku, w której każda linia reprezentuje element i dla każdego z nich wyświetlane są przesunięcia zarówno początku, jak i końca znaków obejmujących element i dowolne atrybuty. Zauważ, że ponieważ elementy stanowią hierarchię, metoda wypełniania tekstu w dolnym komponencie musi być zapisywana rekurencyjnie. Spróbuj zinterpretować wyniki w dolnym JTextArea. Możesz spróbować wprowadzić więcej tekstu w górnym polu JTextPane, a następnie kliknąć OK, aby zobaczyć, czy uzyskasz oczekiwany wynik. 8.5 JEDYTORPAN Jako ostatni ze składników Swinga należy wspomnieć o JEditorPane. Jest to komponent, który rzadko interesuje się sobą, ale może być postrzegany jako podstawowa klasa stylizowanych komponentów tekstowych, a JTextPane jest przykładem komponentu tekstowego pochodzącego z JEditorPane. Jak widać z powyższego, JTextPane jest składnikiem, którego można użyć do edycji dokumentu, do którego przypisane są informacje o stylu. Rzeczywiste informacje o tekście i stylu są reprezentowane przez StyledDocument, ale wymagana jest pewna logika, aby kontrolować, w jaki sposób można edytować

157 tekst i style (jakie akcje można wykonać), a wszystko to jest zawarte w obiekcie EditorKit. Proste komponenty tekstowe, takie jak JTextField i JTextArea, używają DefaultEditorKit, podczas gdy JTextPane używa pochodnej klasy o nazwie StyledEditorKit. JEditorPane ma na celu zdefiniowanie własnego komponentu tekstowego pochodzącego z JEditorPane poprzez zdefiniowanie własnego zestawu EditorKit i typu dokumentu, a tym samym zdefiniowanie komponentów tekstowych, które dokładnie obsługują potrzebne zadania. W rzeczywistości Swing zawiera dwa przykłady, których można użyć do edycji dokumentów HTML i RTF. W tej sekcji bardzo ogólnie zilustruję, jak napisać własny edytor HTML. Celem nie jest dotarcie do gotowego edytora HTML (który jest obszerny), ale pokazanie nieco więcej informacji na temat składników tekstowych i trochę informacji na temat tego, czym jest EditorKit. Chcę zacząć od przykładu o nazwie AEditorPane. Jeśli uruchomisz program, najpierw pojawi się następujące okno komunikatu: a po kliknięciu OK pojawi się następujące okno: Okno pokazuje następujący dokument HTML zapisany z projektem: <html> <head> <meta charset="utf-8"> <title>simple html page</title> </head> <body> <h1>danish Kings</h1> <h2>the wee know about</h2>

158 <p>gorm den Gamle</p> <p>harald Blåtand</p> <p>svend Tveskæg</p> <h3>but we have heard of others before them</h3> <p><a href=" danish history</a></p> </body> </html> Oznacza to, że dokument jest interpretowany, ponieważ komponent zna poszczególne elementy HTML, a zatem wie, jak je interpretować. W szczególności zwróć uwagę na ostatni wiersz, który jest linkiem do strony i kliknięcie tego linku spowoduje wyświetlenie tej strony w oknie. Wynik nie jest zbyt dobry, ale dlatego, że strona zawiera wiele rzeczy (nowoczesne strony internetowe są bardzo złożone pod względem treści), których składnik nie może zinterpretować, ale przykład pokazuje, że można załadować dowolną stronę internetową z mniej lub bardziej dobrą wyniki wyświetlają zawartość strony w komponencie tekstowym. Przykład jest zatem krokiem w kierunku prostej przeglądarki. Kod programu jest następujący: package aeditorpane; import java.net.*; import java.awt.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.border.*; public class MainView extends JFrame private JEditorPane txtpane = null; public MainView() super("a JEditorPane"); setsize(500, 300); createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); try

159 txtpane = new JEditorPane(loadPage()); txtpane.addhyperlinklistener(new LinkHandler()); JOptionPane.showMessageDialog(this, txtpane.geteditorkit().getclass().getname()); catch (Exception ex) JOptionPane.showMessageDialog(this, ex.tostring()); System.exit(1); txtpane.seteditable(false); panel.add(new JScrollPane(txtPane)); add(panel); private static URL loadpage() return MainView.class.getResource("/aeditorpane/kings.html"); class LinkHandler implements HyperlinkListener public void hyperlinkupdate(hyperlinkevent e) try if (e.geteventtype() == HyperlinkEvent.EventType.ACTIVATED) txtpane.setpage(e.geturl()); catch (Exception ex) JOptionPane.showMessageDialog(MainView.this, ex.tostring());

160 Program definiuje zmienną pojedynczej instancji, której typem jest JEditorPane. Jest on tworzony w metodzie createview (), gdzie powyższy dokument HTML jest przesyłany do konstruktora klasy jako adres URL. Dokument HTML jest spakowany z programem w pliku jar i jest ładowany z pliku jar w metodzie loadpage (). Po utworzeniu komponentu JEditorPane jest powiązany moduł obsługi zdarzeń do kliknięcia łącza. Jest to klasa implementująca interfejs HyperlinkListener, który definiuje pojedynczą metodę o nazwie hyperlinkupdate (), a metoda jest wykonywana po kliknięciu łącza w oknie. W rezultacie treść komponentu jest zastępowana treścią witryny, do której prowadzi link. Po powiązaniu tego modułu obsługi zdarzeń ze składnikiem zostanie otwarte pierwsze okno komunikatu. Patrząc z programu, nie ma sensu, ale pokazuje, który EditorKit jest używany. W tym przypadku jest to HTMLEditorKit. Został on utworzony przez konstruktor w JEditorPane, ponieważ argumentem jest dokument HTML. HTMLEditorKit to gotowy EditorKit, który jest częścią Swing, ale spełnia dalekie od wszystkiego, czego należy wymagać od nowoczesnej przeglądarki, a kiedy wynik kliknięcia łącza w oknie nie jest pożądany, dzieje się tak dlatego, że klasa HTMLEditorKit nie jest wystarczająco obszerny, ale możesz napisać własny - jeśli masz taką potrzebę. EditorKit jest klasą abstrakcyjną, ale DefaultEditorKit jest klasą konkretną, używaną przez proste komponenty tekstowe oraz przez klasę pochodną StyledEditorKit, używaną przez JTextPane, a klasa HTMLEditorKit jest ponownie uzyskiwana z StyledEditorKit. EditorKit określa sposób tworzenia dokumentu, a także metody, które można odczytać dokument ze strumienia i zapisać go w strumieniu. DZIAŁANIA Istnieje wiele zdarzeń związanych z komponentami tekstowymi, a zwłaszcza klawiatury i myszy, a także możesz wprowadzić wiele ustawień dotyczących czcionek i kolorów i nie tylko. Wszystkie te opcje są reprezentowane jako akcje. Akcja jest interfejsem, który dziedziczy dwa inne interfejsy: ActionListener i EventListener i należy myśleć o akcji jako o obiekcie reprezentującym ActionEvent. Celem jest udostępnienie tej samej funkcjonalności kilku komponentom. Akcja mogła powiązać kilka par klucz / wartość, do których masz dostęp, z metodami: Object getvalue(string key) void putvalue(string key, Object value) Szczególną uwagę należy zwrócić na ostatnią metodę, ponieważ zmienia ona wartość, jeśli klucz już istnieje. Interfejs jest implementowany przez klasę AbstractAction, ale klasa oczywiście nie implementuje actionperformed (), a jeśli napiszesz własne typy Action, jest to równoważne z napisaniem klasy, która dziedziczy AbstractAction, a następnie implementacją actionperformed (). Zaletą akcji jest to, że klasy takie jak JButton, JMenuItem itp. Mają konstruktory, które mają parametr Akcja jako parametr, i ułatwia przypisanie funkcji do tego rodzaju obiektów, i wreszcie jest to powód, dla którego Akcje są tutaj wymienione, podczas gdy komponenty tekstowe używają wielu akcji. Jeśli uruchomisz program ActionProgram, otworzy się okno ze składnikiem JTextArea, a także menu:

161 Po otwarciu menu dostępne są dwa podmenu, a każde z tych podmenu pokazuje przegląd działań, które zna JTextArea. Gdy zobaczysz te nazwy, łatwo jest zrozumieć, co oznaczają poszczególne działania. Program jest prosty, a poniżej pokazano tylko metodę tworzenia menu: private void createmenu() Action[] actions = txtarea.getactions(); int n = actions.length / 2; JMenu menu1 = new JMenu("Some actions"); for (int i = 0; i < n; ++i) menu1.add(actions[i]); JMenu menu2 = new JMenu("More actions"); for (int i = n; i < actions.length; ++i) menu2.add(actions[i]); JMenu menu = new JMenu("Actions"); menu.add(menu1); menu.add(menu2); JMenuBar bar = new JMenuBar(); bar.add(menu); setjmenubar(bar); ĆWICZENIE 8 Utwórz kopię powyższego programu. Musisz zmienić program, aby komponent zamiast JTextArea był JTextPane, a więc menu pokazuje wszystkie akcje rozmieszczone w trzech podmenu. Spróbuj zbadać różnice między działaniami wyświetlanymi przez ten program a powyższym programem. SIMPLEEDITOR Chcę pokazać program, który jest prostym edytorem tekstu, a program wygląda jak odpowiedni program, który pokazałem w książce Java 2, ale program jest prostszy, a częściowo dlatego, że istnieją pewne funkcje, które nie są zaimplementowane w tym rozwiązaniu i częściowo dlatego, że tutaj w większym stopniu wykorzystuje to, że wiele niezbędnych funkcji jest wbudowanych w klasę JTextArea, a następnie nie muszę ich implementować. Program otwiera okno z JTextArea:

162 Okno ma również pasek narzędzi z funkcjami otwierania i zapisywania dokumentu, a także ikony do wycinania, kopiowania i wklejania. Te same funkcje znajdują się jako pozycje menu. Wypełniony kod jest następujący: public class MainView extends JFrame private JTextComponent txtcomp = new JTextArea(); private Action openaction = new OpenAction(); private Action saveaction = new SaveAction(); public MainView() private void createview() ((JTextArea)txtComp).setLineWrap(true); JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); panel.add(new JScrollPane(txtComp)); defineactions(); panel.add(createtoolbar(), BorderLayout.NORTH); createmenu(); add(panel); protected void defineactions() defi on), "Cut",

163 "/simpleeditor/images/cut.png"); defi ion), "Copy", "/simpleeditor/images/copy.png"); defi tion), "Paste", "/simpleeditor/images/paste.png"); private void defineaction(action action, String name, String icon) action.putvalue(action.name, name); if (icon!= null) action.putvalue(action.small_icon, createimage(icon)); private JToolBar createtoolbar() JToolBar toolbar = new JToolBar(); toolbar.add(openaction).settext(""); toolbar.add(saveaction).settext(""); toolbar.addseparator(); toolbar.add( txtcomp.getactionmap().get(defaulteditorkit.cutaction)).settext(""); toolbar.add( txtcomp.getactionmap().get(defaulteditorkit.copyaction)).settext(""); toolbar.add( txtcomp.getactionmap().get(defaulteditorkit.pasteaction)).settext(""); return toolbar; private void createmenu() JMenu file = new JMenu("File"); file.add(openaction); file.add(saveaction); file.add(new ExitAction()); JMenu edit = new JMenu("Edit"); edit.add(txtcomp.getactionmap().get(defaulteditorkit.cutaction)); edit.add(txtcomp.getactionmap().get(defaulteditorkit.copyaction)); edit.add(txtcomp.getactionmap().get(defaulteditorkit.pasteaction)); JMenuBar bar = new JMenuBar(); bar.add(file); bar.add(edit); setjmenubar(bar); class ExitAction extends AbstractAction

164 public ExitAction() super("exit"); public void actionperformed(actionevent e) System.exit(0); class OpenAction extends AbstractAction public OpenAction() super("open", createimage("/simpleeditor/images/open.png")); public void actionperformed(actionevent e) JFileChooser chooser = new JFileChooser(); if (chooser.showopendialog(mainview.this)!= JFileChooser.APPROVE_OPTION) return; File file = chooser.getselectedfile(); if (file == null) return; try (BufferedReader reader = new BufferedReader(new FileReader(file))) txtcomp.read(reader, null); catch (IOException ex)

165 JOptionPane.showMessageDialog(MainView.this, "File Not found: " + ex.getmessage(), "Error", JOptionPane.ERROR_MESSAGE); class SaveAction extends AbstractAction public SaveAction() super("save", createimage("/simpleeditor/images/save.png")); public void actionperformed(actionevent e) JFileChooser chooser = new JFileChooser( ); if (chooser.showsavedialog(mainview.this)!= JFileChooser.APPROVE_OPTION) return; File file = chooser.getselectedfile(); if (file == null) return; try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) txtcomp.write(writer); catch (IOException ex) JOptionPane.showMessageDialog(MainView.this, "File not Saved: " + ex.getmessage(), "Error", JOptionPane.ERROR_MESSAGE); private static ImageIcon createimage(string path)

166 java.net.url imgurl = MainView.class.getResource(path); return new ImageIcon(new ImageIcon(imgURL, "").getimage(). getscaledinstance(24, 24, Image.SCALE_SMOOTH), ""); Program definiuje trzy zmienne instancji, pierwsza to komponent JTextArea, a druga to obiekty Action, które służą odpowiednio do otwierania i zapisywania pliku. Kiedy dwa obiekty są tworzone jako zmienne instancji, dzieje się tak dlatego, że są to te same obiekty Action, których można używać zarówno z menu, jak iz paska narzędzi. Typ obiektów jest definiowany przez wewnętrzną klasę w programie. Do zdefiniowania trzech akcji służy metoda definactions (). JTextArea ma operacje kopiowania, wklejania lub wycinania, ale zazwyczaj chcesz nadać im inną nazwę i ewentualnie dołączyć do nich ikonę. Tak dzieje się w metodzie replaceactions (). Dzieje się tak przez ustawienie pary klucz / wartość, które jest wykonywane przez metodę replaceaction (). Na przykład odwołujesz się do akcji cięcia jako: txtcomp.getactionmap (). get (DefaultEditorKit.cutAction) Tutaj DefaultEditorKit.cutAction jest stałą, która identyfikuje tę akcję, a następnie obiekt Action jest wysyłany do metody replaceaction () wraz z tekstem wyświetlanym w menu, a także nazwą ikony, która ma być użyta. Metoda replaceactions () jest wywoływana z metody createview (), więc trzy akcje są inicjowane zgodnie z potrzebami. Następnie istnieje metoda createtoolbar (), która tworzy pasek narzędzi z 5 ikonami lub akcjami. Należy zauważyć, że pierwsze dwa to dwie akcje zdefiniowane jako zmienne instancji na początku programu, podczas gdy pozostałe trzy są przywoływane w taki sam sposób, jak pokazano powyżej. Należy również pamiętać, że dla wszystkich 5 obiektów Action wykonywana jest metoda settext (), która powoduje, że tekst jest pusty. Powodem jest to, że pasek narzędzi nie powinien wyświetlać właściwości tekstowej obiektu. Metoda createmenu () jest w zasadzie identyczna. Zamiast tego obiekty JMenuItem są powiązane z akcją. Zauważ również, że menu Plik zawiera akcję ExitAction, która jest również wewnętrzną klasą reprezentującą akcję. Z tyłu są trzy wewnętrzne klasy, które definiują obiekty Action. Nie będę szczegółowo omawiał trzech klas, ale zauważysz, jak wszystkie dziedziczą AbstractAction. Zwróć również uwagę na to, jak dwa ostatnie kojarzą ikonę, a następnie zauważ, że JTextComponent ma metody, które mogą bezpośrednio zainicjować komponent, odczytując go ze strumienia i zapisując jego zawartość. PROBLEM 5 Musisz napisać program, który możesz wywołać StyledEditor, który otworzy następujące okno:

167 Program powinien zasadniczo działać jak poprzedni program, w którym pierwsze dwa menu są takie same (i odpowiednio pierwsze 5 ikon na pasku narzędzi), ale zamiast JTextArea program musi używać JTextPane. Menu Czcionka powinno wyglądać następująco: ont Style Bold Underline Italic Family SansSerif Serif Monospaced Size Zasadniczo program można napisać jak w poprzednim przykładzie, ale bardziej rozbudowany jest pasek narzędzi i menu. Istnieje jednak niewielki problem, ponieważ JTextPane używa metod read () i write (), które są zdefiniowane w JTextComponent i te metody mogą tylko czytać i zapisywać tekst wspólnego planu. Chociaż nie jest to odpowiednie rozwiązanie, problem można rozwiązać za pomocą serializacji. 9 MIĘDZYNARODOWOŚĆ Czasami programy muszą być używane w kilku krajach, a więc w wielu językach, i wymaga interfejsu użytkownika aplikacji oraz sposobu wyświetlania danych. Jedną z opcji byłoby napisanie kilku wersji tego samego programu (wersja angielska, wersja duńska itd.), Ale ze względów konserwacyjnych nie jest to optymalne rozwiązanie. Jednak Java obsługuje internacjonalizację, więc sam program określa sposób wyświetlania danych w zależności od miejsca ich wykorzystania. Jest to temat tego rozdziału i wcale nie jest prosty, a poniższe są jedynie wstępem do tego, o co w tym wszystkim chodzi. Zasadniczo odnosi się do języka używanego przez program do wyświetlania tekstu, a także formatów dat, godzin i liczb. W rzeczywistości istnieją dwie rzeczy, a internacjonalizacja oznacza, że program obsługuje wiele języków i formatów, a lokalizacja jest rozumiana jako proces, w którym program określa, którego języka

168 i formatów użyć. Wszystko to komplikuje fakt, że więcej krajów używa tego samego języka, a niektóre kraje nawet więcej języków, i w ten sam sposób nie ma wyraźnego rozróżnienia między językiem a regułami formatowania. Patrząc z Javy, punktem początkowym jest klasa Ustawienia regionalne, która zasadniczo składa się z trzech właściwości: 1. kod języka (ISO-639) 2. kod kraju (ISO-3166) 3. wariant kodu, który nie jest wymagany Kod języka składa się z dwóch lub trzech znaków i jako przykłady można wymienić Angielski en eng Francuski fr fra Germande deu Duński da dan Podobnie, kody krajów składają się z dwóch lub trzech znaków i jako przykłady można wymienić Angielski gb gbr Francuski fr fra Niemiecki de deu Dania dk dnk Ustawienia regionalne klasy używają 2-znakowych kodów (jednak 3-znakowe kody języka są dozwolone, jeśli nie ma kodu 2-znakowego). Klasa generalnie nie oferuje wiele, ale wiele metod ma parametr Locale, który jest podstawowym celem klasy. Poniższy program pokazuje przykłady użycia klasy Locale: package localeprogram; import java.util.*; public class LocaleProgram public static void main(string[] args) test1();

169 test2(); test3(); private static void test1() Locale loc = Locale.getDefault(); print(loc); private static void test2() Locale.setDefault(Locale.US); test1(); private static void test3() Locale[] locales = Locale.getAvailableLocales(); System.out.println(locales.length); for (Locale loc : locales) System.out.println(String.format("%s: %s, %s: %s", loc.getdisplaycountry(), loc.getcountry(), loc.getdisplaylanguage(), loc.getlanguage())); private static void print(locale loc) System.out.println(String.format("%s, %s", loc.getdisplaycountry(), loc.getcountry())); System.out.println(String.format("%s, %s", loc.getdisplaylanguage(), loc.getlanguage())); System.out.println(String.format(loc, "%1.2f", )); System.out.println(String.format(loc, "%td", Calendar.getInstance()));

170 Metoda test1 () określa domyślne ustawienia regionalne programu zgodnie z ustawieniami języka urządzenia. Następnie program drukuje informacje o języku i kraju. Ponadto liczba jest formatowana za pomocą String.format () i tutaj należy zauważyć, że pierwszym parametrem jest obiekt lokalny. Zapewnia to wyświetlanie wyniku z poprawnym przecinkiem dziesiętnym. Ostatnia instrukcja w metodzie print () działa w ten sam sposób, ale tym razem dla obiektu kalendarza. Należy pamiętać, że metoda format () klasy String ma niesamowite opcje formowania wyniku - szczególnie w odniesieniu do dat i godziny - i zachęcamy do zapoznania się z pomocą dotyczącą dostępnych opcji. Metoda test2 () pokazuje, że masz możliwość zdefiniowania domyślnych ustawień regionalnych. Możesz sam stworzyć obiekt lokalny, określając język i kraj, ale rzadko jest to konieczne, ponieważ klasa Locale zdefiniowała obiekty dla większości języków. Metoda test3 () pokazuje przegląd obiektów Locale. 9.1 WIĄZKI ZASOBÓW Jeśli program ma być internacjonalizowany, oznacza to, że tekst powinien być dostępny w kilku językach i chociaż Java obsługuje internacjonalizację, zadaniem działu rozwoju jest oczywiście przetłumaczenie tekstu. Przetłumaczony tekst musi być gdzieś zapisany i dzieje się to w pakietach zasobów, co pozwala zapisywać tekst, obrazy i inne zasoby poza programem, a nie jako część kodu. Pakiety zasobów są reprezentowane przez klasę ResourceBundle, która zawiera pary klucz / wartość, gdzie klucz jest łańcuchem, a wartością może być dowolny obiekt. ResourceBundle jest klasą abstrakcyjną i chociaż możesz zdefiniować własną klasę, zwykle jej używasz - PropertyResourceBundle do tekstu - ListResourceBundle do obrazów i innych zasobów Jako przykład pokażę, jak korzystać z pakietu zasobów pierwszego rodzaju, ponieważ jest on najczęściej używany w internacjonalizacji, przede wszystkim w celu uzyskania programu niezależnego od języka Punktem początkowym jest plik poperties, który jest po prostu plikiem tekstowym, którego nazwa pliku ma rozszerzenie.properties. Przykładem może być: di = Ruder he = Hjerte sp = dźwigar cl = Kl \ u00f8r który zawiera cztery teksty z kluczami. Treść można interpretować jako kolory kart do gry. Zauważ, że ø w ostatniej wartości jest zakodowane kodem dla Unicode znaku. Nazwa pliku to Cards.properties

171 to jest ważne. Istnieje również inna wersja pliku z angielskimi nazwami zapisanymi jako: di = diament on = serce sp = pik cl = Klub a tutaj jest nazwa pliku Cards_en_US.properties i znowu nazwa jest absolutnie kluczowa. Następnie rozważ następujący program: package resourceprogram; import java.util.*; public class ResourceProgram public static void main(string[] args) ResourceBundle bundle1 = ResourceBundle.getBundle("Cards"); System.out.println(bundle1.getString("di")); System.out.println(bundle1.getString("he")); System.out.println(bundle1.getString("sp")); System.out.println(bundle1.getString("cl")); ResourceBundle bundle2 = ResourceBundle.getBundle("Cards", Locale.US); for (Enumeration<String> keys = bundle2.getkeys(); keys.hasmoreelements(); ) System.out.println(bundle2.getString(keys.nextElement())); ResourceBundle bundle3 = ResourceBundle.getBundle("Cards", Locale.GERMAN); System.out.println(bundle1.getString("di")); Uruchomienie programu daje wynik:

172 Ruder Hjerte Boksować Klør Klub Diament Łopata Serce Ruder Program rozpoczyna się od utworzenia pakietu ResourceBundle o nazwie bundle1, a jeśli nie określi żadnych ustawień regionalnych, odczyta plik Cards.properties, który jest nazywany plikiem właściwości podstawowych. Następnie drukowane są wartości czterech kluczy, a wynikiem są pierwsze cztery linie. Następnie tworzony jest kolejny pakiet zasobów o nazwie pakiet2, ale tym razem dodawane są ustawienia regionalne. Rezultat jest taki, że plik dla tych ustawień regionalnych jest odczytywany, a mianowicie Cards_en_US.properties. Kolejne zdanie to iteracja wszystkich klawiszy i wypisywanie angielskich nazw. Na koniec tworzony jest trzeci pakiet zasobów o nazwie bundle3, ale z lokalnym dla języka niemieckiego. Następnie wyszuka plik Cards_de.properties, ale taki plik nie istnieje i odczyta plik Cards.properties, który jest plikiem właściwości podstawowych. Dlatego ostatnie zdanie zapisuje tekst Ruder Zestawy ResourceBundles oparte na właściwościach mają tę wielką zaletę, że można je utrzymywać poza programem, dzięki czemu są elastyczne dla wersji językowych programów. Należy jednak zauważyć, że kluczy nie można zmienić, ponieważ są one wywoływane z programu. Kolejne pytanie dotyczy tego, gdzie powinny znajdować się pliki właściwości, które należy po prostu umieścić w katalogu projektu w katalogu src (patrz poniżej). Jeśli program zostanie zainstalowany w innym miejscu (jak ma to miejsce w praktyce), właściwości należy skopiować razem z plikiem jar i umieścić w tej samej lokalizacji.

173 9.2 WARTOŚCI FORMATUJĄCE W kontekście internacjonalizacji programów formatowanie liczb odgrywa ważną rolę, a jeszcze ważniejsze jest formatowanie daty i godziny. Częściowo chodzi o to, jak te wartości są wyświetlane, ale także o parsowanie łańcucha w związku z wprowadzaniem danych. Data i godzina były od dawna reprezentowane przez interfejs Kalendarza, zaimplementowany przez klasę GregorianCalendar, ale teraz dodano pakiet java.time zawierający nowe typy dat i godzin. Nie chcę tutaj traktować tych typów, ale tylko wskazuję ich istnienie, a tak naprawdę istnieją pewne typy i zachęcamy do zbadania API. Aby wskazać, co to jest, aplikacja DateTimeProgram to prosta aplikacja konsoli, która pokazuje, jak sformatować i wprowadzić daty i godziny. Na przykład poniższa metoda pokazuje, jak utworzyć obiekt Instant, który jest nowym rodzajem czasu: private static void test1() Instant dt = Instant.now(); System.out.println(dt); System.out.println(String.format("%d %d", dt.getepochsecond(), dt.getnano())); W tym miejscu należy zwrócić uwagę na metodę getepochsecond (), która zwraca liczbę sekund po T00: 00: 00, podczas gdy metoda getnano () zwraca liczbę nano sekund w bieżącej sekundzie. Metoda test2 () pokazuje, jak przekonwertować między nową reprezentacją czasów a obiektami kalendarza: private static void test2() Calendar dt1 = Calendar.getInstance(); Instant dt2 = Instant.ofEpochMilli(dt1.getTimeInMillis()); Calendar dt3 = GregorianCalendar.from(ZonedDateTime.ofInstant(dt2, ZoneId.systemDefault()));

174 System.out.println(dt1); System.out.println(dt2); System.out.println(dt3); Następna metoda pokazuje, jak przekonwertować daty i godziny odpowiadające danym ustawieniom narodowym: private static void test3() print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.SHORT), DateFormat.getTimeInstance(DateFormat.SHORT)); print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.SHORT, DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US)); Locale.US), print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.MEDIUM), DateFormat.getTimeInstance(DateFormat.MEDIUM)); print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.MEDIUM, DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.US)); Locale.US), print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.LONG), DateFormat.getTimeInstance(DateFormat.LONG)); print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.LONG, Locale.US), DateFormat.getTimeInstance(DateFormat.LONG, Locale.US)); print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.FULL), DateFormat.getTimeInstance(DateFormat.FULL)); print(calendar.getinstance(), DateFormat.getDateInstance(DateFormat.FULL,Locale.US), DateFormat.getTimeInstance(DateFormat.FULL, Locale.US)); private static void print(calendar dt, DateFormat dformatter, DateFormat tformatter)

175 System.out.println(dFormatter.format(dt.getTime())); System.out.println(tFormatter.format(dt.getTime())); Wykonanie metody daje wynik: : : :49:27 27 kwietnia 2017 r 11:49:27 27 kwietnia :49:27 CEST 27 kwietnia :49:27 CEST 27. kwietnia :49:27 CEST Czwartek, 27 kwietnia 2017 r 11:49:27 CEST Wreszcie ostatnia metoda testowa pokazuje, jak używać formatera do analizowania ciągu reprezentującego datę i godzinę, ale w którym pokazałem tylko jedną z dwóch metod wprowadzania: private static void test4() System.out.println(enterDate(DateFormat.getDateInstance(DateFormat.MEDIUM))); System.out.println(enterDate(DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US))); System.out.println(enterTime(DateFormat.getTimeInstance(DateFormat.SHORT))); System.out.println(enterTime(DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US))); private static Date enterdate(dateformat formatter) Scanner kb = new Scanner(System.in); while (true)

176 System.out.print("Enter date: "); try return formatter.parse(kb.nextline()); catch (Exception ex) System.out.println("Illegal date "); Aby sformatować liczby, masz klasę NumberFormat. W tym kontekście ważne jest, aby klasa obsługiwała użycie obiektu Locale. Klasę ilustruje następujący program: package numberprogram; import java.text.*; import java.util.*; public class NumberProgram public static void main(string[] args) test1(); test2(); private static void test1() System.out.println(NumberFormat.getNumberInstance().format( )); System.out.println(NumberFormat.getNumberInstance(Locale.US).

177 format( )); System.out.println(NumberFormat.getCurrencyInstance().format( )); System.out.println(NumberFormat.getCurrencyInstance(Locale.US). format( )); System.out.println(NumberFormat.getPercentInstance().format( )); System.out.println(NumberFormat.getPercentInstance(Locale.US). format( )); private static void test2() System.out.println(enterNumber(NumberFormat.getNumberInstance())); System.out.println(enterNumber(NumberFormat.getNumberInstance(Locale.US))); private static Number enternumber(numberformat formatter) Scanner kb = new Scanner(System.in); while (true) System.out.print("Enter number: "); try return formatter.parse(kb.nextline()); catch (Exception ex) System.out.println("Illegal number "); eśli program zostanie wykonany, wynikiem może być: 123,456,789

178 ,789 kr 123,456, ,79 USD ,679% ,679% Wpisz numer: , ,789 Wpisz numer: ,789 ĆWICZENIE 9 Utwórz kopię programu PersonProgram. Jeśli uruchomisz program, otworzy się poniższe okno. Jest to prosty program, w którym możesz stworzyć osobę z datą urodzenia i równowagą. Osoby są zapisywane w ArrayList, która jest serializowana za każdym razem, gdy osoba jest tworzona, osoba jest zmieniana lub osoba jest usuwana. Możesz przeglądać listę za pomocą dwóch górnych przycisków. Przetestuj program i przestudiuj kod, aby mieć pewność, że kod działa. Program nie jest umiędzynarodowiony i Twoim zadaniem jest wersja językowa programu. Zacznij od utworzenia podstawowego pliku właściwości z tekstami w języku angielskim. Dla wszystkich tekstów używanych przez program musi istnieć para klucz / wartość. Zastąp wszystkie teksty w programie tekstami z ResourceBundle dla pliku właściwości. Następnie napisz inny plik właściwości, ale z tekstami w innym języku (zgodnie z twoim wyborem). W konstruktorze klasy możesz ustawić domyślne ustawienia regionalne na ten język i sprawdzić, czy program używa następnie wybranego języka. Poniżej znajduje się wersja programu, w której językiem jest duński:

179 A co z formatowaniem daty urodzenia i równowagi - czy działa zgodnie z oczekiwaniami? 10 JEDNORĘKI BANDYTA Jako ostatni przykład tej książki pokażę rozwój programu, który będzie symulował automat do gry. Celem jest oczywiście pokazanie wykorzystania niektórych pojęć przedstawionych w tej książce, ale tam, gdzie nacisk kładziony jest również na proces. Automat składa się z (typowych) trzech kół, na których umieszczane są niektóre figurki. Podczas gry na maszynie koła obracają się, a kombinacja liczb wyświetlana po zatrzymaniu kół określa, czy wygrana, a jeśli tak, to ile wygranych. Aby napisać program, który może symulować taką maszynę, a tym samym obracające się koła, potrzebuję pojęć, które zostaną omówione najpierw w następnej książce, a zatem kod można najpierw napisać po przeczytaniu następnej książki (lub odpowiednika o Javie ). Następująca wersja programu nie będzie zatem symulować obracających się kół, a jedynie pokaż liczby. Jak wspomniano, chcę skupić się na procesie. Przynajmniej na podstawie analizy i projektu, ale program jest stosunkowo duży, a podczas programowania opiszę przede wszystkim, co jest zrobione i co powinieneś szczególnie zauważyć, gdy czytasz kod, ale niewiele o tym, jak powstał, ponieważ liczba stron była inna będzie bardzo obszerny. Z tych samych powodów nie będę opisywać, jak napisać skrypt instalacyjny, ale możesz to zrobić w taki sam sposób, jak pokazano dla innych programów FORMULACJA ZADANIA Należy napisać program symulujący automat na zwykłym komputerze. Musi istnieć jakaś forma administrowania użytkownikami, a aby móc grać na tym komputerze, musisz zostać utworzony jako użytkownik z kontem i zdeponować na nim pieniądze. Musi istnieć także możliwość otrzymania kwoty z konta, jeśli są na nim pieniądze, co w praktyce oznacza, że wygrałeś. Nie powinieneś być w stanie grać z prawdziwymi pieniędzmi, ale program musi być w stanie zasymulować, że gracz wpłaca pieniądze i że gracz otrzymuje pieniądze. Maszyna musi być konfigurowalna i powinieneś mieć możliwość wyboru, czy chcesz grać na 3 czy 4 kołach. Jeśli chodzi o konfigurację maszyny, należy mieć możliwość wyboru, które liczby i ile liczb ma znajdować się na każdym kole, które kombinacje powinny dać wygraną i jaka powinna być wygrana, a na koniec powinna istnieć możliwość wyboru, które obrazy posługiwać się. Gracz musi mieć możliwość wyboru spośród kilku konfiguracji. Konfigurując konfigurację, upewnij się, że w konfiguracji długoterminowej zapewnia zyski właścicielowi maszyny (domu), ale jednocześnie maszyna często musi zyskać, aby mieć pewność, że maszyna oddaje tak wiele

180 z powrotem że jest ktoś, kto chce grać na maszynie. Powinna również istnieć okazja do pewnego rodzaju statystyk, w których można zobaczyć, kiedy maszyna wygrywa i jak duże są wygrane ANALIZA Poniższa specyfikacja wymagań została przygotowana na podstawie analizy sformułowania zadania, a także kontaktu z klientem w celu wyjaśnienia życzeń i wymagań. Ponadto opracowano prototyp interfejsu użytkownika aplikacji. SPECYFIKACJA WYMAGAŃ Maszyna może być w jednym z dwóch trybów 1. Tryb użytkownika 2. Tryb administratora gdzie w ostatnim trybie oprócz grania można skonfigurować urządzenie i administratora. Specyfikacja wymagań jest podzielona zgodnie z tymi dwoma wzorcami użytkowania. Tryb użytkownika W trybie użytkownika jest to prosty program z kilkoma funkcjami. Gdy użytkownik (gracz) spełnia program, gracz musi wykonać jedną z następujących czynności: 1. Zaloguj się, podając nazwę użytkownika (adres ) i hasło 2. Zarejestruj się jako użytkownik (jeśli gracz nie jest jeszcze użytkownikiem) Jeśli gracz zapomniał hasła, musi skontaktować się z administratorem, który może zmienić hasło. Jeśli gracz jest zalogowany, może zmienić hasło w dowolnym momencie. W tym ostatnim przypadku (gdy trzeba utworzyć nowego gracza) użytkownik musi o tym poinformować - adres - hasło - Nazwa - numer telefonu - karta debetowa (numer, data ważności, numer kontrolny - powinna symulować prawdziwą kartę) Następnie użytkownik może grać na maszynie, jeśli na koncie znajdują się pieniądze. Oprócz gry użytkownik może wykonywać następujące funkcje: 1. Wyloguj się 2. Wpłać pieniądze na konto 3. Wyświetl informacje o koncie i zmień je (ale nie adres ), a jednocześnie możesz uzyskać płatne pieniądze, jeśli na koncie znajdują się pieniądze

181 4. Zobacz podsumowanie zwycięskich kombinacji dla bieżącej konfiguracji i jakie są wygrane 5. Wyświetl podsumowanie konta, które pokazuje transakcje i wygrane 6. Wybierz inną konfigurację Jeśli chodzi o płatności, program musi symulować gromadzenie pieniędzy na koncie gracza, a jeśli wpłacisz pieniądze na konto, program musi symulować wpłatę na konto gracza. Wszystkie transakcje muszą być rejestrowane, aby gracz zawsze mógł zobaczyć, ile zostało wstawionych i podniesionych. Oczywiście najważniejszą funkcją jest gra, która odbywa się poprzez kliknięcie przycisku lub odpowiednika. Poszczególne koła muszą być możliwe do utrzymania w podobny sposób: 1. Nie możesz trzymać wszystkich kół 2. Nie powinno być możliwe trzymanie koła, jeśli było trzymane w poprzedniej grze 3. Nie możesz trzymać koła, jeśli w poprzedniej grze była wygrana Podczas grania na maszynie należy zarejestrować wynik gry, w którym należy zarejestrować następujące informacje: 1. użytkownik, który grał 2. czas na zabawę 3. konfiguracja do gry 4. wynik jako kombinacja kół 5. wygrana (0, jeśli nie ma zysku) Tryb administratora Jeśli zalogujesz się jako administrator, oprócz gry na komputerze, możesz go skonfigurować i zarządzać kontami użytkowników. Jeśli chodzi o konfigurację maszyny, musisz być w stanie zachować określone konfiguracje, w których konfiguracja wskazuje: 1. Nazwa, aby gracze mogli wybrać konfigurację 2. Liczba kół (3 lub 4) 3. Które liczby mają być stosowane na kołach (zwykle w sąsiedztwie 10) 4. Pozycja cyfr (kolejności) na kołach - ta sama figura może pojawić się kilka razy 5. Które kombinacje powinny dać wygraną i kwoty wygranych 6. Ile kosztuje gra na maszynie 7. Jeśli konfiguracja jest aktywna i może być wybrana przez graczy Jeśli chodzi o zarządzanie kontami graczy, powinieneś być w stanie: 1. Zmień hasło gracza (na przykład, jeśli zostało zapomniane) 2. Usuń gracza, którego saldo gracza zostało przeniesione na konto gracza

182 Na koniec administrator powinien mieć możliwość wydrukowania statystyki, która pokazuje, jak zachowuje się każda konfiguracja. Tutaj możesz zobaczyć między innymi 1. Czy konfiguracja daje pozytywne wyniki, czy nie 2. Rozkład pojedynczych wygranych w czasie PROPTOTYP Opracowano prototyp ilustrujący interfejs użytkownika i sposób, w jaki będzie on odtwarzany na komputerze. Po kliknięciu przycisku gry prototyp wybiera losowe figurki (dostępnych jest 15 figurek), ale figurki są zmieniane na pewien czas, aby zasymulować, że koła się obracają. Pasek narzędzi w górnej części okna to funkcje dostępne dla odtwarzacza, przy czym ikona po prawej stronie służy jako login do administratora. Prototyp nazywa się SlotMashine0.

183 10.3 PROJEKT Informacje o graczach i konfiguracjach muszą być gdzieś zapisane, a ja skorzystam z bazy danych. Zacznę od projektu tej bazy danych, a projekt jest opisany na poniższym diagramie ER, na którym znajdują się następujące podmioty: 1. Konfiguracja reprezentująca konkretną konfigurację maszyny 2. Koło, które reprezentuje koło dla konfiguracji 3. Rysunek, czyli rysunek na kole 4. Zwycięska har reprezentuje zysk z kwotą dla danej kombinacji liczb 5. Gracz, który reprezentuje gracza 6. Gra reprezentująca grę na maszynie dla gracza 7. Transakcja, czyli kwota, którą gracz włożył lub podniósł na konto Atrybut między Config i Wheel wskazuje pozycję koła, i w ten sam sposób atrybut między Wheel a Figure wskazuje pozycję figury na kole. Po dodaniu atrybutów do diagramu koncepcyjny projekt bazy danych można zilustrować w następujący sposób:

184 Słownik danych: Gracz id INT autonumeracja klucza podstawowego emal VARCHAR (100) musi być unikalny hasło Hasło VARCHAR (200) nazwa VARCHAR (50) nazwa gracza, NIE NULL telefon Numer telefonu graczy VARCHAR (20) karta VARCHAR (20) liczba graczy, NIE NULL data DATA data ważności karty, NIE NULL kod INT kod sterujący karty, NIE NULL saldo DECIMAL Transakcja id INT klucz podstawowy autonumeracji kwota INT kwota transakcji, NIE NULL wpisz BOOLEAN true = zdeponowane, false = zapłacona kwota od Config

185 id INT autonumner klucz podstawowy name VARCHAR (50) nazwa konfiguracji, NOT NULL kwota INT cena do gry z tą konfiguracją, NIE NULL rozmiar INT liczba kół, musi wynosić 3 lub 4 Postać id INT autonumeracja klucza podstawowego nazwa VARCHAR (50) nazwa figury, NIE NULL obraz BINARY postać, NIE NULL Koło id INT autonumeracja klucza podstawowego Zwycięski id INT autonumeracja klucza podstawowego wygrana INT, może wynosić 0 Gra id INT autonumeracja klucza podstawowego czas DATETIME data i godzina wygranej Powyższy model ER można zmapować na model relacyjny, jak pokazano poniżej. Oprócz nazw istnieje tylko kilka zmian. Jeśli chodzi o transakcje, typ został usunięty i zdecydowano, że transakcja dodatnia oznacza, że została wstawiona na konto, a transakcja ujemna oznacza, że konto zostało utworzone. Powyższy model ER można zmapować na model relacyjny, jak pokazano poniżej. Oprócz nazw istnieje tylko kilka zmian. Jeśli chodzi o transakcje, typ został usunięty i zdecydowano, że transakcja dodatnia oznacza, że została wstawiona na konto, a transakcja ujemna oznacza, że konto zostało utworzone.

186 W ramach projektu utworzono projekt NetBean jako kopię projektu na podstawie analizy definiującej projekt warstwy modelu. Istnieje również program (projekt CreateDefault), który tworzy konfigurację domyślną. Celem tego programu jest przygotowanie konfiguracji przed rozpoczęciem programowania. W odniesieniu do rysunków (zdjęć) kół zdecydowano, że maszyna musi wykorzystywać kwadratowe obrazy w małej rozdzielczości, aby nie wypełniały zbyt wiele. Po wykonaniu tych kroków do bazy danych dodano dwie dodatkowe zmiany: 1. Rysunek tabeli jest rozszerzany o serię kolumn. Kolumna zawiera krótki tekst i służy do kategoryzacji liczb. Powyższy program przesłał 16 cyfr do bazy danych, która znajduje się w kategorii Domyślne. 2. Baza danych jest rozszerzana o prostego administratora tabeli z dwiema kolumnami. Tabela musi zawierać hasło i adres administratora PROGRAMOWANIE Jeśli wykonałeś dobrą i staranną analizę i ewentualnie w połączeniu z jednym lub więcej prototypami, powinieneś mieć wszystkie wymagania na początku fazy programowania. Jest to przynajmniej teoria i, w praktyce, sprawdzi się również w przypadku mniejszych projektów. Trzeba jednak również przyznać, że bez względu na to, jak ostrożny byłeś, w trakcie programowania mogą wystąpić zmiany w specyfikacji, w tym życzenia czegoś, co powinno działać w inny sposób, a może nawet nowe życzenia. W rzeczywistości nie jest to dziwne, a podczas opracowywania programu zrozumienie użycia aplikacji, a także możliwość rozpoznania nowych funkcji, które powinien mieć program, lub czegoś, co jest uzgodnione w specyfikacji, które można korzystnie modyfikować wszystkie może powodować zmiany. Oczywiście, im większy program, tym większe są szanse, że podczas programowania będzie chęć zmiany specyfikacji wymagań. Jeśli tak się stanie - lub kiedy to nastąpi - należy skontaktować się z

187 klientem i przedstawić zmiany, w tym zmiany będzie oznaczać pod względem wykorzystania zasobów i horyzontu czasowego, w którym program może zostać ukończony. Następnie musi być zgoda co do tego, czy nowe życzenia są uwzględnione w projekcie, czy też nie, lub może być konieczne ich przełożenie na później. W praktyce ważne jest, aby takie życzenia się spełniły, ale ważne jest również, aby jako programista nie tylko wdrażał rzeczy, które nie są częścią specyfikacji wymagań, ponieważ ostatecznie to klient decyduje (i płaci) co powinno być częścią gotowego programu. Wniosek jest taki, że nawet po dokładnej dogłębnej analizie, istnieje możliwość, że podczas programowania będzie chęć zmiany specyfikacji wymagań, życzenia, które można wyjaśnić klientowi. To samo dotyczy projektu, a tutaj jest bardziej reguła niż wyjątek. Wynika to między innymi z tego, że granica między projektowaniem a programowaniem nie jest ostra. Tak więc prawie zawsze będą zmiany w projekcie i zwykle zmiany, które musi wprowadzić programista bez zgody klienta. Typowym działaniem projektowym jest projektowanie baz danych, a zmiany prawie zawsze będą występować podczas fazy programowania. Nie oznacza to, że projekt bazy danych nie jest ważny na etapie projektowania, ponieważ programowanie zwykle wymaga jedynie drobnych korekt, ale prawie na pewno również tam będą. Dotyczy to również bieżącego programu, w którym do niektórych tabel dodano niektóre kolumny, a także zmiany w kluczach obcych. Warto zwrócić uwagę na projekt bazy danych na etapie projektowania, aby uniknąć tego rodzaju zmian podczas programowania, ale odwrotnie, w przypadku bazy danych o niewielkich rozmiarach trudno jest uniknąć jakichkolwiek korekt podczas programowania. Gdy takie zmiany są problematyczne, dzieje się tak dlatego, że czasami oznaczają, że zawartość danych bazy danych musi zostać odtworzona, co może być czasochłonne. W tym przypadku niektóre zmiany spowodowały, że konieczna była również zmiana programu inicjującego CreateDefault i konieczne było ponowne uruchomienie programu. Kolejnym działaniem projektowym jest projektowanie warstwy modelu i uwzględnianie najważniejszych klas modeli. Rzadko wiąże się to z głównymi wyzwaniami, ale musisz być przygotowany na zmiany w tych klasach modeli i tworzenie nowych. To nie jest problem i po prostu stwierdza, że podczas projektowania nie jesteś na tym samym poziomie szczegółowości, co podczas programowania, i to nie jest pomysł, ponieważ projekt powinien koncentrować się wyłącznie na ogólnych koncepcjach. Jak wspomniano, granica między projektowaniem a programowaniem nie jest wyraźna, co może spowodować, że znaczące decyzje zostaną przełożone na fazę programowania. Ogólnie rzecz biorąc, należy zachować ostrożność i istnieje tendencja lub ryzyko związane z projektem, aby nie znajdować wyzwań i problemów, które należy rozwiązać. Wskazuje to, że podczas projektowania nie wykorzystuje się wystarczającej ilości czasu, tym samym zaniedbując lub nie doceniając problemów, które mogą wystąpić podczas programowania. Trudno wprowadzić właściwe rozróżnienie między projektowaniem a programowaniem, ale odwrotnie, tuż pod pracą projektową z dużymi liniami, a nie pisaniem programu, ale w rezultacie, jeśli znaczące decyzje zostaną odłożone na programowanie, istnieje duże ryzyko, że części napisanego kodu muszą zostać zmienione lub, w najgorszym wypadku, przepisane. Dlatego ważne jest, aby wszystkie główne wyzwania zostały rozwiązane przed napisaniem programu. W związku z opracowaniem tego programu istnieją przykłady zadań, które powinny być lepiej potraktowane podczas projektowania, i na przykład chciałbym wspomnieć o dwóch. Jest to część wymagań, że administrator musi być w stanie przetestować poszczególne konfiguracje pod kątem tego, jak zarabiają i ile dom zarabia na konfiguracji. Jak to się stanie, nie jest traktowane ani w analizie, ani w projekcie. To, że nie zostało przetworzone podczas analizy, wskazuje, że pozwala programistom na znalezienie rozwiązania, które nie jest niczym niezwykłym, dlatego też w projekcie powinien znajdować się szkic rozwiązania. Na przykład należy wziąć pod uwagę, czy baza danych zawiera dane potrzebne do rozsądnej analizy danej konfiguracji. Innym problemem jest to, co powinno się stać, jeśli zmienisz lub usuniesz konfigurację. W ten sposób dane zarejestrowane dla gier z konfiguracją niekoniecznie są dłużej aktualne, co oznacza, że informacje o korzystaniu z urządzenia przez danego gracza nie są już ważne. Dane, o których mowa, można nie

188 tylko usunąć, ale także usunąć, ile gracz wygrał lub przegrał. Jest to również przykład problemu, który powinien zostać rozwiązany podczas projektowania. AKTUALNY PROGRAM W przypadku nieco większego programu faza programowania powinna być podzielona na wiele iteracji, gdzie iteracja jest zdefiniowana jako część programu, który można opracowywać i testować niezależnie, co wcześniej zilustrowałem. Dotyczy to również obecnego programu, ale tym razem nie chcę przeglądać poszczególnych iteracji, ale skupiam się tylko na końcowym wyniku, wskazując, co zostało zrobione i gdzie były największe wyzwania. Chcę korzystać z biblioteki PaLib iw zakresie, w jakim nie zawiera wymaganych metod bibliotecznych, należy ją zaktualizować. Wynikiem tego zadania jest zarówno bieżący program, jak i zaktualizowana biblioteka. Program ma klasyczną architekturę MVC: Warstwa dal ma tylko jedną klasę o nazwie DB. Klasa jest napisana jako singleton i ma wszystkie metody utrzymujące bazę danych. Klasa jest obszerna, ale nie zawiera niczego nowego. Warstwa modelu ma klasy z projektu. Wszystkie są modyfikowane tam, gdzie metody są implementowane, a także dodano kilka nowych klas, ale ogólnie istnieje kilka prostych rozszerzeń. W szczególności istnieje repozytorium klas, które jest dość cienką klasą adaptera do warstwy dal. Zasadniczo klasa nie ma znaczenia, ponieważ przekazuje wywołanie metod do klasy DB, a jej jedynym celem jest to, aby warstwa dal nie była znana w warstwach widoku i kontrolera. Bardzo duża część kodu znajduje się w warstwie widoku, która definiuje nie mniej niż 33 typy (klasy i interfejsy). Liczba przesadza, ponieważ kilka klas jest klasami modelowymi dla komponentów JTable, a także klas CellRenderer, a wiele innych klas dotyczy prostych okien dialogowych. Odpowiada to temu, że istnieją prawie dwa okna, które nie są prostymi oknami dialogowymi, a warstwa kontrolna ma tylko 7 klas. Po otwarciu programu pojawi się następujące okno:

189 Jeśli klikniesz Zamknij w oknie dialogowym Logowanie lub klikniesz krzyż w prawym górnym rogu, program zakończy się, więc nie będziesz mógł grać, zanim pojawi się gracz. Jeśli nie jesteś graczem, możesz kliknąć Nowy gracz, a pojawi się okno dialogowe, w którym możesz zostać utworzony jako gracz:

190 W przypadku kart płatniczych jest to pseudo, ale numer karty musi składać się z 16 cyfr, przy czym spacja może być wstawiona między 4-cyfrowymi grupami. Kod kontrolny musi składać się z 3 znaków, ale tak samo jak numer karty, nic nie jest używane. Wreszcie jest data wygaśnięcia, którą program sprawdza względem bieżącej daty podczas gry na komputerze. Adres musi mieć prawidłowy format adresu i musi być unikalny, ponieważ jest używany do identyfikacji użytkownika. Hasło musi mieć co najmniej 10 znaków. Jeśli utworzysz użytkownika z prawidłowymi danymi, okno dialogowe zostanie zamknięte, a okno dialogowe okna logowania również się zamknie i będziesz gotowy do gry. Dwa okna dialogowe logowania i tworzenia użytkownika, oba mają dołączony kontroler, ale poza tym są proste. Jeśli chodzi o okno dialogowe tworzenia gracza, to to samo okno dialogowe otwiera się z głównego paska narzędzi, jeśli gracz chce utrzymywać konto. Aby zaimplementować samą grę (a tym samym klasę MainView, która jest jednym z dwóch złożonych okien w programie), napisano klasę WheelComponent, która jest klasą widoku reprezentującą koło, a MainView używa trzech instancji lub czterech z tego składnik. To ten element rozpoczyna wątek, który będzie symulował obracanie się kół. Funkcja gry przypomina prototyp i wprowadzono tylko niewielkie zmiany, które dotyczą głównie obrotu kół. Funkcja gry musi także zapewniać prawidłowe wdrożenie logiki przycisków wstrzymania, aby sprawdzić, czy wygrana została wygrana i czy saldo gracza zostało zaktualizowane. Ponadto funkcja musi zapewniać, że nie można grać, jeśli konto gracza jest puste. Funkcje te są zaimplementowane w klasie MainModel, a baza danych jest aktualizowana dla każdej gry, ponieważ wymagane jest, aby wszystkie gry były zapisywane w dzienniku (uruchamianie tabeli). W porównaniu do projektu bazy danych, tabela ta jest rozszerzona o dwie kolumny: Ilość wyniku gry oraz identyfikator konfiguracji, z którą grał. Ma to na celu uproszczenie niektórych innych funkcji. Następnie są funkcje na pasku narzędzi. Pierwszą funkcją jest wylogowanie, a jeśli ją klikniesz, ponownie pojawi się okno logowania. Następna funkcja służy do wpłacania kwot na konto, a kliknięcie przycisku daje proste okno dialogowe, w którym podajesz kwotę i hasło. Ostatnim jest zapewnienie, że inni nie dokonają nagle wpłaty na konto, a tym samym nie podniosą kwoty zarejestrowanej karty płatniczej. Trzecia funkcja paska narzędzi jest używana przez odtwarzacz do przechowywania danych

191 użytkownika i, jak wspomniano, otwiera okno dialogowe podobne do powyższego, aby utworzyć użytkownika. Gracz nie może zmienić swojego adresu , ponieważ jest on postrzegany jako klucz. Pola hasła są puste i nie należy wprowadzać starego hasła. Te pola należy wypełnić tylko wtedy, gdy chcesz zmienić hasło. Czwarte funkcje otwierają proste okno dialogowe, które pokazuje przegląd kombinacji, które dają wygraną: a zawartość jest niczym innym jak JTable, która pokazuje zwycięskie możliwości. Kolejne ostatnie narzędzie na pasku narzędzi służy do wyświetlania przeglądu wyników bieżącego gracza. Jest to okno dialogowe z tabelą JTable, w którym możesz zobaczyć transakcje gracza na koncie oraz informacje o tym, jakie gry były. Zasadniczo jest to również proste okno dialogowe, ale nieco się komplikuje z powodu filtrów, dla których wyświetlane są transakcje. Wreszcie na pasku narzędzi znajduje się ostatnie narzędzie, w którym możesz wybrać inną konfigurację. Programowo jest to tylko JTable z przeglądem aktywnych konfiguracji. Konfiguracja jest wybierana poprzez dwukrotne kliknięcie nazwy konfiguracji. Powyżej opisano, w jaki sposób maszyna działa dla odtwarzacza i, czysto programowo, praca jest w klasie DB w celu implementacji funkcji bazy danych, klasy MainModel, która będzie śledzić cała logika gry na maszynie, a na końcu klasa MainView, która zawiera wszystkie elementy wizualne. Ta klasa jest skomplikowana przez wybór 3 i 4 kół, co oznacza, że elementy i układ okna zależą od tego wyboru, to samo dotyczy wyżej wspomnianego dialogu z kombinacjami wygranych. Następnie jest część administracyjna. Jeśli klikniesz ikonę po prawej stronie paska narzędzi, pojawi się następujące okno dialogowe:

192 Może być tylko jeden administrator. Musisz wprowadzić hasło administratora (które musi również mieć co najmniej 10 znaków), a jeśli administrator nie zostanie utworzony, administrator zostanie utworzony, a następnie możesz zalogować się jako administrator za pomocą odpowiedniego hasła (ponieważ administrator może się zmienić). Należy pamiętać, że utrzymanie informacji administratora do praktycznego wykorzystania jest zbyt proste, a rozwiązaniem może być zarejestrowanie konkretnego użytkownika z uprawnieniami administratora - problem, do którego powrócę w następnej książce. Po zalogowaniu się jako administrator pasek narzędzi zostaje rozszerzony o 4 nowe ikony: tóry służy do przesyłania obrazów do bazy danych, utrzymania użytkowników, utrzymania konfiguracji i zmiany hasła administratora. Ostatnia funkcja jest prosta i nie powinna być tutaj dalej omawiana. Funkcja konserwacji użytkownika otwiera okno dialogowe z JTabel, które pokazuje wszystkich użytkowników. Po dwukrotnym kliknięciu użytkownika w tabeli pojawi się następujące okno dialogowe: gdzie administrator może zmienić hasło użytkownika (a tym samym przypisać mu nowe hasło) i ewentualnie usunąć użytkownika. Następnie jest funkcja przesyłania zdjęć do bazy danych. Po wybraniu funkcji pojawi się następujące okno dialogowe:

193 Okno dialogowe pokazuje wszystkie liczby w bazie danych, ale możesz ustawić filtr dla serii. Kiedy przesyłasz figurę, najpierw wpisujesz nazwę serii, a następnie przeglądasz system plików w poszukiwaniu obrazu. Program używa nazwy pliku jako nazwy rysunku, ale możesz edytować tabelę i zmienić zarówno nazwę, jak i serię. Jeśli chcesz usunąć figurę, możesz kliknąć dwukrotnie obraz. Jeśli na pasku narzędzi kliknij ikonę, aby zachować konfiguracje, pojawi się następujące okno dialogowe:

194 pokazuje tabelę ze wszystkimi konfiguracjami. Tutaj możesz aktywować i dezaktywować konfiguracje. Po kliknięciu opcji Wynik otworzy się okno, które pokazuje wynik użycia tej konfiguracji i ile zwróconych konfiguracji. Nie widzę tu okna. Jeśli klikniesz Utwórz, możesz utworzyć nową konfigurację: gdzie musisz wprowadzić nazwę konfiguracji, obrazy serii i, których chcesz użyć, oraz liczbę kół. Tych danych nie można później zmienić. Po kliknięciu przycisku OK pojawi się następujące okno, które jest najbardziej złożonym oknem dialogowym w programie (to samo okno zostanie wyświetlone po dwukrotnym kliknięciu konfiguracji w oknie dialogowym Konfiguracje):

195 Po lewej stronie znajduje się JTable ze wszystkimi kształtami w bieżącej serii. Pośrodku znajduje się stół JTable dla każdego koła, a po prawej stronie stół JTable ze wszystkimi zwycięskimi kombinacjami. Dodajesz liczby do kół i zwycięskiego stołu, przeciągając je myszką z lewego stołu. Jeśli chcesz usunąć figurę, kliknij ją dwukrotnie. Aby dodać nowy wiersz do zwycięskiej tabeli, po prostu upuść cyfrę do tabeli, a aby dodać nowy wiersz do wiersza, upuść cyfrę w nagłówku wiersza. W tabeli wygranej kolumnę z wartością można edytować, dzięki czemu można wprowadzić kwotę. W dolnej części znajduje się pole wprowadzania, za ile gra się na maszynie, a także następujące przyciski: 1. Anuluj cofnij zmiany. 2. Zapisz, aby zapisać zmiany. Spowoduje to usunięcie wszystkich danych historycznych dla tej konfiguracji, czyli wszystkich wierszy w tabeli uruchomionych w odniesieniu do tej konfiguracji. Kwoty są liczone dla każdego gracza i dodawane jako transakcja do konta gracza. 3. Usuń usuwa konfigurację, a tutaj to samo, co powyżej, gdzie bieżące wiersze w tabeli są usuwane i zliczane razem i wstawiane jako transakcje na kontach graczy. 4. Sortuj sortuje tabelę wzmocnienia.

Java: otwórz okienko. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Java: otwórz okienko. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak Java: otwórz okienko Programowanie w językach wysokiego poziomu mgr inż. Anna Wawszczak PLAN WYKŁADU klasy wewnętrzne, lokalne i anonimowe biblioteka AWT zestaw Swing JFrame JPanel komponenty obsługa zdarzeń

Bardziej szczegółowo

Programowanie zdarzeniowe

Programowanie zdarzeniowe Programowanie zdarzeniowe I. Podstawy obsługi zdarzeń Małgorzata Prolejko ZDA JA16Z03 Plan Pojęcie zdarzenia Klasy i obiekty słuchaczy Rejestracja słuchaczy Obsługa naciśnięcia przycisku Rozpoznawanie

Bardziej szczegółowo

Materiał pomocniczy do kursu Podstawy programowania Autor: Grzegorz Góralski ggoralski.com

Materiał pomocniczy do kursu Podstawy programowania Autor: Grzegorz Góralski ggoralski.com Materiał pomocniczy do kursu Podstawy programowania Autor: Grzegorz Góralski ggoralski.com GUI-Swing Wstęp do tworzenia prostych aplikacji z interfejsem graficznym (GUI) przy pomocy Swing, rysowanie prostych

Bardziej szczegółowo

Tworzenie elementów graficznych

Tworzenie elementów graficznych Tworzenie elementów graficznych Elementy graficzne w Javie pozwalające tworzyć Graficzny Interfejs Użytkownika (GUI) możemy podzielić na dwie grupy: AWT (Abstract Window Toolkit) bibliotek klas służąca

Bardziej szczegółowo

Graphic User Interfaces pakiet Swing

Graphic User Interfaces pakiet Swing Graphic User Interfaces pakiet Swing Streszczenie Celem wykładu jest zaprezentowanie podstaw tworzenia graficznych interfejsów przy użyciu pakietu Swing. Czas wykładu 90 minut. Można śmiało stwierdzić,

Bardziej szczegółowo

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem.

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem. WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM NetBeans Wykonał: Jacek Ventzke informatyka sem. VI 1. Uruchamiamy program NetBeans (tu wersja 6.8 ) 2. Tworzymy

Bardziej szczegółowo

Kurs programowania. Wykład 4. Wojciech Macyna. 23 marca 2016

Kurs programowania. Wykład 4. Wojciech Macyna. 23 marca 2016 Wykład 4 23 marca 2016 Graficzny interfejs użytkownika - GUI W Javie możemy skorzystać z dwóch bibliotek do tworzenia graficznych interfejsów: AWT (Abstract Windowing Toolkit) podstawowa biblioteka będaca

Bardziej szczegółowo

Podstawy Swing. Tomasz Borzyszkowski

Podstawy Swing. Tomasz Borzyszkowski Podstawy Swing Tomasz Borzyszkowski Wprowadzenie Już Java 1.0 zawierała bibliotekę AWT (Abstract Window Toolkit) służącą do oprogramowania GUI. Kolejne wersje Java również wspierały to rozwiązanie. Swing

Bardziej szczegółowo

JAVA 2: PROGRAMY Z GRAFICZNYM INTERFEJSEM UŻYTKOWNIKA 1. WSTĘP

JAVA 2: PROGRAMY Z GRAFICZNYM INTERFEJSEM UŻYTKOWNIKA 1. WSTĘP JAVA 2: PROGRAMY Z GRAFICZNYM INTERFEJSEM UŻYTKOWNIKA 1. WSTĘP Jest to wprowadzenie do pisania aplikacji GUI w języku Java przy użyciu interfejsu API Swing. Po przeczytaniu książki możesz pisać małe aplikacje

Bardziej szczegółowo

Java - interfejs graficzny

Java - interfejs graficzny Java - interfejs graficzny Pakiet Swing Pakiet Swing przygotował: pawel@kasprowski.pl Czym jest Swing? Rozszerzenie AWT (Abstract Windows Toolkit) do tworzenia GUI (Graphical User Interface) w Javie import

Bardziej szczegółowo

Kontenery i komponenty graficzne

Kontenery i komponenty graficzne JAVA Kontenery i komponenty graficzne Bibliografia: JAVA Szkoła programowania, D. Trajkowska Ćwiczenia praktyczne JAVA. Wydanie III,M. Lis Opracował: Andrzej Nowak Kontenery Aplikacja okienkowa składa

Bardziej szczegółowo

Programowanie obiektowe

Programowanie obiektowe Programowanie obiektowe Wykład 7 Marcin Młotkowski 8 kwietnia 2015 Plan wykładu Z życia programisty, część 1 1 Z życia programisty, część 1 2 3 Z życia programisty, część 2 Model View Controller MVC w

Bardziej szczegółowo

Informatyka I. Interfejs GUI wysokiego poziomu. Biblioteka Swing. Programowanie zdarzeniowe. Politechnika Warszawska Wydział Transportu 2018

Informatyka I. Interfejs GUI wysokiego poziomu. Biblioteka Swing. Programowanie zdarzeniowe. Politechnika Warszawska Wydział Transportu 2018 Informatyka I Interfejs GUI wysokiego poziomu. Biblioteka Swing. Programowanie zdarzeniowe. dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2018 Interfejs GUI wysokiego poziomu Pojęcie

Bardziej szczegółowo

Java biblioteka Swing

Java biblioteka Swing Java biblioteka Swing Podstawowe klasy Klasa JComponent Klasa JFrame Klasa JFrame Klasa bazowa dla okien Ważne właściwości: settitle ( ) setdefaultcloseoperation ( ) setsize ( ), setlocation ( ) setlayout

Bardziej szczegółowo

Programowanie w języku Java

Programowanie w języku Java Programowanie w języku Java Wykład 7: JavaBeans Programowanie komponentowe Klasy uniwersalne komponenty Wymagania: Standardowy dostęp do pól Standardowy mechanizm wywoływania metod Komponent: Zestaw własności

Bardziej szczegółowo

Współbieżność w środowisku Java

Współbieżność w środowisku Java Współbieżność w środowisku Java Wątki i ich synchronizacja Zagadnienia Tworzenie wątków Stany wątków i ich zmiana Demony Synchronizacja wątków wzajemne wykluczanie oczekiwanie na zmiennych warunkowych

Bardziej szczegółowo

Język Java część 2 (przykładowa aplikacja)

Język Java część 2 (przykładowa aplikacja) Programowanie obiektowe Język Java część 2 (przykładowa aplikacja) Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej pawel.rogalinski @ pwr.wroc.pl Java Java przykładowa

Bardziej szczegółowo

Programowanie graficznego interfejsu użytkownika. Wykład 8. Maciej Wołoszyn 10 maja 2006

Programowanie graficznego interfejsu użytkownika. Wykład 8. Maciej Wołoszyn 10 maja 2006 Programowanie graficznego interfejsu użytkownika Wykład 8 Maciej Wołoszyn mailto:woloszyn@fatcat.ftj.agh.edu.pl 10 maja 2006 Spis treści 1 JFC/Swing 1 1.1 Prosty przykład.................................

Bardziej szczegółowo

JAVA W SUPER EXPRESOWEJ PIGUŁCE

JAVA W SUPER EXPRESOWEJ PIGUŁCE JAVA W SUPER EXPRESOWEJ PIGUŁCE Obiekt Obiekty programowe to zbiór własności i zachowań (zmiennych i metod). Podobnie jak w świecie rzeczywistym obiekty posiadają swój stan i zachowanie. Komunikat Wszystkie

Bardziej szczegółowo

Programowanie graficznych interfejsów użytkownika

Programowanie graficznych interfejsów użytkownika Programowanie obiektowe Programowanie graficznych interfejsów użytkownika Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej pawel.rogalinski pwr.wroc.pl Programowanie

Bardziej szczegółowo

Programowanie Obiektowe GUI

Programowanie Obiektowe GUI Programowanie Obiektowe GUI Swing Celem ćwiczenia jest ilustracja wizualnego tworzenia graficznego interfejsu użytkownika opartego o bibliotekę Swing w środowisku NetBeans. Ponadto, ćwiczenie ma na celu

Bardziej szczegółowo

Programowanie zdarzeniowe

Programowanie zdarzeniowe Programowanie zdarzeniowe II. Biblioteka Swing Małgorzata Prolejko ZDA JA16Z03 Plan Struktura Swing Komponenty proste Ramki Kolejność warstw Zarządca układu Panele Komponenty złożone Okna dialogowe i wewnętrzne

Bardziej szczegółowo

SWING. dr Jarosław Skaruz http://jareks.ii.uph.edu.pl jaroslaw@skaruz.com

SWING. dr Jarosław Skaruz http://jareks.ii.uph.edu.pl jaroslaw@skaruz.com SWING dr Jarosław Skaruz http://jareks.ii.uph.edu.pl jaroslaw@skaruz.com O czym będzie? Przykład aplikacji z GUI Zarządcy układu Obsługa zdarzeń Komponenty GUI Wprowadzenie obiektowy paradygmat do tworzenia

Bardziej szczegółowo

LABORATORIUM 7 Cel: 1_1

LABORATORIUM 7 Cel: 1_1 LABORATORIUM 7 Cel: Budowa klas z wykorzystaniem dziedziczenia oraz implementowania interfejsów. Wprowadzenie do trybu graficznego -cd. Animacje obiektów w trybie graficznym obsługa zdarzeń od klawiatury

Bardziej szczegółowo

Architektura interfejsu użytkownika

Architektura interfejsu użytkownika Uniwersytet Jagielloński Interfejsy graficzne Wykład 3 Architektura interfejsu użytkownika Barbara Strug 2011 Hall of shame Hall of Shame Hall of Fame O czym dzisiaj Model Widok- Kontroler Hierarchia widoków

Bardziej szczegółowo

Java SE Laboratorium nr 5. Temat: Obsługa zdarzeń

Java SE Laboratorium nr 5. Temat: Obsługa zdarzeń Java SE Laboratorium nr 5 Temat: Obsługa zdarzeń 1 Obsługa zdarzeń 1. Definicja i idea Każda z aplikacji zmienia swój stan (reaguje) pod wpływem zdarzeń. Mogą to być zdarzenia generowane przez urządzenia

Bardziej szczegółowo

Język Java. Rysowanie GUI Określanie wyglądu komponentów

Język Java. Rysowanie GUI Określanie wyglądu komponentów Język Java Rysowanie GUI Określanie wyglądu komponentów Rysowanie GUI Rysowanie GUI w Swingu np. przy pierwszym wyświetleniu przy ponownym odsłonięciu przy zmianach stanu programu Kolejność rysowania -

Bardziej szczegółowo

Java niezbędnik programisty spotkanie nr 12. Graficzny interfejs użytkownika

Java niezbędnik programisty spotkanie nr 12. Graficzny interfejs użytkownika Java niezbędnik programisty spotkanie nr 12 Graficzny interfejs użytkownika 1 Graphical User Interface (GUI) Abstract Window Toolkit Swing słabo się prezentuje mało obiektowy projekt i implementacja zajęły

Bardziej szczegółowo

Interfejsy w Java. Przetwarzanie równoległe. Wątki.

Interfejsy w Java. Przetwarzanie równoległe. Wątki. Informatyka I Interfejsy w Java. Przetwarzanie równoległe. Wątki. dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2017 Interfejsy w Java Pojęcie interfejsu w programowaniu Deklaracja

Bardziej szczegółowo

Obsługa zdarzeń. Wykład 4

Obsługa zdarzeń. Wykład 4 Obsługa zdarzeń Wykład 4 Zdarzenia Zdarzenie niskiego poziomu to wciśnięcie klawisza klawiatury lub kliknięcie przycisku myszki. Większość zdarzeń jest generowana przez: mysz klawiaturę elementy interfejsu

Bardziej szczegółowo

Języki i metody programowania Java Obsługa zdarzeń - przykłady

Języki i metody programowania Java Obsługa zdarzeń - przykłady Języki i metody programowania Java Obsługa zdarzeń - przykłady wg https://docs.oracle.com/javase/tutorial/uiswing/components/ind ex.html Autor Dr inż. Zofia Kruczkiewicz Rodzaje słuchaczy zdarzeń Wydarzenia

Bardziej szczegółowo

PROJEKTOWANIE ABSTRAKCYJNEJ KLASY FIGURA PRZECHOWUJĄCEJ WSPÓLNE CECHY OBIEKTÓW GRAFICZNYCH

PROJEKTOWANIE ABSTRAKCYJNEJ KLASY FIGURA PRZECHOWUJĄCEJ WSPÓLNE CECHY OBIEKTÓW GRAFICZNYCH Animacja wielowątkowa - gra zręcznościowa. I. UTWORZENIE SZKIELETU APLIKACJI 1. Uruchom środowisko programowania NetBeans. Utwórz aplikację typu Swing tworząc projekt o nazwie Projekt10 2. Dodaj do projektu

Bardziej szczegółowo

Podstawy Języka Java

Podstawy Języka Java Podstawy Języka Java Wprowadzenie do AWT AWT Abstract Window Toolkit, biblioteka wykorzystywana do budowy graficznych interfejsów użytkownika w Javie AWT do obsługi elementów interfejsu użytkownika wykorzystuje

Bardziej szczegółowo

Programowanie obiektowe

Programowanie obiektowe Laboratorium z przedmiotu Programowanie obiektowe - zestaw 02 Cel zajęć. Celem zajęć jest zapoznanie z praktycznymi aspektami projektowania oraz implementacji klas i obiektów z wykorzystaniem dziedziczenia.

Bardziej szczegółowo

Rozdział 4 KLASY, OBIEKTY, METODY

Rozdział 4 KLASY, OBIEKTY, METODY Rozdział 4 KLASY, OBIEKTY, METODY Java jest językiem w pełni zorientowanym obiektowo. Wszystkie elementy opisujące dane, za wyjątkiem zmiennych prostych są obiektami. Sam program też jest obiektem pewnej

Bardziej szczegółowo

Programowanie zdarzeniowe

Programowanie zdarzeniowe Programowanie zdarzeniowe III. Zaawansowana obsługa zdarzeń Małgorzata Prolejko ZDA JA16Z03 Plan Hierarchia zdarzeń Typy zdarzeń niskiego poziomu Zdarzenia okna Obsługa kliknięcia myszy Adaptery Zdarzenia

Bardziej szczegółowo

Programowanie aplikacji na urządzenia mobilne

Programowanie aplikacji na urządzenia mobilne Informatyka I Programowanie aplikacji na urządzenia mobilne dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2018 JME - Java Platform Micro Edition JME platforma Java przeznaczona

Bardziej szczegółowo

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji. JAVA Java jest wszechstronnym językiem programowania, zorientowanym obiektowo, dostarczającym możliwość uruchamiania apletów oraz samodzielnych aplikacji. Java nie jest typowym kompilatorem. Źródłowy kod

Bardziej szczegółowo

Wprowadzenie do projektu QualitySpy

Wprowadzenie do projektu QualitySpy Wprowadzenie do projektu QualitySpy Na podstawie instrukcji implementacji prostej funkcjonalności. 1. Wstęp Celem tego poradnika jest wprowadzić programistę do projektu QualitySpy. Będziemy implementować

Bardziej szczegółowo

Temat: Organizacja skoroszytów i arkuszy

Temat: Organizacja skoroszytów i arkuszy Temat: Organizacja skoroszytów i arkuszy Podstawowe informacje o skoroszycie Excel jest najczęściej wykorzystywany do tworzenia skoroszytów. Skoroszyt jest zbiorem informacji, które są przechowywane w

Bardziej szczegółowo

Instrukcja obsługi Szybkiego paragonu w programie LiderSim [ProLider].

Instrukcja obsługi Szybkiego paragonu w programie LiderSim [ProLider]. Instrukcja obsługi Szybkiego paragonu w programie LiderSim [ProLider]. W wersji 6.31.0 programu LiderSim [ProLider] została wprowadzona funkcjonalność o nazwie Szybki paragon umożliwiająca łatwe wystawianie

Bardziej szczegółowo

Aplikacja wielowątkowa prosty komunikator

Aplikacja wielowątkowa prosty komunikator Aplikacja wielowątkowa prosty komunikator Klient 0 (host 1) Wątek 0 Komponent serwera Wątek pochodny 3.1 Klient 1 (host 2) Wątek 1 Komponent serwera Wątek pochodny 3.2 Host 4 Serwer Wątek 3 Klient 2 (host

Bardziej szczegółowo

JAVA. Strumienie wejścia i wyjścia. Pliki - zapis i odczyt

JAVA. Strumienie wejścia i wyjścia. Pliki - zapis i odczyt JAVA Pliki - zapis i odczyt Opracował: Andrzej Nowak Bibliografia: JAVA Szkoła programowania, D. Trajkowska Ćwiczenia praktyczne JAVA. Wydanie III,M. Lis Strumienie wejścia i wyjścia Strumienie wejścia

Bardziej szczegółowo

Tworzenie i obsługa graficznego interfejsu uŝytkownika

Tworzenie i obsługa graficznego interfejsu uŝytkownika Tworzenie i obsługa graficznego interfejsu uŝytkownika Programowanie w środowisku rozproszonym. Wykład 3. Aplety aplikacje uruchamiane w środowisku przeglądarki - przykład import java.applet.applet; import

Bardziej szczegółowo

Informatyka i Ekonometria Programowanie komputerów Ćwiczenia Tworzenie aplikacji wykorzystaniem graficznego interfejsu użytkownika - Swing.

Informatyka i Ekonometria Programowanie komputerów Ćwiczenia Tworzenie aplikacji wykorzystaniem graficznego interfejsu użytkownika - Swing. Kierunek: Informatyka i Ekonometria Przedmiot: Programowanie komputerów Forma zajęć: Ćwiczenia Temat: Tworzenie aplikacji z wykorzystaniem graficznego interfejsu użytkownika - Swing. Biblioteka SWING podstawowa

Bardziej szczegółowo

narzędzie Linia. 2. W polu koloru kliknij kolor, którego chcesz użyć. 3. Aby coś narysować, przeciągnij wskaźnikiem w obszarze rysowania.

narzędzie Linia. 2. W polu koloru kliknij kolor, którego chcesz użyć. 3. Aby coś narysować, przeciągnij wskaźnikiem w obszarze rysowania. Elementy programu Paint Aby otworzyć program Paint, należy kliknąć przycisk Start i Paint., Wszystkie programy, Akcesoria Po uruchomieniu programu Paint jest wyświetlane okno, które jest w większej części

Bardziej szczegółowo

Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science

Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science mluckner@mini.pw.edu.pl http://www.mini.pw.edu.pl/~lucknerm Wszystkie komponenty są rozmieszczane przez domyślny

Bardziej szczegółowo

II Tworzenie klasy Prostokąt dziedziczącej z klasy wątku

II Tworzenie klasy Prostokąt dziedziczącej z klasy wątku INSTRUKCJA DO ĆWICZENIA 9 Animacja z wieloma wątkami Projekt1 Utwórz aplikację Swing, która umożliwi rysowanie na panelu 10 prostokątów o tej samej podstawie i losowych wysokościach, niezależnie zmieniających

Bardziej szczegółowo

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object Java: kilka brakujących szczegółów i uniwersalna nadklasa Object Programowanie w językach wysokiego poziomu mgr inż. Anna Wawszczak PLAN WYKŁADU Konstrukcja obiektów Niszczenie obiektów i zwalnianie zasobów

Bardziej szczegółowo

Adobe InDesign lab.1 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Podstawy pracy z aplikacją Układ strony... 2.

Adobe InDesign lab.1 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Podstawy pracy z aplikacją Układ strony... 2. Spis treści: 1 Podstawy pracy z aplikacją... 2 1.1 Układ strony... 2 strona 1 z 7 1 Podstawy pracy z aplikacją InDesign jest następcą starzejącego się PageMakera. Pod wieloma względami jest do niego bardzo

Bardziej szczegółowo

PHP 5 język obiektowy

PHP 5 język obiektowy PHP 5 język obiektowy Wprowadzenie Klasa w PHP jest traktowana jak zbiór, rodzaj różnych typów danych. Stanowi przepis jak stworzyć konkretne obiekty (instancje klasy), jest definicją obiektów. Klasa reprezentuje

Bardziej szczegółowo

Klasy abstrakcyjne. Klasę abstrakcyjną tworzymy przy pomocy modyfikatora abstract

Klasy abstrakcyjne. Klasę abstrakcyjną tworzymy przy pomocy modyfikatora abstract Klasy abstrakcyjne Klasa abstrakcyjna jest to klasa której obiekty nie mogą być tworzone, może być natomiast dziedziczona. Może posiadać konstruktor, może on być jednak wywołany tylko przez klasy pochodne.

Bardziej szczegółowo

Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science

Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science Marcin Luckner Warsaw University of Technology Faculty of Mathematics and Information Science mluckner@mini.pw.edu.pl http://www.mini.pw.edu.pl/~lucknerm Abstract Window Toolkit Przekazuje tworzenie i

Bardziej szczegółowo

Programowanie w JAVA Lab. 5 - Wątki. 1. Wykorzystując metodę Monte Carlo narysować wykres funkcji oraz obliczyć całkę: 7 x ) xy, 8,8

Programowanie w JAVA Lab. 5 - Wątki. 1. Wykorzystując metodę Monte Carlo narysować wykres funkcji oraz obliczyć całkę: 7 x ) xy, 8,8 Programowanie w JAVA Lab. 5 - Wątki 1. Wykorzystując metodę Monte Carlo narysować wykres funkcji oraz obliczyć całkę: 33 y 3 2 2 x x 3 y 7 x 3 33 7) 2 2 f x, y 1 x 3 1 x 2 1 y 7 x 3 3 33 2 112 y 3 7 x

Bardziej szczegółowo

Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt

Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt Zadanie: Utwórz szablon rysunkowy składający się z: - warstw - tabelki rysunkowej w postaci bloku (według wzoru poniżej)

Bardziej szczegółowo

Laboratorium 8 ( Android -pierwsza aplikacja)

Laboratorium 8 ( Android -pierwsza aplikacja) Dr Mirosław Łątka Informatyka dla medycyny Jesień 2012 Laboratorium 8 ( Android -pierwsza aplikacja) Naszym celem jest stworzenie aplikacji, która wyświetla zdjęcie Alberta Einsteina. Jeden z przycisków

Bardziej szczegółowo

Visual Studio instalacja

Visual Studio instalacja Visual Studio 2017 - instalacja Do tej pory napisaliśmy wiele programów, z czego niemal wszystkie były aplikacjami konsolowymi. Najwyższy więc czas zająć się tworzeniem aplikacji z graficznym interfejsem

Bardziej szczegółowo

Kurs programowania 2 - listy

Kurs programowania 2 - listy Kurs programowania 2 - listy Listy rozwijane (ComboBox) Listy rozwijane (rozwijalne) można tworzyć przy użyciu klasy ComboBox. W tabeli poniżej właściwości udostępniane przez tę kontrolkę. Najważniejsza

Bardziej szczegółowo

- Narzędzie Windows Forms. - Przykładowe aplikacje. Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy

- Narzędzie Windows Forms. - Przykładowe aplikacje. Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy - Narzędzie Windows Forms - Przykładowe aplikacje 1 Narzędzia Windows Form Windows Form jest narzędziem do tworzenia aplikacji dla

Bardziej szczegółowo

setdefaultcloseoperation(jframe.exit_on_close);//obsługa zamykania aplikacji setvisible(true); } //wyświetlenie okna

setdefaultcloseoperation(jframe.exit_on_close);//obsługa zamykania aplikacji setvisible(true); } //wyświetlenie okna Programowanie wizualne- pakiet Swing 1. Główny obiekt interfejsu uŝytkownika - obiekt klasy JFrame 1.1. Przykład prostej aplikacji Zdefiniowanie klasy dziedziczącej po klasie JFrame z pakietu Swing (lub

Bardziej szczegółowo

SWING c.d. przydatne narzędzia: JFileChooser, JOptionPane. drag'n drop, menu kontekstowe.

SWING c.d. przydatne narzędzia: JFileChooser, JOptionPane. drag'n drop, menu kontekstowe. SWING c.d. ZAGADNIENIA: przydatne narzędzia: JFileChooser, JOptionPane. drag'n drop, menu kontekstowe. MATERIAŁY: http://docs.oracle.com/javase/tutorial/uiswing/dnd/ http://th-www.if.uj.edu.pl/zfs/ciesla/

Bardziej szczegółowo

Grafika i komunikacja człowiek komputer Laboratorium. Część 1: Wstęp do grafiki

Grafika i komunikacja człowiek komputer Laboratorium. Część 1: Wstęp do grafiki UNIWERSYTET RZESZOWSKI KATEDRA INFORMATYKI Opracował: mgr inż. Przemysław Pardel, dr hab. Bogdan Kwolek v1.01 2010 Grafika i komunikacja człowiek komputer Laboratorium Część 1: Wstęp do grafiki ZAGADNIENIA

Bardziej szczegółowo

INSTRUKCJA DO ĆWICZENIA 5

INSTRUKCJA DO ĆWICZENIA 5 INSTRUKCJA DO ĆWICZENIA 5 Kontynuacja tworzenia aplikacje umożliwiających oszacowanie szukanych wartości przez symulację doświadczenia losowego, z ilustracją graficzną jego wyników. Zadanie wykonamy dla

Bardziej szczegółowo

Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit)

Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit) Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit) Biblioteka Abstrakcyjnych Narzędzi Okienkowych AWT (Abstract Window Toolkit) jako historycznie pierwsza w JDK

Bardziej szczegółowo

Java. Wykład 9. Piotr Tronczyk

Java. Wykład 9. Piotr Tronczyk Java Wykład 9 Piotr Tronczyk Zegar analogowy Tarcza Cyferblat Wskazówki Timer 2 Zegar analogowy Tym razem postaramy się napisać program, który wyświetlał będzie zegar analogowy. Część odpowiedzialna za

Bardziej szczegółowo

Dokumentacja do API Javy.

Dokumentacja do API Javy. Dokumentacja do API Javy http://java.sun.com/j2se/1.5.0/docs/api/ Klasy i obiekty Klasa jest to struktura zawierająca dane (pola), oraz funkcje operujące na tych danych (metody). Klasa jest rodzajem szablonu

Bardziej szczegółowo

Dodanie nowej formy do projektu polega na:

Dodanie nowej formy do projektu polega na: 7 Tworzenie formy Forma jest podstawowym elementem dla tworzenia interfejsu użytkownika aplikacji systemu Windows. Umożliwia uruchomienie aplikacji, oraz komunikację z użytkownikiem aplikacji. W trakcie

Bardziej szczegółowo

Obsługa grafiki w Delphi, rysowanie na płótnie, obsługa myszki, zapisywanie obrazków do plików, bitmapy pozaekranowe.

Obsługa grafiki w Delphi, rysowanie na płótnie, obsługa myszki, zapisywanie obrazków do plików, bitmapy pozaekranowe. Programowanie Wizualno-Obiektowe (studia zaoczne - inżynieria komputerowa) Zajęcia z Delphi 5, program 1 Temat: Zadanie: Obsługa grafiki w Delphi, rysowanie na płótnie, obsługa myszki, zapisywanie obrazków

Bardziej szczegółowo

Enkapsulacja, dziedziczenie, polimorfizm

Enkapsulacja, dziedziczenie, polimorfizm 17 grudnia 2008 Spis treści I Enkapsulacja 1 Enkapsulacja 2 Spis treści II Enkapsulacja 3 Czym jest interfejs Jak definuje się interfejs? Rozszerzanie interfejsu Implementacja interfejsu Częściowa implementacja

Bardziej szczegółowo

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu. Zrozumienie funkcji danych statycznych jest podstawą programowania obiektowego. W niniejszym artykule opiszę zasadę tworzenia klas statycznych w C#. Oprócz tego dowiesz się czym są statyczne pola i metody

Bardziej szczegółowo

Grafika i komunikacja człowiek komputer Laboratorium. Część 2: Graphics

Grafika i komunikacja człowiek komputer Laboratorium. Część 2: Graphics UNIWERSYTET RZESZOWSKI KATEDRA INFORMATYKI Opracował: mgr inż. Przemysław Pardel, dr hab. Bogdan Kwolek v1.01 2010 Grafika i komunikacja człowiek komputer Laboratorium Część 2: Graphics ZAGADNIENIA DO

Bardziej szczegółowo

Informatyka Arkusz kalkulacyjny Excel 2010 dla WINDOWS cz. 1

Informatyka Arkusz kalkulacyjny Excel 2010 dla WINDOWS cz. 1 Wyższa Szkoła Ekologii i Zarządzania Informatyka Arkusz kalkulacyjny 2010 dla WINDOWS cz. 1 Slajd 1 Slajd 2 Ogólne informacje Arkusz kalkulacyjny podstawowe narzędzie pracy menadżera Arkusz kalkulacyjny

Bardziej szczegółowo

JAVA CZ.2 Programowanie obiektowe. poniedziałek, 20 kwietnia 2009

JAVA CZ.2 Programowanie obiektowe. poniedziałek, 20 kwietnia 2009 JAVA CZ.2 Programowanie obiektowe Przygotowanie projektu aplikacji 1. File >New Project 2. Z listy Categories z panelu Projects wybieramy Java Application. Nasz wybór zatwierdzamy przyciskiem Next. 3.

Bardziej szczegółowo

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki Informatyka I Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2017 Dziedziczenie klas

Bardziej szczegółowo

Przykładowa dostępna aplikacja w Visual Studio - krok po kroku

Przykładowa dostępna aplikacja w Visual Studio - krok po kroku Przykładowa dostępna aplikacja w Visual Studio - krok po kroku Zadaniem poniższego opisu jest pokazanie, jak stworzyć aplikację z dostępnym interfejsem. Sama aplikacja nie ma konkretnego zastosowania i

Bardziej szczegółowo

Język Java część 2 (przykładowa aplikacja)

Język Java część 2 (przykładowa aplikacja) Programowanie obiektowe Język Java część 2 (przykładowa aplikacja) Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej pawel.rogalinski @ pwr.wroc.pl Java Java przykładowa

Bardziej szczegółowo

CorelDraw - podstawowe operacje na obiektach graficznych

CorelDraw - podstawowe operacje na obiektach graficznych CorelDraw - podstawowe operacje na obiektach graficznych Przesuwanie obiektu Wymaż obszar roboczy programu CorelDraw (klawisze Ctrl+A i Delete). U góry kartki narysuj dowolnego bazgrołka po czym naciśnij

Bardziej szczegółowo

Programowanie obiektowe

Programowanie obiektowe Laboratorium z przedmiotu - zestaw 02 Cel zajęć. Celem zajęć jest zapoznanie z praktycznymi aspektami projektowania oraz implementacji klas i obiektów z wykorzystaniem dziedziczenia. Wprowadzenie teoretyczne.

Bardziej szczegółowo

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub PWSG Ćwiczenia 12 Wszystkie ukończone zadania należy wysłać na adres: sara.m.jurczyk@gmail.com lub sarajurczyk@kul.lublin.pl Zadanie 1: Różnica między zwykłymi polami/metodami, a polami/metodami static

Bardziej szczegółowo

Aplikacje w środowisku Java

Aplikacje w środowisku Java Aplikacje w środowisku Java Materiały do zajęć laboratoryjnych Klasy i obiekty - wprowadzenie mgr inż. Kamil Zieliński Katolicki Uniwersytet Lubelski Jana Pawła II 2018/2019 Klasa zbiór pól i metod Obiekt

Bardziej szczegółowo

Java Podstawy. Michał Bereta

Java Podstawy. Michał Bereta Prezentacja współfinansowana przez Unię Europejską ze środków Europejskiego Funduszu Społecznego w ramach projektu Wzmocnienie znaczenia Politechniki Krakowskiej w kształceniu przedmiotów ścisłych i propagowaniu

Bardziej szczegółowo

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018 Informatyka I Klasy i obiekty. Podstawy programowania obiektowego dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2018 Plan wykładu Pojęcie klasy Deklaracja klasy Pola i metody klasy

Bardziej szczegółowo

Skróty klawiaturowe w systemie Windows 10

Skróty klawiaturowe w systemie Windows 10 Skróty klawiaturowe w systemie Windows 10 Skróty klawiaturowe to klawisze lub kombinacje klawiszy, które zapewniają alternatywny sposób na wykonanie czynności zwykle wykonywanych za pomocą myszy. Kopiowanie,

Bardziej szczegółowo

TEMAT : KLASY DZIEDZICZENIE

TEMAT : KLASY DZIEDZICZENIE TEMAT : KLASY DZIEDZICZENIE Wprowadzenie do dziedziczenia w języku C++ Język C++ możliwa tworzenie nowej klasy (nazywanej klasą pochodną) w oparciu o pewną wcześniej zdefiniowaną klasę (nazywaną klasą

Bardziej szczegółowo

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami. UML a kod w C++ i Javie Projektowanie oprogramowania Dokumentowanie oprogramowania Diagramy przypadków użycia Przewoznik Zarzadzanie pojazdami Optymalizacja Uzytkownik Wydawanie opinii Zarzadzanie uzytkownikami

Bardziej szczegółowo

Swing ćwiczenia 2 opis

Swing ćwiczenia 2 opis Swing ćwiczenia 2 opis Zad 1. a) Dołożyliśmy nowy obszar tekstowy JTextArea i w jego właściwości Document ustawiliśmy, że ma wspólny dokument (model-treść) z naszym pierwszym JTextArea. Zauważmy, że wpisując

Bardziej szczegółowo

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Java - tablice, konstruktory, dziedziczenie i hermetyzacja Java - tablice, konstruktory, dziedziczenie i hermetyzacja Programowanie w językach wysokiego poziomu mgr inż. Anna Wawszczak PLAN WYKŁADU zmienne tablicowe konstruktory klas dziedziczenie hermetyzacja

Bardziej szczegółowo

Programowanie komponentowe

Programowanie komponentowe Programowanie komponentowe Aplety w aplikacjach typu Visual Web Java Server Faces Autor: Struktura wykładu 1. Tworzenie uniwersalnego programu graficznego 2. Program typu Java Application wykonanie programu

Bardziej szczegółowo

Tworzenie projektu zawierającego aplet w środowisku NetBeans. lab1. Dr inż. Zofia Kruczkiewicz Programowanie aplikacji internetowych

Tworzenie projektu zawierającego aplet w środowisku NetBeans. lab1. Dr inż. Zofia Kruczkiewicz Programowanie aplikacji internetowych Tworzenie projektu zawierającego aplet w środowisku NetBeans. lab1 Dr inż. Zofia Kruczkiewicz Etap 1 - Tworzenie apletu 1. Wybierz z menu File\ New Project. Na formularzu New Project wybierz w oknie Categories

Bardziej szczegółowo

PWŚG Ćwiczenia 13. Ukończoną pracę należy przesłać na adres lub

PWŚG Ćwiczenia 13. Ukończoną pracę należy przesłać na adres   lub PWŚG Ćwiczenia 13 Ukończoną pracę należy przesłać na adres email: sara.m.jurczyk@gmail.com lub sarajurczyk@kul.lublin.pl Zadanie. Stwórz aplikację stawiającą stemple w postaci figur geometrycznych: koło,

Bardziej szczegółowo

SWING ZAGADNIENIA: wprowadzenie, kontenery I komponenty, LayoutManager, komponenty tekstowe.

SWING ZAGADNIENIA: wprowadzenie, kontenery I komponenty, LayoutManager, komponenty tekstowe. SWING ZAGADNIENIA: wprowadzenie, kontenery I komponenty, LayoutManager, komponenty tekstowe. MATERIAŁY: http://docs.oracle.com/javase/tutorial/uiswing/ http://th-www.if.uj.edu.pl/zfs/ciesla/ JĘZYK JAVA,

Bardziej szczegółowo

Laboratorium z informatyki sem.iii/ćw. 4 Wydział Transportu PW /19

Laboratorium z informatyki sem.iii/ćw. 4 Wydział Transportu PW /19 INSTRUKCJA DO ĆWICZENIA 4 Utworzymy aplikacje umożliwiające oszacowanie szukanych wartości przez symulację doświadczenia losowego, z ilustracją graficzną wyników doświadczenia. Zadanie wykonamy dla przykładów

Bardziej szczegółowo

Multimedia JAVA. Historia

Multimedia JAVA. Historia Multimedia JAVA mgr inż. Piotr Odya piotrod@sound.eti.pg.gda.pl Historia 1990 rozpoczęcie prac nad nowym systemem operacyjnym w firmie SUN, do jego tworzenia postanowiono wykorzystać nowy język programowania

Bardziej szczegółowo

Informatyka II. Laboratorium Aplikacja okienkowa

Informatyka II. Laboratorium Aplikacja okienkowa Informatyka II Laboratorium Aplikacja okienkowa Założenia Program będzie obliczał obwód oraz pole trójkąta na podstawie podanych zmiennych. Użytkownik będzie poproszony o podanie długości boków trójkąta.

Bardziej szczegółowo

Kopiowanie, przenoszenie plików i folderów

Kopiowanie, przenoszenie plików i folderów Kopiowanie, przenoszenie plików i folderów Pliki i foldery znajdujące się na dysku można kopiować lub przenosić zarówno w ramach jednego dysku jak i między różnymi nośnikami (np. pendrive, karta pamięci,

Bardziej szczegółowo

Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia

Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia nowego projektu (poniżej są utworzone projekty) Po kliknięciu

Bardziej szczegółowo

Jak przesłać mapę do urządzenia lub na kartę pamięci?

Jak przesłać mapę do urządzenia lub na kartę pamięci? Jak przesłać mapę do urządzenia lub na kartę pamięci? Poniższe instrukcje opisują procedury dla programu MapSource w wersji 6.14.1. Jeśli posiadasz starszą wersję możesz dokonać aktualizacji programu pobierając

Bardziej szczegółowo

Automatyzowanie zadan przy uz yciu makr języka Visual Basic

Automatyzowanie zadan przy uz yciu makr języka Visual Basic Automatyzowanie zadan przy uz yciu makr języka Visual Basic Jeśli użytkownik nie korzystał nigdy z makr, nie powinien się zniechęcać. Makro jest po prostu zarejestrowanym zestawem naciśnięć klawiszy i

Bardziej szczegółowo