Z pomocą książki dalej męczę naukę Javy. Oto co teraz zostało wypłodzone:
package myMouse;
import javax.swing.*;
public class MouseTest {
public static void main(String[] args) {
MouseFrame frame = new MouseFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setVisible(true );
}
}Główna klasa programu. Tworzy obiekt MouseFrame, ustawia domyślną reakcję na zdarzenie zamknięcia i pokazuje ramkę.
Dalej plik MouseFrame.java:
package myMouse;
import javax.swing.*;
import java.awt.*;
public class MouseFrame extends JFrame{
public MouseFrame()
{
setTitle( “Test Myszki” );
setSize( WIDTH, HEIGHT );
//
MousePanel panel = new MousePanel();
Container cont = getContentPane();
cont.add( panel );
}
private static final int WIDTH = 600;
private static final int HEIGHT = 400;
}
Również bez cudów, podstawowe metody klasy JFrame w użyciu. Następnie powstaje obiekt klasy dziedziczącej po JPanel i dodany do ramki.
Następny plik MousePanel.java:
package myMouse;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.geom.*;
import java.awt.*;
public class MousePanel extends JPanel{
public MousePanel()
{
squares = new ArrayList();
current = null;
//add listeners
addMouseListener( new MouseHandler() );
addMouseMotionListener( new MouseMotionHandler() );
}
public void paintComponent( Graphics graph )
{
super.paintComponent( graph );
Graphics2D graph2D = (Graphics2D)graph;
for ( int i = 0; i < squares.size(); i++ )
graph2D.draw( (Rectangle2D)squares.get( i ) );
}
public Rectangle2D find( Point2D point )
{
for ( int i = 0; i < squares.size(); i++ )
{
Rectangle2D rect = (Rectangle2D)squares.get( i );
if ( rect.contains( point ) ) return rect;
}
return null;
}
public void add( Point2D point )
{
current = new Rectangle2D.Double( point.getX() - LENGTH/2, point.getY() - LENGTH/2, LENGTH, LENGTH );
squares.add( current );
}
public void remove( Rectangle2D rect )
{
if ( rect == null ) return;
if ( rect == current ) current = null;
squares.remove( rect );
repaint();
}
private class MouseHandler extends MouseAdapter
{
public void mousePressed( MouseEvent event )
{
current = find( event.getPoint() );
if ( current == null )
add( event.getPoint() );
repaint();
}
public void mouseClicked( MouseEvent event )
{
current = find( event.getPoint() );
if ( current != null && event.getClickCount() >= 2 )
remove( current );
repaint();
}
}
private class MouseMotionHandler implements MouseMotionListener
{
public void mouseMoved( MouseEvent event )
{
if ( find( event.getPoint()) == null )
setCursor( Cursor.getDefaultCursor() );
else
setCursor( Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR ) );
}
public void mouseDragged( MouseEvent event )
{
if ( current != null )
{
current.setFrame( event.getX() - LENGTH/2, event.getY() - LENGTH/2, LENGTH, LENGTH );
repaint();
}
}
}
private ArrayList squares;
private Rectangle2D current;
private static final int LENGTH = 10;
}
Jak widać kodu jest tu sporo więcej i wymaga on szerszego komentarza.
Konstruktor inicjalizuje zmienne oraz przypisuje do panelu dwa listenery, obydwa dotyczą zdarzeń związanych z myszką. Dlaczego dwa? Otóż zwykle interesują nas zdarzenia dotyczące kliknięć myszki i tym zajmuje się klasa, który implementuje interface MouseListener. Jednak aby śledzić ruchy myszki potrzebna jest zdecydowanie większa wydajność ponieważ są one zdecydowanie częstsze niż kliknięcia ( w sumie to występują zwykle cały czas w trakcie działania aplikacji ). Stąd powstał drugi interface MouseMotionListener. Metoda find() klasy MousePanel sprawdza czy dany punkt ( który właśnie kliknęliśmy ) należy do jakiegoś prostokąta. Jeśli tak to zwraca ten obiekt ( prostokąt ) a jeśli nie - zwraca null. Wykorzystana jest tutaj przydatna metoda Rectangle2D.contains( Point ), dzięki której nie musimy ręcznie bawić się koordynatami prostokąta i punktu. Kolejne metody to add() i remove(), które odpowiednio dodają i usuwają prostokąt. Lista prostokątów znajduje się w obiekcie typu ArrayList z pakietu java.util.*
I teraz ważna rzecz. Dwie klasy PRYWATNE, które są naszymi listenerami ( słuchaczami ). Pierwsza z nich implementuje interface MouseListener, ale można powiedzieć pośrednio. Ponieważ interface ten zawiera więcej niż jedną metodą posiada klasę adaptacyjną, w której wszystkie te metody są zaimplementowane jako puste więc dziedzicząc przeciążamy tylko interesujące nas funkcje składowe. Klasa adaptacyjna w tym wypadku to MouseAdapter. W metodzie mousePressed() sprawdzamy czy kliknięcie nie nastąpiło wewnątrz prostokąta, jeśli nie to dodajemy prostokąt, który ma środek w punkcie klikniętym przez nas myszką. Metoda mouseClicked() sprawdzą z kolei czy kliknęliśmy dwukrotnie wewnątrz prostokąta, jeśli tak to usuwamy ten prostokąt.
Druga klasa prywatna odpowiada z obsługę zdarzeń ruchu myszką. Metoda mouseMoved() sprawdza gdzie jest kursor myszy - jeśli wewnątrz prostokąta to zmienia jego wygląd na krzyżyk, jeśli poza prostokątem to ustawia standardowy wygląd. Metoda mouseDragged() jak nazwa sugeruje odpowiada za ‘przeciąganie’. Przesuwa ona obecny prostokąt razem z kursorem myszki. Wykorzystana jest tu metoda setFrame( double x, double y, double width, double height ), która zmienia położenie i wielkość obiektu typu Rectangle2D. Po przeniesieniu należy odrysować panel wywołując metodę repaint().
Nie zamieszczam screenów programu bo nic ciekawego nie widać, program bardziej edukacyjny niż pożyteczny.