Werkzeugspitzen verfeinern

Jetzt wollen wir einen JCheckBox hinzufügen die, wenn sie abgehakt ist, die Werkzeugspitzen gefüllt darstellt, und wenn nicht, eben ungefüllt.

StartFenster.java

package miniMalen1;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SpinnerNumberModel;

public class StartFenster extends JFrame {

  public static void main(String[] s) {
    new StartFenster();
  }

  private ArrayList<Grafikobjekt> virtuellerBildschirm = new ArrayList<Grafikobjekt>();
  private JPanel panel;
  private int x = -10;
  private int y = -10;
  JRadioButton kreisBtn;
  JRadioButton quadratBtn;
  JSpinner linienstaerke;
  JCheckBox fuellen;
  ButtonGroup werkzeugspitzen = new ButtonGroup();
  private JToolBar auswahl;

  public StartFenster() {
    initialisiereStartFenster();
  }

  private void initialisiereStartFenster() {
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    this.setBounds(0, 0, 500, 500);
    this.setLocationRelativeTo(null);
    this.setAlwaysOnTop(true);
    this.setBackground(Color.white);
    this.setTitle("Tafel");
    this.setLayout(new BorderLayout());

    auswahl = new JToolBar();
    auswahl.setPreferredSize(new Dimension(300, 30)); // PreferredSize = bevorzugte Größe
    auswahl.setFloatable(true);
    
    
    kreisBtn = new JRadioButton("Kreis");
    quadratBtn = new JRadioButton("Quadrat");
    werkzeugspitzen.add(kreisBtn);
    werkzeugspitzen.add(quadratBtn);
    
    fuellen = new JCheckBox("Füllen");
    auswahl.add(fuellen);
    
    auswahl.add(kreisBtn);
    auswahl.add(quadratBtn);

    SpinnerNumberModel nummern = new SpinnerNumberModel(10, 1, 99, 1); // ( default, Minimum, Maximum, Schrittweite
                                      // )
    linienstaerke = new JSpinner(nummern);
    linienstaerke.setMinimumSize(new Dimension(50, 30)); // setzt die Mindestgröße ( Breite, Höhe )
    linienstaerke.setMaximumSize(new Dimension(50, 30)); // setzt die Maximalgröße

    auswahl.add(linienstaerke);
    this.add(auswahl, BorderLayout.PAGE_START);
    
    Tafel tafel = new Tafel(this);
    this.add(tafel);

    sichtbar();
  }

  private void sichtbar() {
    this.setVisible(true);

  }
  
  public int getLinienstaerke() {
    int staerke = (int) this.linienstaerke.getValue();
    return staerke;
  }
}

Zunächst deklarieren wir dafür eine JCheckBox im Variablenfeld von StartFenster (Zeile 30).
Dann initialisieren wir die Checkbox in unserer Initialisierungsmethode (Zeile 57 und 58) und fügen sie der auswahl hinzu.

Grafikobjekt.java

package miniMalen1;

import java.awt.Point;

import javax.swing.JCheckBox;

class Grafikobjekt {

  Point koordinaten;
  int stiftdicke;
  int stifttyp;
  boolean gefuellt;

  public Grafikobjekt(Point p, int sd, int st, boolean gf) {
    this.koordinaten = p;
    this.stiftdicke = sd;
    this.stifttyp = st;
    this.gefuellt = gf;
  }

  public Point getKoordinaten() {
    return this.koordinaten;
  }

  public void setKoordinaten(Point koordinaten) {
    this.koordinaten = koordinaten;
  }

  public int getStiftdicke() {
    return this.stiftdicke;
  }
  
  public int getStifttyp() {
    return stifttyp;
  }

  public boolean isGefuellt() {
    return this.gefuellt;
  }
}

Im Grafikobjekt fügen wir eine boolean Variable gefuellt hinzu und ergänzen den Konstruktor. Ausserdem fügen wir einen entsprechenden getter hinzu.

Tafel.java

package miniMalen1;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import javax.swing.JPanel;

public class Tafel extends JPanel implements MouseListener, MouseMotionListener {

  private ArrayList<Grafikobjekt> virtuellerBildschirm = new ArrayList<Grafikobjekt>();

  private int x = -10;
  private int y = -10;
  private StartFenster auswahl;

  public Tafel(StartFenster auswahl) {
    this.auswahl = auswahl;
    this.setBackground(Color.WHITE);
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
  }

  public void paintComponent(Graphics g) {
    super.paintComponent(g);

    {
      for (int z = 0; z < virtuellerBildschirm.size(); z++) {
        Grafikobjekt go = virtuellerBildschirm.get(z);
        if(go.isGefuellt()) {
          if(go.stifttyp == 2)
          g.fillRect(go.getKoordinaten().x, go.getKoordinaten().y, go.getStiftdicke(), go.getStiftdicke());
          else
          g.fillOval(go.getKoordinaten().x, go.getKoordinaten().y, go.getStiftdicke(), go.getStiftdicke());
        } else {
          if(go.stifttyp == 2)
          g.drawRect(go.getKoordinaten().x, go.getKoordinaten().y, go.getStiftdicke(), go.getStiftdicke());
          else
          g.drawOval(go.getKoordinaten().x, go.getKoordinaten().y, go.getStiftdicke(), go.getStiftdicke());
        }
      }
    }

  }
  
  public void zeichneAufVirtuellenBildschirm(int x, int y) {
    int werkzeug;
    if ( auswahl.quadratBtn.isSelected() ) {
      werkzeug = 2;
    } else {
      werkzeug = 1;
    }
    virtuellerBildschirm.add(new Grafikobjekt(new Point(x, y), auswahl.getLinienstaerke(), werkzeug, auswahl.fuellen.isSelected() ));
    System.out.println(auswahl.fuellen.isSelected());
    this.repaint();
  }

  @Override
  public void mouseDragged(MouseEvent e) {
    System.out.println("Dragged");
    x = e.getX();
    y = e.getY();
    zeichneAufVirtuellenBildschirm(x,y);
  }

  @Override
  public void mousePressed(MouseEvent e) {
    System.out.println("Pressed");
    x = e.getX();
    y = e.getY();
    zeichneAufVirtuellenBildschirm(x,y);
  }
}

Hier übergeben wir dem virtuellen Bildschirm unser neues Grafikobjekt in dessen Konstruktoraufruf wir am Ende unseren JSpinner mit isSelected abfragen und dem entsprechend ein true oder false erhalten das weitergegeben wird (Zeile 56).
Abschließend müssen wir nur noch in der paint-Methode unsere if-Abfrage in eine weitere einbetten sowie einen else-Zweig hinzufügen der im Falle eines ungefüllten Grafikobjekts greift (Zeile 38 – 43).

Nun ist es so, das in der paint oder paintComponent-Methode möglichst wenig Logik sein sollte. Also idealerweise nur direkte Darstellungen von Grafikobjekten. Da wir mittlerweile schon einige if-Abfragen direkt in der paint-Methode sitzen haben, müssen wir damit rechnen das dies früher oder später auf die Performance geht. Und das ist nicht akzeptabel. Schließlich wollen wir nach jedem Klick nicht eine Sekunde warten bis der Punkt erscheint. Natürlich passiert das nicht so schnell bis dieser Fall eintritt, aber es ist dennoch guter Programmierstil die Logik auszulagern bzw. das Programm an dieser Stelle zu optimieren.
Diese Optimierungen und die sich daraus ergebenden neuen Möglichkeiten wollen wir im nächsten Kapitel behandeln.