wie füge ich ein JLabel in ein JFrame ein?

2 Antworten

Lies bitte erst einmal meine Antwort von hier: Java Var-Klasse schlimm?

Der Lernquelle, von der du das hast, solltest du nicht mehr folgen, denn resultierend daraus folgt in deinem Code nun leider eine schlechte Praxis der nächsten: Missbrauch von static, schlechte Bezeichner, inflexible Null-Layouts, unnötige repaint-Aufrufe, schlecht gefangene Exceptions, etc..

An sich ist die vLabel-Klasse auch nicht notwendig, wenn du lediglich ein Bild in einem Label haben möchtest. Im einfachsten Fall geht das ungefähr so:

try {
  BufferedImage image = ImageIO.read("image path ...");
  ImageIcon icon = new ImageIcon(image);
  yourLabel.setIcon(icon);
}
catch (IOException ex) {
  // error handling ...
}

Diese Zeile

Var.jf1.setVisible(true);

gehört folgend an das Ende des Konstruktors. Dann wird der Frame erst dann gezeichnet, wenn alle Komponenten zur Anzeige bereit sind.

Ich werde mir heute Abend noch einmal die Zeit nehmen, deine Klassen zu überarbeiten und das Resultat mit Erklärungen in meiner Antwort zu ergänzen.

RomanEngelhard 
Fragesteller
 08.09.2021, 15:24

Vielen Dank für die schnelle Antwort
schlussendlich sollten es dann schon mehr als nur ein Bild sein
jedoch möchte ich das Prinzip der verschiedenen Klassen verstehen.

Das wäre sehr nett wenn du das machen würdest :)

Und sorry ich kenne mich mit dieser Sprache sehr schlecht aus.

0
regex9  08.09.2021, 22:06
@RomanEngelhard

Also vorweg: Falls du mit Java Grundlagen (Arrays, Schleifen, Verzweigungen, u.ä.) sowie den Grundkonzepten der Objektorientierung (Kapselung, Vererbung, usw.) noch nicht vertraut sein solltest, wäre Swing definitiv nicht der richtige Einstiegspunkt, denn der Umgang mit diesem Toolkit setzt solche Kenntnisse schon voraus.

Ich werde folgend erst einmal einzelne Code-Bereiche deines obigen Programms herausgreifen und genauer darauf ausgehen, was daran falsch ist oder wie es besser geht. Auch anderweitige Tipps werde ich wohl einfließen lassen.

1) Tipp: Package Wildcards

In deinem Fall sind es zwar noch wenige Imports, aber wenn mehrere Komponenten hinzukommen, rafft sich das schnell zu einem Ballen zusammen. IDEs wie Eclipse oder IntelliJ klappen die Import-Zeilen automatisch zu, du kannst dir aber auch noch etwas Mühe einsparen.

Mit einem Wildcard Import brauchst du nicht mehr jede Klasse eines Package einzeln einbinden:

import javax.swing.*;

Hiermit werden alle Klassen aus dem javax.swing-Package angesprochen (Subpackages allerdings nicht!). Java ist selbst klug genug, später nur auf die Klassen zu referenzieren, die du auch wirklich im Code benutzt.

2) Var-Klasse

Im Großen und Ganzen bricht sie das komplette Konzept, auf dem Java / die OOP baut (bzw. führt genau die Problembereiche ein, die explizit durch die OOP aus der Welt geschafft werden sollen), indem sie einen zentralen Ablagehaufen für alles schafft, auf dem alles und jeder frei zugreifen kann. Die Ressourcen ballern zudem gleich von Anfang an den Programmspeicher zu und werden auch erst mit Programmende wieder entfernt.

Versuche nach Möglichkeit stets mit Objekten zu arbeiten. Statische (bzw. objektungebundene) Elemente machen tatsächlich nur in eher wenigen Fällen wirklich Sinn. Ein Objekt wird erst in den Speicher geholt, sobald notwendig und kann auch, wenn es nicht mehr benötigt wird, wieder abgeräumt werden.

Alle Felder und Methoden sollten in Klassen eingeordnet werden, die logisch zu ihnen passen. Das heißt, wenn du z.B. einen Datentyp Auto entwirfst, kommen Felder wie Farbe, Reifen oder Hubraum in die Klasse Auto. Das ermöglicht dir nicht nur eine intuitivere Programmierung (wenn du mit einem Auto-Objekt arbeitest, findest du die erwarteten Felder und Methoden an erwarteter Stelle), sondern auch ein generell übersichtliches Projekt. Stell dir nur vor, du würdest bei einem komplexeren Projekt alles weiter in Var hineinwerfen. Vielleicht hättest du dann irgendwann eine so aufgeblähte Datei, bei der deine IDE jedesmal stockt, wenn du sie öffnest.

Mit einer korrekten Einordnung und Kapselung lässt sich der Programmfluss außerdem besser nachverfolgen. Bei Elementen, auf die jedmögliche Programmstelle zugreifen kann, ist das wiederum schwieriger.

Konkret in deinem Fall reicht es aus, den Frame als lokale Variable in Gui zu kreieren.

JFrame frame = new JFrame("Project one");

3) Bezeichnerwahl

Du machst es dir selbst einfacher, wenn du Variablen (sowie Methoden, Klassen, etc.) stets aussagekräftig und eindeutig benennst. Abkürzungen wie jf1 oder lbl1 sind das nicht.

Problematisch wird es vor allem, würde man dieses System so fortführen. In einem Programm mit lbl1, lbl2, ... weißt du vielleicht, dass es sich hier um Labels handelt, doch welchen Zweck sie erfüllen sollten, ist nicht mehr ersichtlich.

4) Setup eines Frame

Im besten Fall wird die Größe eines Fensters dynamisch berechnet und der Nutzer hat die Möglichkeit, die Größe selbst zu bestimmen (bzw. zumindest zu einer Mindestgröße zu skalieren). Das macht sich vor allem bei unterschiedlichen Bildschirmgrößen bemerkbar: Auf Bildschirm A mag alles noch passen, doch auf Bildschirm B werden plötzlich Oberflächenelemente abgeschnitten und der Nutzer kann die Anwendung nicht mehr bedienen.

Dieses Problem taucht immer wieder auf (auch hier auf GF, erst heute morgen/gestern Abend gab es eine entsprechende Frage deswegen).

Schuld sind bei Swing-Anwendungen in der Regel diese Aufrufe / Konfigurationen:

Var.jf1.setSize(Var.screenw, Var.screenh);
Var.jf1.setResizable(false);
Var.jf1.setLayout(null);

Swing hat bestimmte Konzepte/Features, mit denen es diesen Problemen entgegenwirkt. Ein fester, zentraler Bestandteil sind die Layout Manager. Wenn man das Layout auf null setzt, nimmt man den besten Helfer aus dem Spiel, der verhindert, das Komponenten verschwinden, deplatziert werden, u.ä..

Insgesamt, das muss man zugeben, ist das Layouting mit Swing nicht so einfach. Mit bestimmten Layout Managern muss man sich, wenn man sie nutzen möchte, intensiver beschäftigen.

Eine bekanntere Alternative, die es vereinfachen kann, ist das MiGLayout (eine externe Bibliothek). Ansonsten könnte man auch einen Swing Builder nutzen (also alles mit Drag&Drop zusammenziehen). So ein Builder-Tool arbeitet in der Regel zwar mit Layout Managern, doch der generierte Code ist nicht hochklassig.

Das Anfordern des Fokus:

Var.jf1.requestFocus();

ist überhaupt nicht nötig, kann daher getrost raus.

Weiteres in den Folgekommentaren.

2
RomanEngelhard 
Fragesteller
 08.09.2021, 22:33
@regex9

Ich möchte mich schon jetzt herzlichst für deine detaillierte Antwort bedanken.

Das ist überhaupt nicht selbstverständlich aber ich schätze es sehr das du dein Wissen mit mir teilst.

Zu meinem Aktuellen Wissens-Stand:
Ich weiss nicht ob dir "Scratch " ein Begriff ist... jedoch ist es damit möglich Programme zu schreiben.
Ich kenne mich damit bestens aus und habe dadurch auch das Prinzip von Variablen und Methoden und vielem mehr, bestens im Griff.
Nun waren mit Scratch meine Möglichkeiten begrenzt und zudem wollte ich "richtig programmieren lernen.

Dazu habe ich mich hinter einige Toutorials gesetzt und nehme mir auch immer mal wieder Hilfe von www.w3schools.com

Da ich noch in Ausbildung bin (Mein Beruf hat nichts mit dem Interesse am programmieren zu tun) ist mein Budget sehr begrenzt und ich kann mich nicht für einen professionellen Kurs einschreiben.

0
regex9  08.09.2021, 23:17
@regex9

Beim Setup eines Frame würde ich stets mit angeben, was passieren soll, wenn man auf den X-Button (rechts oben) klickt. Mit dieser Konfiguration:

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

würde die Anwendung bspw. beendert werden.

5) Frame zeichnen

Grundsätzlich ist das Zeichnen eines Fensters ein etwas teurer Prozess. Oftmals muss nach dem Hinzufügen von Komponenten zu einem sichtbaren Frame dieser komplett neu gezeichnet werden.

Deshalb solltest du die setVisible-Methode (die das Zeichnen des Frames antriggert) erst aufrufen, sobald dein initiales Setup abgeschlossen ist (also alle Komponenten an den Frame gehängt wurden, die bei Start angezeigt werden sollen). So reduzierst du für diese Stelle die Anzahl an Zeichenvorgängen konstant auf 1.

6) Passende Exceptions fangen

Dein try-catch in Gui ist aus zwei Gründen unvorteilhaft. Zum einen ist es nicht notwendig, denn im try-Bereich gibt es keine Anweisung, von der man einen Ausnahmefall erwarten würde.

Zum anderen - angenommen, da würde nun etwas fliegen - würde catch eine etwas unspezifische Exception auffangen.

Fange nach Möglichkeit erst einmal immer nur die Exceptions, die du tatsächlich erwartest. Wenn du bspw. eine Datei öffnest, könnte man auf eine FileNotFoundException oder allgemeiner eine IOException (wegen Problemen beim Lesen / Zugriffsproblemen / o.ä.) spekulieren. Wenn du aber stattdessen eine unkonkrete Exception (wie Exception) für catch angibst, könnten dir auch andere Ausnahmefälle darunter geraten, die für dich versteckt bleiben und die du falsch behandelst.

7) Override-Annotation

Wenn du Methoden überschreibst, füge eine @Override-Annotation davor an.

Beispiel:

@Override
protected void paintComponent(Graphics graphics) {

Diese Annotation sagt dem Compiler: Hey, hier wird jetzt eine Methode überschrieben und der Compiler wird das daraufhin konkret prüfen: Passt die Signatur? Wird hier tatsächlich gerade eine Methode überschrieben?

Das ist für dich eine gute Hilfe, denn sollte dir ein Tippfehler o.ä. passieren, würdest du direkt einen Fehler an den Kopf geworfen bekommen, so, als hättest du einen Syntaxfehler gemacht. Wenn man die Annotation hingegen weglässt, erfolgt keine explizite Prüfung und Tippfehler o.ä. bleiben länger unentdeckt.

Zuletzt die Überarbeitung:

Gui.java:

package example;

import javax.swing.*;

public class Gui {
  public Gui() {
    JFrame frame = new JFrame("Projekt one");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    ImageLabel imageLabel = new ImageLabel();
    frame.add(imageLabel);

    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

Die pack-Methode berechnet selbst, welche Größen für die jeweiligen Komponenten notwendig sind. Das Label wird hier die gesamte Fläche des Fensters einnehmen, da es im BorderLayout (dem Standardlayout für JFrame-Komponenten) automatisch in das Zentrum positioniert wird, dem generell der größtmögliche Platz zukommt.

Und noch als Randbemerkung: Wenn die Größe des Fensters feststeht, kann auch seine Positionierung problemlos vorgenommen werden. Das wäre in diesem Fall das Bildschirmzentrum.

ImageLabel.java:

package example;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.JLabel;

public class ImageLabel extends JLabel {
  private static final long serialVersionUID = 1L;

  private BufferedImage image;

  private final int height;

  private final int width;

  public ImageLabel() {
    height = 800;
    width = 1200;

    try {
      image = ImageIO.read(new File("..."));
    }
    catch (IOException ex) {
      ex.printStackTrace();
      System.out.println("Image could not be loaded.");
    }
  }

  @Override
  public Dimension getPreferredSize() {
    return new Dimension(width, height);
  }

  @Override
  protected void paintComponent (Graphics graphics) {
    super.paintComponent(graphics);
    Graphics2D graphics2d = (Graphics2D) graphics;
    graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    graphics2d.drawImage(image, 0, 0, width, height, null);
  }
}

Wie schon in meiner Antwort oben geschrieben, geht das Setzen eines Bildes eigentlich viel einfacher. Dieses Beispiel zeigt nun also den umständlichen Weg, den ich für die Praxis nicht empfehlen würde.

Erwähnenswert wäre einmal die Methode getPreferredSize: Sie gibt dem Layout einen Hinweis, welche Größe optimal für die Komponente wäre. Wenn du konkrete Größen festlegen möchtest, wären get-/setPreferredSize meist die beste Wahl.

Das Bild wird im Konstruktor geholt, nur ein try-catch ist notwendig. Und wie du auch siehst: Es gibt keinen repaint-Aufruf. Der ist oft nicht nötig und daher oft auch ein Signalzeichen, das etwas schief läuft.

0
regex9  08.09.2021, 23:55
@RomanEngelhard

Ja, Scratch kenne ich. Java ist im Vergleich dazu natürlich ein weiter Schritt. Ich empfehle für einen Einstieg Richtung Java gern Processing, da es für den Anfang erst einmal die OOP etwas fern hält und Funktionen bietet, auf einer Zeichenfläche zu zeichnen.

Online kannst du auch noch diese kostenlosen Quellen nutzen:

Sie sind ausführlicher und detaillierter als das Tutorial von W3Schools.

Nachträglich kannst du dir die Features anschauen, die in den neuen Versionen dazugekommen sind. Es gibt da schon einige Artikel wie diesen, die diese Features auflisten. Größtenteils handelt es sich um Detailerweiterungen für die API und Runtime (sie sind also, gerade für Anfänger/Fortgeschrittene noch nicht so interessant, wie bspw. für professionelle Anwendungsentwickler). Eines der relevantesten Features (zu dem du dich etwas einlesen solltest) ist das Modulsystem, welches mit Java 9 eingeführt wurde.

Bezüglich Swing sind die Oracle Tutorials die sicherste Quelle, da kann ich die Aussagen von alfredo153 nur nachdrücklich bestätigen. Trotz des grauenhaften Designs der Seiten ist mir keine bessere, vertrauenswürdigere bekannt.

Ein paar Einzelartikel wurden inzwischen auf die neueren Oracle-Seiten verlagert. Da wäre hinsichtlich AWT/Swing dieser Artikel (Painting in AWT and Swing) für dich von Interesse. Solltest du jemals vorhaben, Spiele mit Java zu entwickeln, das noch vorweg, würde ich deutlich dazu raten, eine darauf ausgerichtete Bibliothek (wie libGDX) oder JME zu verwenden. AWT/Swing wurden nur für klassische GUI-Anwendungen mit Buttons, Labels, etc. konzipiert.

Außerdem noch eine Anmerkung zum Laden der Bilder: Dateien kann man eleganter in einem Ressourcenordner innerhalb des Projekts lagern. Das ist vor allem relevant, wenn du später einmal deine Programme auf andere Rechner exportieren möchtest. Dieser Artikel zeigt, wie es geht.

1
RomanEngelhard 
Fragesteller
 09.09.2021, 00:17
@regex9

Das ist unfassbar nett von dir

Das wird mir sehr helfen

Ich dachte das du filleicht ein Beispiels-Code hättest an dem ich mich einwehnig orientieren könnte und auch sehe wie richtig aussieht

Damit meine ich nur einen ganz kurzen, vergleichbar mit meinem(jFrame mit Label und eingefügtem bild)

Und wenn nicht oder du findest es wäre besser ich würde mich da zuerst besser einlesen dann mach ich das so.

Vielen lieben Dank

0
regex9  09.09.2021, 01:08
@RomanEngelhard

Nimm meine Überarbeitung der Gui.java und ersetze die ImageLabel-Zeile (ln 10) mit diesem Snippet:

JLabel imageLabel = new JLabel();

try {
  BufferedImage image = ImageIO.read(new File("image path ..."));
  ImageIcon icon = new ImageIcon(image);
  imageLabel.setIcon(icon);
}
catch (IOException ex) {
  // error handling ...
}

Dann hättest du schon ein ausreichend gutes Beispiel. Für das Laden der Datei via Ressource solltest du den oben verlinkten Artikel nutzen.

0

Erstens mal, und bitte nicht persönlich nehmen, der Code ist einfach von Anfang bis Ende grauenhaft - und dabei sogar viel länger als nötig. Das Gewurschtel mit dieser Var-Klasse voller statischer Variablen...wenn du das aus einem Tutorial hast (ich tippe auf YouTube), bitte hör sofort auf damit zu arbeiten. Da unterrichtet jemand, der selbst keine Ahnung hat.

Zu deiner Frage zurückkommend:

Ein minimales Programm, das einen JFrame mit einem JPanel drin anzeigt, könnte so aussehen (ohne Klassendeklaration und Imports rundherum, und ohne es in invokeLater() zu packen, was man in einem ernsthaften Programm tun sollte):

public static void main(String[] args) {
    JFrame mainFrame = new JFrame("My App");
    mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JLabel mainLabel = new JLabel();
    mainLabel.setOpaque(true);
    mainLabel.setPreferredSize(new Dimension(200, 200));
    mainLabel.setBackground(Color.CYAN);

    mainFrame.getContentPane().add(mainLabel);
    mainFrame.pack();
    mainFrame.setVisible(true);
}
RomanEngelhard 
Fragesteller
 08.09.2021, 15:48

Vielen Dank für die Antwort

Ja mittlerweilen ist mir dies bewusst...

Wie du schon gesagt hasst arbeite ich mithilfe eines YT-Videos. Es klang mir vielversprechend, da er dies laut seiner Aussage im Studium lerne.

Könntes du mir ein anderes Toutorial empfehlen?

0
alfredo153  08.09.2021, 15:52
@RomanEngelhard

Die beste kostenlose schriftliche Quelle ist und bleibt das Oracle-Tutorial, auf das ich hier schon verlinkt habe. Es ist nicht mehr ganz taufrisch, aber immer noch zuverlässig.

Videos kenne ich keine brauchbaren. Java-Bücher beschäftigen sich meist nicht viel mit Swing, eine Ausnahme ist das Inselbuch, das ich ansonsten nicht sonderlich gut finde - aber eine (alte) Ausgabe gibt's gratis online: https://openbook.rheinwerk-verlag.de/javainsel/17_004.html#u17.4

1