Edytor graficzny

Będzie to raczej szkic edytora graficznego. Będzie pozwalał na dodawanie linii do rysunku.

Wstęp

Utwórz nowy projekt typu Desktop Application. Nazwij go Mouse.

Utwórz nową klasę dziedziczącą po JPanel. Nazwij ją MousePanel. Dodaj metodę public void paint(Graphics g){}. Dodaj instrukcję import. Przekompiluj klasę. Umieść komponent w oknie widoku. W miarę możliwości powinien wypełniać całe okno.

Etap I -- hierarchia klas

Utworzymy fragment hierarchii klas: klasę Line dziedziczącą po klasie Shape. Obie klasy będą klasami zagnieżdżonymi w MousePanel. Obiekty hierarchii Shape będą umiały się narysować korzystając z zdefiniowanych dla nich atrybutów.

// wewnątrz MousePanel

    static class Shape{

        Color color=Color.BLACK;

        void paint(Graphics g){

        }

    }

   

    static class Line extends Shape{

        int x1,y1,x2,y2;

        Line(int _x1, int _y1,int _x2, int _y2){

            x1=_x1;y1=_y1;x2=_x2;y2=_y2;

        }

        @Override

         void paint(Graphics g){

             g.setColor(color);

             g.drawLine(x1, y1, x2, y2);

        }

    }

 

Etap II – kontener dla naszych wektorów

Wektory będą przechowywane w kontenerze Vector<Shape>.

1.      Dodajemy atrybut klasy MousePanel:

    Vector<Shape> shapes=new Vector<Shape>();

2.      Modyfikujemy metodę paint() klasy MousePanel tak, aby wyświetlić zawartość kontenera.

    @Override

    public void paint(Graphics g){

        for(int i=0;i<shapes.size();i++){

            shapes.get(i).paint(g);

        }  

    }

3.      Chcielibyśmy przetestować. W konstruktorze MousePanel dodamy klika linii…

    public MousePanel() {

        initComponents();

        shapes.add(new Line(100,50,200,75));

        shapes.add(new Line(50,75,150,175));

    }

Po uruchomieniu wyświetli się okno z dwiema liniami.

Etap III – dodajemy linie

W docelowej aplikacji, aby dodać linię powinniśmy wybrać opcję Dodaj linię i następnie zdefiniować jej położenie za pomocą myszy. Przyjmiemy najprostsze rozwiązanie. Program będzie reagował na kliknięcia:

·        Pierwsze kliknięcie będzie punktem początkowym linii.

·        Drugie kliknięcie będzie punktem końcowym.

Aby komponent reagował na zdarzenia typu kliknięcie przycisku myszy, powinien implementować interfejs MouseListener.

1.      Zmień deklarację klasy:

public class MousePanel extends javax.swing.JPanel
implements MouseListener{

}

2.      Klikając na symbol “żarówki” zaimportuj interfejs i dodaj automatycznie implementację metod mouseClicked(), mousePressed(), itd… Następnie usuń linie

//throw new UnsupportedOperationException("Not supported yet.");

3.      Zarejestruj komponent jako odbiorcę zdarzeń dodając w konstruktorze MousePanel linię:

      addMouseListener(this);

4.      Dodawanie linii jest procesem dwuetapowym, dlatego powinniśmy gdzieś przechować współrzędne pierwszego kliknięcia. Najwygodniej przyjąć następującą strategię.

a.       Zadeklarujemy atrybut typu Line o wartości początkowej null.

Line newLine;

b.      Przy pierwszym kliknięciu utworzymy obiekt typu Line i przypiszemy jego referencję zmiennej newLine. Przy drugim kliknięciu uaktualnimy współrzędne końcowe, dodamy obiekt do kontenera shapes, z powrotem ustawimy newLine na null i przerysujemy okno za pomocą  repaint().

    public void mouseClicked(MouseEvent e) {

        //throw new UnsupportedOperationException("Not supported yet.");

        System.out.println(e.getX()+" "+e.getY());

        if(newLine == null){  // (a)

            newLine = new Line(e.getX(),e.getY(),e.getX(),e.getY());

        }

        else {               // (b)

            newLine.x2=e.getX();

            newLine.y2=e.getY();

            shapes.add(newLine);

            newLine=null;

            repaint();

        }

    }

Przetestujemy. Linie powinny być prawidłowo dodawane…

Etap III – chcemy wizualizować linie w trakcie dodawania

Strategia wizualizacji wektorów w trakcie dodawania opiera się na rozwiązaniu typu gumka (ang. rubberband). Kiedy przesuwamy mysz rysujemy kształty obiektu. Wykorzystujemy w tym celu operację XOR na pikselach. Pierwsze wyświetlenie w trybie XOR rysuje obiekt. Kolejna operacja graficzna z tymi samymi parametrami przywraca oryginalny kolor pikseli.

Rysowanie powinno odbywać się w reakcji na zdarzenie generowane podczas ruchu myszy.

Podobnie, jak w poprzednim przypadku zdefiniowany jest stosowny interfejs MouseMotionListener, który komponent powinien zaimplementować. Alternatywnie można automatycznie wygenerować odpowiednią metodę…

1.      W trybie Design kliknij prawym klawisze na komponent MousePanel i wybierz: Events®MouseMotion®mouseMoved

Wygenerowana zostanie automatycznie metoda o nazwie typu formMouseMoved(). Mogliśmy to samo zrobić w poprzednim przypadku!

2.      Wpiszemy tam kod, który rysuje obiekt newLine w trybie XOR, uaktualnia współrzędne i rysuje obiekt w nowym miejscu… Oczywiście, nie rysujemy linii, jeśli newLine ma wartość null. Rysowanie nie zachodzi w reakcji na zdarzenie paint(), dlatego musimy pobrać kontekst graficzny i na końcu go zwolnić.

private void formMouseMoved(java.awt.event.MouseEvent evt) {

// TODO add your handling code here:

        if(newLine == null) return;

        Graphics g = getGraphics();

        g.setXORMode(Color.GRAY);

        newLine.paint(g);

        newLine.x2=evt.getX();

        newLine.y2=evt.getY();

        newLine.paint(g);

        g.setPaintMode();

        g.dispose();

}

 

Uwaga – należy we własnościach komponentu MousePanel usunąć przezroczystość (ang. opaque)