# GUI Auflösung ändern - Koordianten und Proportions Problem



## fresha (30. Mrz 2016)

Hallo,

ich wälze mich schon seit 2 Tagen durch das Net und habe leider keine zufriedenstellende Lösung zu meinem Problem gefunden. 

Momentan sitze ich an meinem Hausautomationsprogramm und habe kleinere Schönheitsfehler.
Als Hintergrund habe ich mit Photoshop den Umriss des Hauses sowie Tabellen für die Anzeige für Daten gezeichnet. Dieser Hintergrund wird in ein Label (lblBackground) gepackt und dieser wiederum auf einen JPanel(panelBackground). Auch habe ich mit Photoshop Buttons,Fenster und Schieberregler selber gezeichnet und jeweils in Labels (lblButton1,2,3...lblSchieberegler1,2,3...lblFenster1,2,3...)gepackt und dieser wiederum in ein extra JPanel (panelComponente) gelegt.
Da die Buttons,Schieberegler und Fenster genau auf dem Hintergrund positioniert werden müssen, musste ich das panelComponent Layout auf null setzen. Ja ich weiß, nicht schön.
Zuerst dachte ich mir das es ausreichen wird das Hauptframe einfach auf eine feste Größe zu setzen um somit das Problem der Verschiebung der Komponente (Buttons,Fenster..) beim größer ziehen der Frame zu verhindern. Mittlerweile bin ich doch davon überzeugt das ganze dynamisch für verschiedene Bildschirmauflösung zu erzeugen.
Problem ist nur, wie kriege ich die Komponente dazu immer auf die exakte Position und auch Proportion zum Hintergrund zu behalten, wenn ich das Frame größer ziehe und somit auch der Hintergrund sich vergrößert.   
Ich habe zwar immer gelesen man soll doch mit den Layouts arbeiten, aber mit den Layouts kann ich nicht pixelgenau die Komponente auf das Hintergrund platzieren.

Die einzige Möglichkeit die mir einfiel war, die Auflösung des Bildschirms zu ermitteln und je nach Auflösung drei verschiedene Versionen des Hintergrund und der Komponente mit Photoshop zu erzeugen und darzustellen, aber das glaube ich ist zu umständlich. Oder es doch auf eine feste Größe zu belassen und per ScrollPane die Sache zu realisieren, was aber nicht so schön wäre.


----------



## Jardcore (30. Mrz 2016)

Wieso hast du die Bilder in Labels und dann in Panels gepackt und nicht einfach Images erzeugt und diese Positioniert (bin kein Swing Experte).
Ich würde in kleinen Schritte versuchen erst einmal nur das Hintergrundbild auf verschiedene Auflösungen sauber darzustellen. Wenn du das geschafft hast fängst du an mit einer Komponente das gleiche zu machen, also ein Button sollte sich von der Skalierung genauso verhalten wie das Hintergrundbild. Danach sollte der Rest kein Problem mehr darstellen.

Möglicherweise kannst du dir auch mal JavaFx angucken, dort gibt es viele Möglichkeiten Elemente am Fenster auszurichten.

Ein anderer Tipp, wieso benutzt du den Variablennamen: _lblBackground _und nicht _backgroundLabel_? zweiteres kann man doch wunderbar lesen


----------



## fresha (30. Mrz 2016)

Ich bin leider noch kein Java-Vollprofi, aber zu Anfang als ich mit Java begann wurden bei den Beispielen erst ein ImageIcon erzeugt und dieses dem Label gegeben per Label.seticon(icon) . Das habe ich bis heute so beibehalten.

Ich habe für das Hintergrund bereits eine Methode geschrieben, die das Hintergrund je nach Größe des Frames rauf oder runter skaliert. Das Problem sind aber die anderen Komponente wie _ButtonLabel _ala _lblButton  . _Beim skalieren des Hintergrund stimmt zum einen die X und Y Position nicht mehr und auch die Proportion der Komponente.Vielleicht wäre damit, wie du schreibst, das JavaFX hilfreich!?!

Warum meine Variablen so heißen, keine Ahnung, hat sich auch bei mir mit der Zeit so eingebürgert.
Glaube damit ich zuerst sehe um was für ein Object es sich handelt und dann was es für eine Funktion hat.


----------



## dzim (30. Mrz 2016)

JavaFX ist der modernere, hardwarebeschleunigte, Nachfolger des von dir verwendeten Swing. Also ein komplett anderes GUI-Framework.
Generell würde ich allen Neueinsteigern nicht mehr empfehlen, Swing zu verwenden. Nicht, weil es nicht geht (die Java-seitige Unterstüzung wird nicht so schnell verschwinden), sondern weil es nicht mehr weiter gepflegt wird und schlich auch von dem Grundsätzlichen Herangehen her altmodisch ist.

Jedoch ist generell ratsam, erst einmal überhaupt mit Java zurecht zu kommen, ehe man sich an noch weitere Konzepte (wie etwa GUIs, Model-View-Control, ...) wagt.

Egal ob Swing oder JavaFX: Du musst natürlich schlaue (in Bezug auf deine GUI) Layouts verwenden, die sich idealerweise um die meiste Arbeit mit deinen Komponenten (Anordnung im Fenster, Skalierung der Komponenten, etc.) kümmern. Und: Doch, auch mit Layouts lassen sich Komponenten gut platzieren - das ist aber je nachdem eventuell schon etwas fortgeschritten.
Statt eines statischen Bildes könntest du dein Hintergrundbild auch als SVG erstellen - die Unterstützung dafür ist speziell in JavaFX recht gut - und das Bild skaliert!

Vielleicht zeigst du mal ein Mockup. Dann könnten wir schauen, was man damit anfangen kann.

Ich denke, wenn du noch nicht so grosse Java-Erfahrung hast, ist ein Hausautomation-Projekt schon recht... Anspruchsvoll. Aber gut. Kann man auch viel draus lernen.


----------



## Thallius (30. Mrz 2016)

Eigentlich ist das Ganze nicht so schwer.

Du erzeugst ein MainPanel mit einem Hintergrundbild, dass maximal groß ist. Also z.B. 4096*4096 Pixel (Sollte für die nächsten Jahre reichen naja und wenn nicht, dann wird das Bild beim Hochskalieren halt ein wenig unschön).

Dann erzeugst Du zu diesem Bild passende Buttons (In Photoshop ja kein Problem. Baust Dir alles schön zusammen mit eigenen Ebenenen für jeden Button).

Dann schreibst Du Dir Position und Größe der Buttons auf dem Hintergrundbild auf.

Beim Erstellen der UI must du nun nur die Buttons relativ zur Mainpanel Größe umrechnen. Also ist das Main Panel nur 2048x2048, dann sind deine Buttons an 50% der alten Position und nur 50% so groß. 

Natuürlich must du dich dann in den Resize des Panels reinhängen und beim Resizen die Buttons ändern. Sollte aber ja kein Problem sein.

Gruß

Claus


----------



## fresha (31. Mrz 2016)

@dzim

Ich arbeite schon ca. 1 Jahr mit Java und habe schon einige eigene Projekt verwirklicht. Dennoch sage ich von mir selbst, das ich kein Vollprofi in Java bin und es gibt immer wieder was dazu zu lernen, vor allem wenn man neue Projekte verwirklichen möchte, wie mein Jetziges.
Da muss ich mich wohl mit JavaFX mal auseinandersetzen, was wohl etwas dauern wird bis man es halbwegs drauf hat.
Achso, was ist ein Mockup?

@Thallius 

Ich habe mal probeweise deine Idee in ein neues Java-Projekt versucht umzusetzen.
Zumindest bei meiner Umsetzung habe ich ein Problem.
Wenn ich die Komponente prozentual zum Hintergrundbild positionieren möchte, dann muss ich mit Doublewerten arbeiten, aber Double möchte setBounds nicht.

Beispiel: (skalliertes Backgroundbild)

double x=Background.getWidth/100;
double y=Background.getHeight/100;

lblButton.setBounds(x*20,y*30....);


----------



## Jardcore (31. Mrz 2016)

Ein Mockup ist eine Konzeptzeichnung deiner GUI, diese kann aus mehreren einzelnen Zeichnungen bestehen, welche einen Ablauf darstellen. Zum Beispiel kann man so beschreiben wie der Login auf einer Website aussehen soll. Designer und Programmierer können so zusammen ohne zu Fachlich zu werden besprechen wie eine Funktion gestaltet werden soll.

Du kannst double auch in int casten 

```
lblButton.setBounds((int)(x * 20), (int)(y * 30) ...);
```


----------



## Thallius (31. Mrz 2016)

Wenn Du einfach die Berechnung in einem Schritt machst brauchst du auch keine double.

Ich würde es aber trotzdem mit double rechnen und mir erstmal Scale-Faktoren errechnen:


```
double scaleXFactor = PanelWidth / BGImageWidth;
double scaleYFactor = PanelHeight / GBImageHeight;
```

und dann halt für jedes Element die neuen Positionen und Größen errechnen:


```
int newXPos = (int) (oldXPos * scaleXFactor);
int newYPos = (int) (oldYPos * scaleYFactor);
```

Gruß

Claus


----------



## fresha (1. Apr 2016)

So, ich habe es glaube ich endlich hinbekommen.

Ich lese mir die Auflösung des Bildes ein:


```
BufferedImage bimg;
        try
        {
            bimg = ImageIO.read(new File("raster.jpg"));
            BGImageWidth          = bimg.getWidth();
            GBImageHeight         = bimg.getHeight();        
        } catch (IOException e)
        {
            e.printStackTrace();
        }
```

In meinem Beispiel habe ich mir ein Raster-Bild heruntergeladen 1440x960 Pixel.

Ermittel den Scale-Faktor:


```
double scaleXFactor = lblBackground.getWidth() / BGImageWidth;
double scaleYFactor = lblBackground.getHeight() / GBImageHeight;
```

Habe Testweise mein TestButton mittig der X-Achse vom Backgroundbild festgelegt und errechne anhand des Scale-Faktors die neue Position.


```
double buttonXPos=720;
double buttonYPos=643;
       
double newXPos = buttonXPos * scaleXFactor;
double newYPos = buttonYPos * scaleYFactor;
```

Und positioniere es neu auf das Hintergrundbild:


```
lblButton.setBounds((int)(newXPos)-27, (int)(newYPos), (int)(356 * scaleXFactor)+27,(int)( 141 *scaleYFactor));
```

Warum die -27 und +27?!

Komischerweise hat sich das Button immer weiter auf dem Hintergrundbild auf der X-Achse verschoben, je nach Skalierung.
Daraufhin habe ein MouseListener auf das Hintergrund-Label hinzugefügt und mit der Maus die X-Position des Buttons zu ermitteln. Obwohl durch das setBounds das Button genau mittig ,720 Pixel, sein sollte, war es laut MousListener auf Pixel 747. Durch die -27 blieb das Button jetzt genau mittig und die +27 für die Breite. Aber ab einer Skalierung über 1,5 stimmt die Breite des Buttons wieder nicht mir dem Hintergrund überein, darunter ist alles richtig.


----------



## Jardcore (1. Apr 2016)

Ist dein Button möglicherweise 54 Breit?  Du musst wahrscheinlich einfach immer die hälfte des Elements, in deinem Fall der Button, zusätzlich abziehen.


----------



## fresha (1. Apr 2016)

Nein, ist der Scale-Faktor = 1 dann hat das lblBackground die original Breite des Hintergrundbildes, also 1440 Pixel. 
In dem Fall hat das Button eine Breite von ((356 * scaleXfactor)+27) -> (356 * 1 + 27 = 383).


----------



## Jardcore (1. Apr 2016)

Kannst du vielleicht mal einen screenshot / mokup zeigen, damit man weiß was du ganz genau machen willst. Ich glaube mit einem vernünftigen Layout sollte man nicht mit so vielen MagicNumbers rumspielen müssen.


----------



## Thallius (1. Apr 2016)

Ich verstehe nicht wieso du da so kompliziert rechnest. Du brauchst wie gesagt ein Bild in einer festen Auflösung das Du mit Photoshop zusammen baust. Dann hast du die Größen und Positionen aller Buttons für die Auflösung dieses Bildes. Wenn du jetzt Größe und Position der Buttons über den Skalierfaktor änderst muss es passen. Aber wie Jardcore schon sagte must du natuerlich immer für die Mitte des Buttons rechnen und nicht für die obere linke Ecke.

Hast Du also ein Bild mit 1000 Pixeln (Ich betrachte jetzt nur die X-Achse, die Y-Achse ist ja identisch) und darauf einen Button genau zentriert der 200 Pixel breit ist, dann sitzt der bei (1000/2)-(200/2) = 400
Änderst du das jetzt auf 500, dann hast Du einen ScaleFactor von 0.5. Also
Position = (1000*0.5/2)-(200*0.5/2) = 200

Passt 

Claus


----------



## fresha (1. Apr 2016)

Von meinem Hausautomation-Projekt?
Ja, kann ich machen, aber leider erst wenn ich zu hause bin.


----------



## Thallius (1. Apr 2016)

Jardcore hat gesagt.:


> Kannst du vielleicht mal einen screenshot / mokup zeigen, damit man weiß was du ganz genau machen willst. Ich glaube mit einem vernünftigen Layout sollte man nicht mit so vielen MagicNumbers rumspielen müssen.



Das ist einfachster Dreisatz. Wenn das für dich schon MagicNumbers sind, dann schreib mal eine Fourier-Transformation 

Gruß

Claus


----------



## Jardcore (1. Apr 2016)

Ich zitiere hier mal Wikipedia, finde gerade den Abschnitt in Clean Code nicht 

_"Ein im Sourcecode eines Programms auftauchender Zahlenwert (auch engl. „hard coded value“ genannt), dessen Bedeutung sich nicht unmittelbar erkennen lässt – seine Bedeutung ist somit „magisch“. Derartige Magische Zahlen sind zu vermeiden und durch gut benannte Konstantendefinitionen zu ersetzen, deren Namen Bedeutung und Herkunft klar angeben"
https://de.wikipedia.org/wiki/Magische_Zahl_(Informatik)
_
Dabei ist egal ob es sich um einfachsten Dreisatz, die sehr lästige Fourier-Transformation oder das 1*1 handelt


----------



## Thallius (1. Apr 2016)

Jardcore hat gesagt.:


> Ich zitiere hier mal Wikipedia, finde gerade den Abschnitt in Clean Code nicht
> 
> _"Ein im Sourcecode eines Programms auftauchender Zahlenwert (auch engl. „hard coded value“ genannt), dessen Bedeutung sich nicht unmittelbar erkennen lässt – seine Bedeutung ist somit „magisch“. Derartige Magische Zahlen sind zu vermeiden und durch gut benannte Konstantendefinitionen zu ersetzen, deren Namen Bedeutung und Herkunft klar angeben"
> https://de.wikipedia.org/wiki/Magische_Zahl_(Informatik)
> ...



Und was hat die Benennung seiner Variablen nun damit zu tun das er keinen LayoutManager benutzt. Den kann er genauso a nennen.

Aber ich kann meine Berechnung natuerlich für Dich auch gerne Leserlicher gestallten, falls Du bei /2 nicht erkennst das es sich um die Hälfte handelt


```
newXPos = (getHalfFrom(imageBaseWidth)*scaleFactor)-(getHalfFrom(buttonBaseWidth)*scaleFactor);

public int getHalfFrom(int value)
{
        return value/2;
}
```


----------



## Jardcore (1. Apr 2016)

Das Benennen seiner Variable hat nichts mit dem Layoutmanager zu tun, trotzdem sollte sowas vermieden werden.
Deine Berechnung mit dem / 2 kam nach meinem Post, ergo habe ich mich nicht darauf bezogen. Sondern auf: _"((356 * scaleXfactor)+27) -> (356 * 1 + 27 = 383)."
_
Nichts desto trotz würde mir ein Schaubild helfen, dann könnte man sich ein gutes Layout überlegen.


----------



## fresha (1. Apr 2016)

@Thallius
Und auch mit deiner neuen Rechenformel muss ich mit der -+27 arbeiten. Gerade getestet.
Rein der X-Koordinate mit Button.getX() liegt das Button genau richtig, aber Optisch nicht.
Die Y-Achse und die höhe des Buttons habe ich erstmal außen vor gelassen.


```
lblButton.setBounds((int)(BGImageWidth*scaleXFactor/2)-(int)(200*scaleXFactor/2), 200,(int)( 200*scaleXFactor), 100);
```
    lblButton.setBounds((1440*0,5/2)-(200*0,5/2),200,200*0,5,100);
->lblButton.setBounds(310,200,100,100)


```
public void mouseClicked(MouseEvent e)
    {
        if(e.getSource()==lbl)
        {
            System.out.println("MausX= "+e.getX());
            System.out.println("ButtonX= "+lblButton.getX());
        }
     
    }
```

Ausgabe:
MausX= 337
ButtonX= 310

Der Mauszeiger steht dabei ganz rechts am Rand vom Button.

ps: Noch nicht zu hause, Screenshot vom Projekt folgt noch.


----------



## fresha (1. Apr 2016)

Ahh, Kommando zurück, ich depp.
Ok, geht doch alles. Ich habe einen Button in png-format geladen der links und rechts noch einen durchsichtigen Streifen hatte. 
Sorry.


----------



## Thallius (1. Apr 2016)

Und ich dachte schon Du hättest alle Mathematischen Axiome wiederlegt 

Gruß

Claus


----------



## fresha (1. Apr 2016)

So, wie versprochen die Screenshots.

Hier ist das Hintergrundbild mit Umriss vom Haus und Tabellen (liegt auf BorderLayout.CENTER):
 

Hier mit den Buttons und Schieber und die Fenster. Man soll die Fenster (rote Rechtecke am Hausumriss) sowie die Buttons anklicken können. Und ja, der Button heißt "Spanner", das Haus liegt direkt am Gehweg, daher eine Anti-Spanner-Funktion der Rollos 

 

Und vollständigkeitshalber das Menü (liegt auf BorderLayout.WEST)

 






[/IMG]





[/IMG]


----------

