{"id":496,"date":"2018-11-15T14:03:48","date_gmt":"2018-11-15T13:03:48","guid":{"rendered":"https:\/\/freizone.net\/java-einfach-lernen\/?p=496"},"modified":"2020-05-11T19:29:40","modified_gmt":"2020-05-11T17:29:40","slug":"grafikobjekt-verbessern","status":"publish","type":"post","link":"https:\/\/freizone.net\/java-einfach-lernen\/2018\/11\/15\/grafikobjekt-verbessern\/","title":{"rendered":"Grafikobjekt verbessern"},"content":{"rendered":"<p>Ideal w\u00e4re es doch, wenn wir in unserem Grafikobjekt nicht nur den Typ der Grafik abspeichern, sondern die Grafik selbst &#8211; also gleich die Zeichnung die mit dem Zeichnungsbefehl fillOval oder drawRect erstellt wird.<br \/>\nEine Ellipse ist ein Objekt vom Typ <em>Ellipse<\/em> ein Quadrat vom Typ <em>Rectangle.\u00a0<\/em>Wir k\u00f6nnten nun in unserem Grafikobjekt statt dem Stifttyp einfach eine Variable vom Typ <em>Object<\/em> speichern was einfach ausgedr\u00fcckt alles m\u00f6gliche, also auch eine Ellipse oder ein Rectangle enthalten kann. Nur wie bringt man es dann in der Paint auf dem Bildschirm?<br \/>\nUnser\u00a0 Graphics-Objekt das der paint-Methode \u00fcbergeben wurde, hat auch daf\u00fcr eine L\u00f6sung bzw. eine Methode. Man ruft diese auf mit: <code>g.draw(shape)<\/code>.\u00a0 Ein Shape ist ein sogenanntes Interface. Im Gegensatz zur Klasse besitzt ein Interface\u00a0keine Implementierungen von Methoden, sondern nur leere Methoden sowie evtl. Konstanten, das sind nicht mehr ver\u00e4nderbare Variable. Wir werden uns auch gleich mehr damit befassen. Jedenfalls m\u00fcssten wir, wenn wir Object verwenden,\u00a0dieses dann erst auf ein Shape casten. Also <code>g.draw((shape) objekt)<\/code>. Auch an einigen anderen Stellen m\u00fcssten wir darauf achten.<br \/>\nEleganter ist es, wenn wir uns mit der Schnittstelle Shape jetzt kurz befassen.<\/p>\n<p>Ein Interface ist eine Sammlung von leeren Methoden die einem bestimmten Zweck dienen. Ein Objekt das dieses Interface implementiert passt dann einfach jene Methoden f\u00fcr den eigenen Zweck an.<\/p>\n<p>Das Objekt Ellipse enth\u00e4lt zun\u00e4chst nur die Koordinaten der Ellipse. Aber es braucht auch Methoden um diese Daten zu verarbeiten. Zum Beispiel ein Methode um die Werte auszulesen (getter) oder ob ein \u00fcbergebener Punkt innerhalb der Ellipse liegt etc.<br \/>\nDiese Methoden werden von allen geometrischen Objekten in Java verwendet. Deshalb implementieren auch alle letztendlich das Interface Shape (Ellipse z.B. das Interface RectangleShape welches wiederum Shape implementiert). Shape enth\u00e4lt also weitere Methoden die ben\u00f6tigt werden um die Grafik darzustellen.<\/p>\n<p>Von einem Interface allein kann nat\u00fcrlich kein noch Objekt gebildet werden. Man kann ihm aber ein Objekt zuweisen das ein Interface des gleichen Typs implementiert hat. Genau das machen wir uns zunutze indem wir in unserem Grafikobjekt statt stifttyp, eine Variable vom Datentyp Shape definieren.<br \/>\nF\u00fcr den Fall das die Werkzeugspitze Kreis ausgew\u00e4hlt ist, erzeugen wir einfach eine neue Ellipse und f\u00fcgen sie unserem virtuellem Bildschirm hinzu. Im Konstruktor des Grafikobjekts muss dann nur noch das Shape mit der Ellipse initialisiert werden, was problemlos m\u00f6glich ist da ja Ellipse das Shape implementiert. Wie in Java \u00fcblich enth\u00e4lt danach die Variable Shape eine\u00a0<em>Referenz<\/em> auf das Ellipse-Objekt.<br \/>\nJetzt aber ran an den Speck:<br \/>\nGrafikobjekt.java<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-highlight=\"11,14,16,28-30\">package miniMalen1;\r\n\r\nimport java.awt.Point;\r\nimport java.awt.Shape;\r\n\r\nimport javax.swing.JCheckBox;\r\n\r\nclass Grafikobjekt {\r\n\r\n  Point koordinaten;\r\n  Shape form;\r\n  boolean gefuellt;\r\n\r\n  public Grafikobjekt(Point p, Shape st, boolean cb) {\r\n    this.koordinaten = p;\r\n    this.form = st;\r\n    this.gefuellt = cb;\r\n  }\r\n\r\n  public Point getKoordinaten() {\r\n    return this.koordinaten;\r\n  }\r\n\r\n  public void setKoordinaten(Point koordinaten) {\r\n    this.koordinaten = koordinaten;\r\n  }\r\n  \r\n  public Shape getForm() {\r\n    return this.form;\r\n  }\r\n\r\n  public boolean isGefuellt() {\r\n    return this.gefuellt;\r\n  }\r\n}<\/pre>\n<p>Wir sehen das stifttyp dem Shape gewichen ist und stiftstaerke weggefallen, da das Shapeobjekt gleich in der richtigen St\u00e4rke, bzw. Gr\u00f6\u00dfe eingestellt wird. Dementsprechend m\u00fcssen wir nat\u00fcrlich auch den Konstruktor sowie die getter \u00e4ndern oder l\u00f6schen.<\/p>\n<p>Tafel.java<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-highlight=\"46,49-53,33\">package miniMalen1;\r\n\r\nimport java.awt.Color;\r\nimport java.awt.Graphics;\r\nimport java.awt.Graphics2D;\r\nimport java.awt.Point;\r\nimport java.awt.Rectangle;\r\nimport java.awt.Shape;\r\nimport java.awt.event.MouseEvent;\r\nimport java.awt.event.MouseListener;\r\nimport java.awt.event.MouseMotionListener;\r\nimport java.awt.geom.Ellipse2D;\r\nimport java.awt.geom.Rectangle2D;\r\nimport java.util.ArrayList;\r\nimport javax.swing.JPanel;\r\n\r\npublic class Tafel extends JPanel implements MouseListener, MouseMotionListener {\r\n\r\n  private ArrayList&lt;Grafikobjekt&gt; virtuellerBildschirm = new ArrayList&lt;Grafikobjekt&gt;();\r\n\r\n  private int x = -10;\r\n  private int y = -10;\r\n  private StartFenster auswahl;\r\n\r\n  public Tafel(StartFenster auswahl) {\r\n    this.auswahl = auswahl;\r\n    this.setBackground(Color.WHITE);\r\n    this.addMouseListener(this);\r\n    this.addMouseMotionListener(this);\r\n  }\r\n\r\n  public void paintComponent(Graphics g0) {\r\n    Graphics2D g = (Graphics2D) g0;\r\n    super.paintComponent(g);\r\n    \r\n      for (int z = 0; z &lt; virtuellerBildschirm.size(); z++) {\r\n        Grafikobjekt go = virtuellerBildschirm.get(z);\r\n        if(go.isGefuellt()) {\r\n          g.fill(go.getForm());\r\n        } else {\r\n          g.draw(go.getForm());\r\n        }\r\n      }\r\n  }\r\n  \r\n  public void zeichneAufVirtuellenBildschirm(double dx, double dy) {\r\n    Shape werkzeug;\r\n    \r\n    if ( auswahl.quadratBtn.isSelected() ) {\r\n      werkzeug = new Rectangle2D.Double(dx, dy, auswahl.getLinienstaerke(), auswahl.getLinienstaerke());\r\n    } else {\r\n      werkzeug = new Ellipse2D.Double(dx, dy, auswahl.getLinienstaerke(),auswahl.getLinienstaerke());\r\n    }\r\n    virtuellerBildschirm.add(new Grafikobjekt(new Point(x, y), werkzeug, auswahl.fuellen.isSelected() ));\r\n\r\n    this.repaint();\r\n  }\r\n\r\n  @Override\r\n  public void mouseDragged(MouseEvent e) {\r\n    System.out.println(\"Dragged\");\r\n    x = e.getX();\r\n    y = e.getY();\r\n    \r\n    zeichneAufVirtuellenBildschirm(x,y);\r\n  }\r\n\r\n  @Override\r\n  public void mousePressed(MouseEvent e) {\r\n    System.out.println(\"Pressed\");\r\n    x = e.getX();\r\n    y = e.getY();\r\n    \r\n    zeichneAufVirtuellenBildschirm(x,y);\r\n  }\r\n}\r\n<\/pre>\n<p>Nach der \u00dcbergabe der x und y-Koordinaten in die zeichneAufVirtuellenBildschirm-Methode findet ein sogenannter<em> impliziter Cast<\/em>\u00a0statt da wir im Methodenkopf statt <code>int<\/code> nun <code>double<\/code> verwenden (Zeile 46). Von einem impliziten Cast spricht man wenn ein niederwertiger Datentyp in einen h\u00f6herwertigen umgewandelt wird. Solch ein Cast muss in Java nicht angegeben werden, der Compiler \u00fcbernimmt das selbst.<\/p>\n<p>Nach der Deklaration von <code>Shape werkzeug<\/code> verwenden wir die if-Abfrage um das Shape entweder mit einer Ellipse oder einem Quadrat zu initialisieren (Zeile 49 &#8211; 53).<\/p>\n<p>Da Sowohl Ellipse2D als auch Rectangle2D die nur in Verbindung mit Graphics2D funktionieren, m\u00fcssen wir in Zeile 33 unser Graphics-Objekt in ein Graphics2D-Objekt casten. Graphics2D ist eine abstrakte Klasse, die wesentlich mehr Methoden zur Verf\u00fcgung stellt als eine normale Graphics-Klasse.<\/p>\n<p>Schlie\u00dflich haben wir in den Zeilen 38 bis 42 wesentlich weniger zu tun und k\u00f6nnen bequem das Shape zeichnen lassen.<\/p>\n<p>StartFenster.java<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-highlight=\"66, 85-87\">package miniMalen1;\r\n\r\nimport java.awt.BorderLayout;\r\nimport java.awt.Color;\r\nimport java.awt.Dimension;\r\nimport java.awt.event.ActionEvent;\r\nimport java.awt.event.ActionListener;\r\nimport java.util.ArrayList;\r\n\r\nimport javax.swing.Action;\r\nimport javax.swing.ButtonGroup;\r\nimport javax.swing.JCheckBox;\r\nimport javax.swing.JFrame;\r\nimport javax.swing.JPanel;\r\nimport javax.swing.JRadioButton;\r\nimport javax.swing.JSpinner;\r\nimport javax.swing.JToggleButton;\r\nimport javax.swing.JToolBar;\r\nimport javax.swing.SpinnerNumberModel;\r\n\r\npublic class StartFenster extends JFrame {\r\n\r\n  public static void main(String[] s) {\r\n    new StartFenster();\r\n  }\r\n\r\n  private ArrayList&lt;Grafikobjekt&gt; virtuellerBildschirm = new ArrayList&lt;Grafikobjekt&gt;();\r\n  private JPanel panel;\r\n  private int x = -10;\r\n  private int y = -10;\r\n  JRadioButton kreisBtn;\r\n  JRadioButton quadratBtn;\r\n  JSpinner linienstaerke;\r\n  JCheckBox fuellen;\r\n  ButtonGroup werkzeugspitzen = new ButtonGroup();\r\n  private JToolBar auswahl;\r\n\r\n  public StartFenster() {\r\n    initialisiereStartFenster();\r\n  }\r\n\r\n  private void initialisiereStartFenster() {\r\n    this.setDefaultCloseOperation(EXIT_ON_CLOSE);\r\n    this.setBounds(0, 0, 500, 500);\r\n    this.setLocationRelativeTo(null);\r\n    this.setAlwaysOnTop(true);\r\n    this.setBackground(Color.white);\r\n    this.setTitle(\"Tafel\");\r\n    this.setLayout(new BorderLayout());\r\n\r\n    auswahl = new JToolBar();\r\n    auswahl.setPreferredSize(new Dimension(300, 30)); \/\/ PreferredSize = bevorzugte Gr\u00f6\u00dfe\r\n    auswahl.setFloatable(true);\r\n    \r\n    kreisBtn = new JRadioButton(\"Kreis\");\r\n    quadratBtn = new JRadioButton(\"Quadrat\");\r\n    werkzeugspitzen.add(kreisBtn);\r\n    werkzeugspitzen.add(quadratBtn);\r\n    \r\n    fuellen = new JCheckBox(\"F\u00fcllen\");\r\n    auswahl.add(fuellen);\r\n    \r\n    auswahl.add(kreisBtn);\r\n    auswahl.add(quadratBtn);\r\n\r\n    SpinnerNumberModel nummern = new SpinnerNumberModel(10.0, 1.0, 99.0, 1.0); \/\/ double Werte enthalten immer Kommaangaben ( default, Minimum, Maximum, Schrittweite )\r\n    linienstaerke = new JSpinner(nummern);\r\n    linienstaerke.setMinimumSize(new Dimension(50, 30)); \/\/ setzt die Mindestgr\u00f6\u00dfe ( Breite, H\u00f6he )\r\n    linienstaerke.setMaximumSize(new Dimension(50, 30)); \/\/ setzt die Maximalgr\u00f6\u00dfe\r\n\r\n    auswahl.add(linienstaerke);\r\n    this.add(auswahl, BorderLayout.PAGE_START);\r\n    \r\n    Tafel tafel = new Tafel(this);\r\n    this.add(tafel);\r\n\r\n    sichtbar();\r\n  }\r\n\r\n  private void sichtbar() {\r\n    this.setVisible(true);\r\n\r\n  }\r\n  \r\n  public double getLinienstaerke() {\r\n    double staerke = (double) this.linienstaerke.getValue();\r\n    return staerke;\r\n  }\r\n}\r\n<\/pre>\n<p>In StartFenster.java muss zun\u00e4chst der JSpinner mit double-Werten angepasst werden (Zeile 66). double-Werte kennzeichnet man einfach indem man immer Kommawerte nimmt.<\/p>\n<p>Dann passen wir den getter getLinienstaerke() an (Zeile 85 &#8211; 87), da sowohl Rectangle2D wie auch Ellipse2D double-Werte verwendet (Zeilen 50 und 52 in Tafel.java). Ansonsten w\u00fcrde es eine Fehlermeldung geben.<br \/>\nDa linienstaerke.getValue() einen Wert vom Typ Object zur\u00fcckgibt, m\u00fcssen wir ihn hier explizit in ein double casten.<\/p>\n<p>Unser Quellcode ist nun nocheinmal effizienter geworden und er\u00f6ffnet uns weitere sch\u00f6ne M\u00f6glichkeiten. Als n\u00e4chstes wollen wir einen Farbw\u00e4hler hinzuf\u00fcgen.<\/p>\n<p>Ich glaube jetzt ist wieder h\u00f6chste Zeit f\u00fcr einen Kaffee.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ideal w\u00e4re es doch, wenn wir in unserem Grafikobjekt nicht nur den Typ der Grafik abspeichern, sondern die Grafik selbst &#8211; also gleich die Zeichnung die mit dem Zeichnungsbefehl fillOval oder drawRect erstellt wird. Eine Ellipse ist ein Objekt vom Typ Ellipse ein Quadrat vom Typ Rectangle.\u00a0Wir k\u00f6nnten nun in unserem Grafikobjekt statt dem Stifttyp [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/posts\/496"}],"collection":[{"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/comments?post=496"}],"version-history":[{"count":9,"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/posts\/496\/revisions"}],"predecessor-version":[{"id":885,"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/posts\/496\/revisions\/885"}],"wp:attachment":[{"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/media?parent=496"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/categories?post=496"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/freizone.net\/java-einfach-lernen\/wp-json\/wp\/v2\/tags?post=496"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}