Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Applet mit Backgroundimage und Vordergrundelementen
In einem Projekt haben wir ein Problem mit einem Applet. Das Applet soll im Hintergrund ein Stadtplan(winterthur_altstadt.JPG) anzeigen, darauf sind verschiedene anklickbare Objekte vorhanden. Das Bild einzeln einzufügen ist kein Problem. Nun möchten wir jedoch ein klickbares Element (Button beispielsweise) dazufügen.
Code:
public class Stadtplan extends JApplet {
public void init() {
this.setSize(1075,861);
JLabel label = new JLabel(new ImageIcon("winterthur_altstadt.JPG"));
Button but = new Button();
but.setBackground(Color.YELLOW);
Panel panel = new Panel();
panel.add(label);
this.getContentPane().add(panel);
but.setSize(10, 10);
// auch mit but.setMaximumSize(new Dimension(10,10));
this.add(but);
}
}
Hier haben wir das Problem, dass der Button das ganze Appletfenster füllt, obwohl wir die grösse auf 10,10 beschränkt hatten.
Wenn wir das Bild über das Graphics-Objekt einfügen, ist der Button dann hinter dem Bild, und somit nicht sichtbar.
Das Hintergrundbild musst du entweder im JApplet oder in einem darin eingebetteten JPanel in dessen paintComponent()-Methode mit der drawImage()-Methode zeichnen.
Dann kannst entweder die Punkte zum Anklicken in einer Koordiatenberechnung festlegen und dann die Mausposition überwachen (Beispiel), oder JLabels oder JButtons an bestimmten Koordinaten positionieren.
Mein Tipp: Laß die Buttons weg und ermittle die angeklickte Region einfach über den Punkt, den Dir MouseEvent.getPoint() liefert.
Die Karte zeichnest Du, wie schon von L-ectron-X beschrieben in der paintComponent-Methode.
Um das angeklickte Objekt zu ermitteln schlage ich zwei Methoden vor:
1. Die Einfache:
Du ermittelst die Koordinaten von kleinen Rechtecken auf Deinem Bild. Diese definierst Du als Klassenvariable vom Typ Rectangle und prüfst mit der Methode Rectangel.contains(..) ob der Mausklick innerhalb eines der vordefinierten Rechtecke ist und führst dann die geplante Aktion aus.
2. Die Schwierigere:
Du malst Dir eine "Schattenkarte". Die relevanten Punkte färbst Du mit unterschiedlichen Farben ein (z. B. Rathaus = gelb, Schule = rot, usw.) und färbst den Rest der Karte schwarz. Diese Bild lädst Du gleich am Anfang mit und hälst es als Objekt "im Hintergrund" - soll heißen im Speicher. Diese Schattenkarte wird niemals angezeigt!
Wenn jetzt Deine "richtige" Straßenkarte angeklickt wird, ermittlest Du den Farbwert des angeklickten Punktes von der "Schattenkarte" und führst je nach Farbwert die entsprechende Aktion aus.
Diese Methode ist etwas komplexer umzusetzen, aber dafür ist sie genauer, wenn es z. B. auf 2 Bereiche ankommt, die sehr nahe bei einander liegen, so daß man keine oder nur sehr kleine Rechtecke definieren könnte. Oder wenn Du z. B. sehr unterschiedlich geformte Bereiche hast, z. B. einen Fluß ermitteln willst.
Wofür gibt es das Shape Interface und Polygone?
Um Rechenleistung zu sparen erst einen Bounding Box Test durchführen, dann fallen die meisten Polygone von Anfang an raus.
Prusik hat ja leider nicht genau gesagt, was er vorhat. Wenn es relativ einfache Bereiche sind, sind Polygone sicherlich sinnvoll. Wenn es komplexere Strukturen sind (ein Fluß) fände ich es eher umständlich ein Polygon zusammen zu frickeln.
Ist aber zugegebenermaßen eine persönliche Vorliebe.
das mit dem Rathaus, der Schule, der Bibliothek, der Kirche, dem Kino und dem Bahnhof... (nicht vollständig), aber sind doch zählbar und nicht sehr kompliziert... Sonst könnten wir ja auch keinen Button darüber legen... also wir dachten uns das auch so, dass auf dem Bild die Punkte zu sehen sind - von dem her ist es ja schwieriger einen Bach zu nehmen, da man sonst gerade die ganze Stadt anmalen kann... Wobei so mit "onmouseover" wäre natürlich auch nicht schlecht.. also, wenn man mit der Maus über die Stadt fährt, sieht man die verschiedenen Objekte, die man anklicken kann... Heia.. ich habe viele Ideen, aber irgendwie fehlt mir die Technik...
Auf jeden Fall danke für die vielen Tipps, die ihr mir bis jetzt gegeben habt! Wir werden uns mal mit den verschiedenen Vorschlägen auseinandersetzen... Und das mit den Polygonen tönt jetzt für mich sehr angenehm, da mir das irgendwie bekannt vor kommt, wenn es so was ist wie hier: http://de.selfhtml.org/html/grafiken/verweis_sensitive.htm
Im Prinzip das gleiche, nur kannst du mit Polygonen feiner Auflösen als mit Rechtecken.
Den Cursor zu ändern wenn er über einem 'hotspot' liegt ist eher trivial (Stichwort Shape#contains(Point))
verstehe ich das richtig:
- Klasse die Shape implementiert, z.B. Polygon
- MouseListener, der bei jeder Mausbewegung überprüft, ob man "auf" einem Shape ist (contains(kord_x,kord_y)==true, bzw. bei Mausklick)
- (Hintergrund)-Bild mit g.getGraphics(..) gezeichnet
Nun meine Frage: jetzt will ich nicht nur ein Polygon speichern, sondern eine ganze Menge. Das gibt einen ziemlich langen und unübersichtlichen Code, wenn ich oben jedes Polygon initalisiere, unten im MouseListener für jedes Polygon eine if abfrage machen muss. Und wirklich objektorientiert ist das ja nicht, hab ich das Gefühl.
Nur stellt sich die frage, wie mach ich es besser?
Nun hab ich mir gedacht, ich mach eine Liste, in der alle polygone gespeichert sind. Danach finde ich problemlos raus, ob sich die Maus über einem Polygon befindet - doch nun, jedes Objekt macht ja was anderes - wie kann Beispielsweise jetzt beim einen ein Fenster öffnen, beim anderen ein Soundclip abspielen lassen, das müsste man ja irgendwie in die Liste speichern, denn sonst ist man ja wieder bei den if-Abfragen für jedes einzelne Objekt.
Ich hoffe ihr versteht, was ich mein und könnt mir einen Tipp geben.
Eine möglichkeit wäre eine Klasse zu definieren, der man ein Polygon und eine Action setzen kann. Wenn ein Mausklick registriert wird, führt das Objekt die übergebene Action aus.
irgendwie hab ich das gefühl das ich etwas nicht verstande habe mit der action!
Er sagt auch javax.swing.Action sei abstrakt, und ich es daher nicht instanziieren soll...
Nur wie wendet man das an? Ich kann bei bestem Willen kein Beispiel finden (google, java api). Habe noch nie mit enum-Typen gearbeitet, glaube das ist sowas...
sorry für den Doppelpost, aber ich möchte gerne noch die Lösung präsentieren, die ich gefunden habe, falls jemand ein ähnliches Probelm haben sollte.
Das mit der Action hab ich nicht hingekriegt, ich habs jetzt anders gelöst:
In der Klasse StadtPlanWinterthur.java speichere ich eine Liste von BuildingAction
Code:
private List<BuildingAction> buildings;
BuildingAction ist eine abstrakte Klasse:
Code:
public abstract class BuildingAction {
private Polygon mypoly;
public Polygon getMypoly() {
return mypoly;
}
public void setMypoly(Polygon mypoly) {
this.mypoly = mypoly;
}
public abstract void toDoWhenClicked();
public abstract void PolygonSetzen();
}
Nun erstelle ich für jedes Building eine Klasse, die gezwungen ist, anzugeben, was auszuführen ist, und wie das Polygon aussieht.
Code:
public class buildBahnhofAction extends BuildingAction{
public buildBahnhofAction(){
setMyPoly();
}
@Override
public void toDoWhenClicked() {
System.out.println("Bahnhof");
//...
}
@Override
public void PolygonSetzen() {
Polygon poly = new Polygon();
poly.addPoint(204, 244);
poly.addPoint(104, 416);
poly.addPoint(157, 432);
poly.addPoint(248, 253);
setMypoly(poly);
}
}
Jetzt kann ich im MouseListener einfach durch die Liste durchgehen und die Action ausführen lassen:
Code:
class myMouseListener implements MouseListener{
public void mouseClicked(MouseEvent e) {
for(BuildingAction ba : buildings){
if(ba.getMypoly().contains(e.getX(), e.getY())){
ba.toDoWhenClicked();
}
}
}
}
Manchmal ist die Lösung einfacher als die Frage Wir haben haben uns in der Schule wirklich mehrere Lektionen damit rumgequält, wie man das jetzt lösen müsste... so einfach gehts... wenn man die Idee hat...
Vielen Dank an alle, die uns hier geholfen haben...