# Applet mit Backgroundimage und Vordergrundelementen



## Prusik (12. Dez 2007)

Hallo zusammen

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. 



```
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.

Vielen Dank für eure Tipps!


----------



## L-ectron-X (12. Dez 2007)

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.


----------



## Quaxli (12. Dez 2007)

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.


----------



## Wildcard (12. Dez 2007)

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.


----------



## Quaxli (12. Dez 2007)

Tja, wofür? Das frage ich mich auch. 

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.


----------



## Prusik (12. Dez 2007)

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


----------



## Wildcard (12. Dez 2007)

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))


----------



## Prusik (12. Dez 2007)

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

EDIT:
also folgende Klassen, die ich bastle:

Stadtplan (Hintergrundbild, Polygone definieren)
--> MouseAction (Bei Mausaction --> über Polygon?)


----------



## Wildcard (12. Dez 2007)

Soweit richtig, bis auf getGraphics. Gezeichnet wird in paintComponent (Swing) bzw. paint (AWT).


----------



## Prusik (14. Dez 2007)

ihr seit super! es funktioniert!

Habe gerade noch eine Frage, die mit dem hier zusammenhängt:
Jetzt sieht es so aus:


```
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;


public class StadtPlanWinterthur extends Applet {

    String adr_stadtplan;
    Image pic;
    Polygon polytest;

    @Override
    public void init() {
       //MouseListener dazufügen
       this.addMouseListener(new myMouseListener());
       //Grösse festlegen
       this.setSize(555, 394);
       //Stadtplan laden
       adr_stadtplan="stadtplan_winterthur.jpg";
       pic = getImage(getCodeBase(),adr_stadtplan);

       //Polygon mit Eckpunkten definieren
       polytest = new Polygon();
       polytest.addPoint(0, 0);
       polytest.addPoint(0, 100);
       polytest.addPoint(100, 100);
       polytest.addPoint(100, 0);
       

       
    }

    @Override
    public void paint(Graphics g) {
        //Bild zeichen
        g.drawImage(pic, 0,0,this);        
    }
    
    class myMouseListener implements MouseListener{

        public void mouseClicked(MouseEvent e) {

              if(polytest.contains(e.getX(), e.getY())){
                  JFrame fenster = new JFrame("Testfenster");
              }
        }

        
    }
    


}
```
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.


----------



## Wildcard (15. Dez 2007)

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.


----------



## Prusik (15. Dez 2007)

hm... das versteh ich jetzt ehrlich gesagt nicht ganz....

ich versuchs mal so zu machen, wie ich das verstehen würde:

Der StadtPlanWinterthur-Klasse wird eine ArrayList von MouseClickPolygon dazugefügt, oke.. Die klasse MouseClickPolygon sähe etwa folgendermasen aus

```
class MouseClickPolygon{
     Polygon poly;

     //Konstruktor

     //Setter/Getter Methoden

}
```

aber wie speicher ich jetzt da die Action??


----------



## Wildcard (15. Dez 2007)

```
private Action action;
```
 :wink:


----------



## Prusik (18. Dez 2007)

hm entweder bin ich jetzt blöd oder weiss auch nicht.. auf jeden fall krieg ichs nicht hin... 

also ich hab die Klasse MouseClickPolygon geschrieben:


```
import java.awt.Desktop.Action;
import java.awt.Polygon;

public class MouseClickPolygon {
    private Polygon polygon;
    private Action action;
    public MouseClickPolygon(){
        polygon = new Polygon();
    }
    
    public void setQuadratPoly(int x_oben, int y_oben, int x_unten, int y_unten){
        getPolygon().addPoint(x_oben, y_oben);
        getPolygon().addPoint(x_oben, y_unten);
        getPolygon().addPoint(x_unten, y_oben);
        getPolygon().addPoint(x_unten, y_unten);
    }

    public Polygon getPolygon() {
        return polygon;
    }

    public void setPolygon(Polygon polygon) {
        this.polygon = polygon;
    }

    public Action getAction() {
        return action;
    }

    public void setAction(Action action) {
        this.action = action;
    }
    
    
    

}
```

und nun rufe ich das in StadtPlanWinterthur2 auf:


```
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.Action;


public class StadtPlanWinterthur2 extends Applet {

    String adr_stadtplan;
    Image pic;
    Polygon polytest;
    MouseClickPolygon kirche;

    @Override
    public void init() {
       this.addMouseListener(new myMouseListener());
       this.setSize(555, 394);
       adr_stadtplan="stadtplan_winterthur.jpg";
       pic = getImage(getCodeBase(),adr_stadtplan);
       
[b]       kirche = new MouseClickPolygon();
       kirche.setQuadratPoly(0, 0, 100, 100);
       kirche.setAction(new Action(System.out.println("Hallo")));[/b]
       
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(pic, 0,0,this);   
    }

    


}
```

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...


----------



## Prusik (19. Dez 2007)

sorry für den Doppelpost.. aber wir stecken fest... 

Wir haben schonmal festgestellt, dass es nicht das Interface Action ist, welches wir benutzen... Also müsste es doch das hier sein
http://java.sun.com/javase/6/docs/api/java/awt/Desktop.Action.html

richtig?

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... 

Vielen Dank für eure Hilfe!


----------



## Prusik (24. Dez 2007)

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

```
private List<BuildingAction> buildings;
```


BuildingAction ist eine abstrakte Klasse:

```
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. 


```
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:

```
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...


----------

