Übungsblatt 7 12 Contains Methode Nun wenn man die geometrische Objekte zeichnen kann, wäre es schön, auch ein Objekt klicken zu können und z.B. seinen Namen auf dem Bildschirm zu kriegen. Dazu werden wir nun die Methode contains entwickeln. Die erste Aufgabe ist, die ganzzahlige Koordinaten des Mausklicks zu reellen Koordinaten der geometrischen Objekten umzuwandeln. Wer soll diese Umwandlung machen? Bestens die Klasse, wo die umgekehrte Aufgabe gelöst wird — GPrim. Man soll dort also eine neue Methode schreiben: public static double uCoord(int val) { return val / koef; } Es werden zwei neue Methoden zur Klasse GObject eingefügt: public boolean contains(int cx, int cy) und protected abstract boolean interneContains(double cx, double cy) . Sie sollen true zurückliefern, falls die gegebene Koordinaten dem Objekt gehören und sonst false. Analog mit zeichnen(Graphics) und internesZeichnen() , wird die erste Methode die Daten vorbereiten während die zweite für alle Unterklassen implementiert und die vorbereitete Daten verarbeiten wird. Die Vorbereitung schließt Umwandlung der Mauskoordinaten zu Koordinatensystem des Objekts ein: // ... klasse GObjekt protected abstract boolean interneContains(double cx, double cy); public boolean contains(int cx, int cy) { return interneContains(GPrim.uCoord(cx)-px, GPrim.uCoord(cy)-py); } Nun soll man interneContains für die Unterklassen implementieren. Nehmen wir an, dass cx und cy vorbereitete Koordinaten des Klicks sind und c der Punkt (cx , cy ) ist. Dann gelten die folgende Bedingungen zum Liegen des Punktes c auf einem Objekt: Punkt p Strecke a-b Ellipse e(a, b) → →=− px = cx ∧ py = cy oder − pc 0 − → − → ac = α ab, wobei α ∈ [0, 1] cx 2 cy 2 + b ≤1 a Für Dreieck und Viereck muss man aber einen anderen Algorithmus verwenden. Obwohl später so genannte „Ray casting“-Algorithmus verwendet wird, werden wir uns nun auf die Flächenregel orientieren. Die Idee ist, dass wenn man das Dreieck ABC und Punkt P ∈ ABC hat, gilt die folgende Aussage bezüglich der Flächen: A(ABP) + A(BC P) + A(C AP) = A(ABC ) 22 Dreieck’s interneContains Entsprechend der gegebenen Formel, kann man diese Methode so schreiben: protected boolean interneContains(double cx, double cy) { Punkt p = new Punkt(cx, cy); Dreieck abp = new Dreieck(a, b, p), bcp = new Dreieck(b, c, p), cap = new Dreieck(c, a, p); return GMath.equals(abp.flaeche()+bcp.flaeche()+cap.flaeche(), flaeche()); } 12.2 Autor: M. A. 12.1 Viereck’s interneContains Beim Viereck kann man zwei Dreiecke bilden: abc und cda. Wenn der Punkt zu einem von diesen Dreiecke gehört, ist er auch im Viereck. 13 Den Applet klicken Um die Mouse-events wahrnehmen zu könnnen, soll eine Klasse zuerst die Schnittstelle MouseListener aus java.awt.event.*; implementieren. In unserem Fall wird es genügen, dass der Applet sich selbst ein MouseListener wird. Also kehren wir zurück zu unserem Demo.java, und fügen wir da implements MouseListener ein (nachdem wir das genannte Paket importiert haben). Danach wird Ihres Program nicht mehr laufen, bevor Sie die gebrauchte Methoden (auch wenn leer!) implementieren. Machen Sie also das für mousePressed, mouseEntered, mouseClicked, mouseExited und mouseReleased. Alle diese Methoden sind public void und haben MouseEvent e als Argument. Ihre Körper sind leer. Damit Sie Ihr neues MouseListener aktiv machen. Sollen Sie nun am Ende der init() Methode die folgende Zeile einfügen: addMouseListener(this); Nach dem ist dieser Applet sich selbst ein MouseListener geworden. Was fehlt, ist die Möglichkeit des Typs ZObjekt, die Methoden contains und art anzubieten. Also werden wir sie da schreiben: public interface ZObjekt { // ... public boolean contains(int cx, int cy); public String art(); } Nun zurück zum Applet. Von allen neu implementierten Methoden werden Sie nur mousePressed erweitern. Kehren wir zu dieser Methode. Wenn Benutzer den Applet klickt, wird diese Methode gerufen und aus dem Objekt e können Sie mittels Methoden getX() und getY() die ganzzahlige Koordinaten des Klicks nehmen. Wenn Sie diese Koordinaten einem ZObjekt über contains(int, int) geben, erfahren Sie gleich, ob dieses Objekt geklickt wurde. Falls das geschieht, sollen Sie den Namen des Objekts (Methode art()) auf diesen Koordinaten des Applets schreiben. Da die 23 Methode mousePressed aber nicht zum Zeichnen gemeint ist, muss sie dese Werte irgendwo speichern, damit sie später von der Methode paint() benutzt werden können. Für den Namen werden wir ein neues Attribut private String artText = ""; benutzen und die Klickkoordinaten werden in neuen Attributen private int klickX = 0, klickY = 0; gespeichert werden. Jetzt kann man diese Methode schreiben. Man greift jedem Objekt aus arr zu, und gibt ihm die Koordinaten des Klicks über Methode contains. Falls man true als Ergebnis bekommt, speichert man die benötigte Daten, bricht die Suchschleife und ruft die Methode repaint(), sodass der Applet sein Bild wieder zeichnet. Falls kein Objekt true liefert, soll artText leer bleiben. public void mousePressed(MouseEvent e) { artText = ""; for(ZObjekt obj : arr) { if(obj != null) { if(obj.contains(e.getX(), e.getY())) { klickX = e.getX(); klickY = e.getY(); artText = obj.art(); break; } } } repaint(); } Nachdem dies gemacht wird, braucht man nur noch die Methode paint so zu erweitern, dass sie artText zeigt. Zusätzlich kann man artText ignorieren, falls sie leer ist. Also Folgendes soll man am Ende der paint()-Methode schreiben: if(!artText.equals("")) { g.drawString(artText, klickX, klickY); } Und es läuft schon. 14 ArrayList Eine klassische Reihe zu benutzen (wie in unserem Demo-Applet) ist nicht immer bequem. Speziell wenn die Länge der Reihe nicht im Voraus bekannt ist. Deswegen werden wir die Klasse ArrayList18 aus dem Paket java.util.*; zum Spechern der Objekte benutzen. Es wird Schritt für Schritt gegeben, welche Änderungen man tun soll. Zuerst soll man die Zeile mit dieser Reihe finden: private ZObjekt[] arr = {null, null, null}; Und sie mit Folgendem ersetzen: private ArrayList<ZObjekt> arr = new ArrayList<ZObjekt>(); 18 Da diese Klasse für unsere Zwecke sehr intuitiv ist, wird sie hier nicht ausführlich betrachtet. Über sie kann man mehr unter http://java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html finden 24 Weitere Unterschiede sind bei der Erzeugung der neuen Objekte: Vor arr[0] = new Punkt(2,1); arr[1] = new Dreieck().positionieren(4,4); arr[2] = new Ellipse(2,1).positionieren(3,2); Nach arr.add(new Punkt(2,1)); arr.add(new Dreieck().positionieren(4,4)); arr.add(new Ellipse(2,1).positionieren(3,2)); Falls Sie die Schleife in der Methode paint() noch nicht nach foreach umgewandelt haben, tun Sie es nun (Musterlösung an der Seite 21. vom Übungsblatt 6). Das soll alles sein. Nun können Sie weitere Objekte zu arr mittels ihrer Methode add einfügen. 25