Könnt ihr mir bei einem Javaprojekt helfen?

1 Antwort

Es gibt an verschiedenen Stellen Veränderungspotenzial. Ich gehe einmal durch, was mir so auffällt.

1) Bleibe bei den Bezeichnern bei einer Sprache. Entweder alles in Englisch oder alles in Deutsch. Ein Sprachmix wie bei BilderVerwaltungMainClass ist unschön.

Wenn man davon ausgeht, dass das Programm später noch komplexer wird, wäre es wohl auch sinnvoll, funktional mehr aufzuspalten. So kann es eine Model-Klasse geben, die die Daten (das DefaultListModel) liefert und eine Klasse, die nur den View-Code beinhaltet. Zweitere kann dann eine Instanz ersterer Klasse aufnehmen, um sich mit den Daten zu befüllen.

2) Eine Vererbung von JFrame ist in der Regel nicht notwendig, so lange du dessen Methoden nicht überschreiben möchtest (was ja auch nicht der Fall ist). Ich würde im Konstruktor stattdessen einfach eine JFrame-Instanz anlegen und die dann mit den Komponenten, etc. ausstatten. Der Vorteil dabei ist, dass es deiner Klasse dann wieder offensteht, von einer anderen Klasse zu erben, falls notwendig.

3) Brauchst du den Content Pane in verschiedenen Methoden? Dein Feld deutet es zumindest an, doch praktisch verwendest du das Objekt nur im Konstruktor.

4) Auf statische Felder würde ich nach Möglichkeit verzichten. Jedes Objekt bekommt durch objektgebundene Elemente seinen eigenen Zustand und könnte demzufolge auch entscheiden, ob es Dateien/Verzeichnisse aus einem Ordner test oder einem Ordner xyz holt. Nach dem Data-Hiding-Prinzip wäre das Feld zudem private oder protected.

5) Deinem Feld probe fehlt ein expliziter Zugriffsmodifikator und generell würde ich seine Existenzberechnung hinterfragen (du verwendest es ja auch nicht). Es speichert immerhin nur einen Zustand, der besteht, sobald das Objekt angelegt wird. Der Zeitpunkt, an dem du den Wert aus diesem Feld nutzt, kann später sein, wo sich der Zustand der Datei selbst womöglich schon längst wieder geändert hat.

6) Streich diese Aufrufe:

setBounds(100, 100, 644, 432);
// ...
contentPane.setLayout(null);
// ...
listDateienListung.setBounds(413, 111, 207, 274);

am besten ganz schnell wieder aus deinem Repertoire.

Auf diese Weise läufst du komplett gegen das Prinzip von Swing, Layouts zu definieren. Was auf deinem Bildschirm für den Moment richtig aussehen mag, sorgt bei einem anderen Nutzer schon wieder für ziemliche Probleme. Generell ist es wohl der meistgemachte Fehler bei Swing.

Verwende Layout Manager, um Komponenen anzuordnen. Da du derzeit nur eine Komponente hast, würde ein BorderLayout (Standardlayout des JFrame) schon völlig genügen.

So in etwa:

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(644, 432));

JPanel panel = new JPanel();
panel.setBorder(new EmptyBorder(5, 5, 5, 5));

JList fileList = new JList();
fileList.setPreferredSize(new Dimension(207, 274));
panel.add(fileList);

frame.add(panel);
frame.pack();
frame.setLocation(100, 100);
frame.setVisible(true);

7) Diese Zeile:

System.out.println();

kann sicherlich aus dem Konstruktor raus.

8) Nun zum DefaultListModel. So ein Model kann recht leicht zu einer JList zugeordnet werden:

DefaultListModel<String> model = new DefaultListModel<>();
JList<String> fileList = new JList<>(model);

Günstig wäre es an der Stelle, String-Objekte in das Model einzutragen, denn du möchtest ja sicherlich nur die Pfade / Dateinamen angezeigt bekommen.

Wenn du dem Model nun Werte zufügst, wird die JList automatisch aktualisiert.

model.add(0, "hello");
model.add(1, "world");

Statt die Werte direkt einzeln einzutragen, könntest du ebenso eine der addAll-Überladungen nutzen.

List<String> words = new ArrayList<>();
words.add("hello");
words.add("world");
model.addAll(words);

Deine Lesemethode könnte also eine Liste liefern und die wird dem Model zugefügt.


thshot12345 
Fragesteller
 10.01.2022, 20:49

Guten Abend, ich will mich erstmal für deine Hilfe bedanken und besonders dafür, dass die so umfangreich ist. Ich bin allerdings noch am Anfang, deshalb denke ich kann ich mit vielem davon noch nichts anfangen. Mein Ziel ist es die Dateien über eine Funktion DatenLesen() mit einem DefaultModel anzeigen zu lassen. Wenn ich das jetzt in der JList anzeigen lassen wollen würde, habe ich deine Hilfestellung wie folgt verstanden:

ich lege die Funktion datenLaden() global an und erstelle eine JList mit dem Namen model.Um den Ordner in der JList anzeigen zu lassen, gebe ich die Konstante File ordner an die JList weiter und ordne sie zu.

Ist das so richtig?

public void datenLaden() {

DefaultListModel<String> model = new DefaultListModel<>();

JList<String> fileList = new JList<>(model);

model.add(ordner);

}

Von meinem Dozent wurde mir allerdings vorgelegt, dass ich mit der Variable DefaultListModel<Datei> an die Sache herangehen muss und für das Anzeigen des Ordnerinhaltes, also alle Dateien innerhalb eines Ordners, die Methode listFIles() nutzen muss.

Ich hoffe, dass sich das mit der Zeit ergibt. Das verwirrt mich echt wirklich.

0
regex9  10.01.2022, 21:52
@thshot12345
Ist das so richtig?

Nein, noch nicht. Beachte zum einen die geforderten Parameter von add (Index und Wert). Zum anderen würdest du dir ein Problem schaffen, wenn du deine JList in dieser Methode deklarierst. Die Methode würde plötzlich zwei Aufgaben erfüllen (JList anlegen und Daten laden), was ihr Name nicht verspricht. Außerdem hättest du keine logische Trennung zwischen View-Code (Anlegen der JList-Komponente) und Model-Logik (Datensammeln). Denk nur einmal an den Fall, dass in deinem Programm noch irgendjemand anders mit dieser Methode die Daten laden lassen wollte. Bei jedem Aufruf würdest du ungewollt noch eine JList-Komponente anlegen.

Ich würde eine dieser drei Möglichkeiten vorschlagen:

a) Die Methode gibt ein DefaultListModel zurück.

private DefaultListModel<String> ladeDaten() {
  DefaultListModel<String> model = new DefaultListModel<>();
  // collect and add data ...
  return model;
}

b) Du legst ein Feld für das DefaultListModel an und befüllst es in der Methode.

private DefaultListModel<String> model;

private void ladeDaten() {
  model.clear();
  // collect and add data ...
}

Wobei es ebenso mit einem Parameter, statt mit einem Feld klappen würde:

private void ladeDaten(DefaultListModel<String> model) {
  model.clear();
  // collect and add data ...
}

c) Du gibst mit der Methode eine Liste zurück, die du später (im Kontext des Aufrufers) dann via addAll an ein DefaultListModel übergibst.

private List<String> ladeDaten() {
  List<String> data = new ArrayList<>();
  // collect and add data ...
  return data;
}

Der Vorteil von Variante c) ist, dass diese Implementation am flexibelsten wäre. Nicht jeder potenzieller Aufrufer der Methode kann mit einem DefaultListModel etwas anfangen. Er soll selbst entscheiden können, ob er die Daten in einen weiteren Container einlagert oder ob ihm die einfache Liste genügt.

Von meinem Dozent wurde (...) vorgelegt, dass ich mit der Variable DefaultListModel<Datei> an die Sache herangehen muss (...)

Dann brauchst du noch eine Klasse Datei, die beispielsweise den Namen der Datei speichert. In dieser sollte die toString-Methode überschrieben werden, die eine lesbare Form des Objekts liefert. Das kann also eine textuelle Beschreibung des Objekts sein. Vielleicht der Dateiname, vielleicht der Pfad, die Dateigröße, o.ä. - das entscheidest du.

@Override
public String toString() {
  // ...
}

Die JList-Komponente geht für die visuelle Darstellung ihrer Einträge intern durch jeden Eintrag und versucht von diesem eine String-Repräsentation zu bekommen (indem sie deren toString-Methode aufruft). Ein Objekt Datei lässt sich immerhin visuell schlecht darstellen, eine textuelle Beschreibung hingegen eher. Wenn du die toString-Methode nicht überschreibst, wird eine Standardimplementation der Object-Klasse verwendet. Nach dieser Implementation würde der Typname des Objekts mitsamt einer Hash-ID ausgegeben werden.

0
thshot12345 
Fragesteller
 10.01.2022, 22:19
@regex9

Ich habe mich jetzt für die erste Methode entschieden und habe die Methode datenLaden() nach dem Konstrukter MainClass aufgesetzt.

Allerdings startet weder System.Out.Println, noch nimmt meine GUI das an. Das klappt bei mir nur, wenn ich das in den Main Konstrukter setze.

Wenn ich den Ordner zum Auslesen nicht über model.add(ordner); an die JList weitergeben kann, wie müsste der Code denn dann aussehen? Ich dachte, dass das über addElement geht, aber das hat auch zu Problemen geführt.

Um genau zu sein weiß ich jetzt nicht wie ich es hinbekomme, dass der Ordnerinhalt in der JList angezeigt wird.

Ich habe den verwirrten Code nochmal angehangen.

Auf jeden Fall vielen Dank!

package bilderverwaltung;

import java.awt.EventQueue;

import java.io.File;

import javax.swing.DefaultListModel;

import javax.swing.JFrame;

import javax.swing.JList;

import javax.swing.JPanel;

import javax.swing.border.EmptyBorder;

public class BilderVerwaltungMainClass extends JFrame {

private JPanel contentPane;

public File ordner = new File("test");

public static void main(String[] args) {

EventQueue.invokeLater(new Runnable() {

@Override

public void run() {

try {

BilderVerwaltungMainClass frame = new BilderVerwaltungMainClass();

frame.setVisible(true);

} catch (Exception e) {

e.printStackTrace();

}

}

});

}

public BilderVerwaltungMainClass() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setBounds(100, 100, 644, 432);

contentPane = new JPanel();

contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));

setContentPane(contentPane);

contentPane.setLayout(null);

JList list = new JList<Datei>();

list.setBounds(404, 161, 216, 224);

contentPane.add(list);

ladeDaten();

}

private DefaultListModel<String> ladeDaten() {

DefaultListModel<String> model = new DefaultListModel<>();

File[] files = ordner.listFiles();

Hier hätte ich jetzt addElement(ordner oder FIle) gemacht, aber das ist nicht möglich.

return model;

}

}

0
regex9  10.01.2022, 22:53
@thshot12345

Die println-Methode ist für Swing erst einmal irrelevant. Sie gibt Text ausschließlich auf der Konsole aus. Deine Anwendung operiert aber über grafische Oberflächenelemente, nicht mehr über die Konsole.

Deine Methode zum Laden der Daten musst du auf jeden Fall irgendwann (z.B. im Konstruktor) aufrufen, denn von allein geschieht das nicht. Und da die Methode ihr Ergebnis zurückgibt, muss dieses bei Aufruf auch aufgefangen und verwertet werden.

DefaultListModel<Datei> model = ladeDaten();
JList<Datei> list = new JList<>(model);

Achte darauf, überall die richtigen generischen Typen einzusetzen. In meinen Beispielen bin ich bisher immer davon ausgegangen, dass nur einfache Strings in die Liste sollen. Da deine Vorgabe allerdings der Typ Datei ist, ändert sich das.

Ich dachte, dass das über addElement geht, aber das hat auch zu Problemen geführt.

Was spricht denn dagegen, die add-Methode so einzusetzen, wie ich es in meiner Antwort oben bereits gezeigt habe? Der einzige Unterschied ist, dass das zweite Argument ein Datei-Objekt sein muss.

Du musst mit einer Schleife durch all deine gefundenen File-Objekte laufen, je File-Objekt ein Datei-Objekt erstellen und dieses via add in das Model einfügen.

Ich denke im Übrigen, du solltest dich nochmal diesen Themen explizit auseinandersetzen: Methoden und Generics.

0
thshot12345 
Fragesteller
 11.01.2022, 00:08
@regex9

//Was spricht denn dagegen, die add-Methode so einzusetzen, wie ich es in meiner Antwort oben bereits gezeigt habe? Der einzige Unterschied ist, dass das zweite Argument ein Datei-Objekt sein muss.//

Wenn ich über model.add(0, datei.txt); etwas einfügen will, wird das bei mir als ein Fehler angezeigt. Ich werde morgen einfach mal versuchen diesbezüglich noch eine Lösung zu finden, mit ein bisschen Forschung werde ich das schon hinbekommen. Ich werde dann auch noch eine For-Schleife versuchen, bzw. die richtige Schleife suchen.

Ich werde mich komplett mit Java nochmal befassen müssen. Ich mache eine Ausbildung zum Fachinformatiker und bin erst in den ersten Wochen, wir haben gerade erst vor zwei Wochen mit Java Grundlagen angefangen.

Ich danke dir echt vielmals, ich wollte dir damit echt nicht auf die Nerven gehen :-)

0
regex9  11.01.2022, 02:18
@thshot12345

datei.txt wäre ein Fehler, denn das wäre ja ein Symbol, mit dem Java nichts anfangen kann. Der Compiler schaut sich diese Zeichenfolge an und denkt, datei wäre ein Objekt und txt ein Feld dieses Objekts (Stichwort: Punktnotation). Er kann aber beides nicht auflösen, denn beides wird nirgendwo definiert.

Du bräuchtest wie gesagt eine Klasse namens Datei. Ganz plump könnte die so aussehen:

public class Datei {
  private String pfad;
  
  public Datei(String pfad) {
    this.pfad = pfad;
  }

  @Override
  public String toString() {
    return pfad;
  }
}

Im Code aus deiner Fragestellung hast du die Methode zum Lesen der Dateien schon zu großen Teilen fertig. Du darfst die Daten nur halt nicht via println ausgeben, sondern du müsstest dir an der Stelle ein Objekt der Klasse Datei anlegen und dieses mit den Informationen ausstatten. Bei meinem Beispiel wäre es nur der Dateipfad, doch vielleicht könnte man noch weitere Informationen dazunehmen (Dateiname, Dateigröße, o.ä.). Danach fügst du das Objekt einfach nur dem Model hinzu.

String pfad = dateien[i].getAbsolutePath();

if (!dateien[i].isDirectory()) {
  modell.add(i, new Datei(pfad));
}

(Da von dir eine Klasse Datei verlangt wird, habe ich die Bezeichner hier alle in Deutsch gehalten. Ein Sprachmix wäre wie schon gesagt unschön)

Sofern du noch nach guten Lernmaterial für Java suchen solltest, hilft dir eventuell meine Antwort von hier. Bezüglich AWT/Swing gibt es leider viele Quellen (z.B. etliche Videotutorials), die zeigen, wie man es gerade nicht machen sollte. Deshalb betrachte ich die Oracle Tutorials an der Stelle als die beste, vertrauenswürdigste Quelle.

0
thshot12345 
Fragesteller
 11.01.2022, 08:38
@regex9

Das könnte definitiv, helfen ich bin noch fleißig dabei und das Projekt ist auch noch nicht fertig. Ich hoffe auch, dass ich den Code wenigstens Ansatzweise nach der Aufgabenstellung aufgebaut habe. In einem Java-Forum wurde mir gesagt, dass ich eigentlich neu anfangen kann, weil alles falsch ist. Ich denke mal darauf ist einfach nicht zu hören.

Thema

P1 Projekt anlegen In Eclipse, legen Sie ein neues Projekt namens "BilderVerwaltung" an, mit einem Package und einem Fenster (JFrame) als Hauptklasse (mit main()) - Sie dürfen WindowBuilder benutzen, müssen aber nicht.

Prüfen Sie, dass das Hauptfenster sich öffnet, eine vernünftige Größe hat, und dass das Programm beendet wird, wenn man es schließt (setDefaultCloseOperation(EXIT_ON_CLOSE)).

P2 Testdaten besorgen Im Projektordner, legen Sie einen neuen Unterordner namens "test" an. Laden Sie einige Bilder aus dem Internet herunter und speichern Sie sie in diesen Ordner ab. Fügen Sie noch einige Dateien hinzu, die keine Bilder sind (z.B. Textdateien). Hinweis: Java kann die Formate .png und .jpg (oder .jpeg) lesen, aber nicht .webp und nicht immer .gif. Wählen Sie Ihre Testdaten entsprechend.

In Eclipse, drücken Sie auf F5 um den neuen Ordner zu sehen.

P3 Datenklasse definieren Definieren Sie eine Klasse "Datei", mit einer Variable "file", die ein java.io.File enthält, und einem Konstruktor, der diese über ein Parameter initialisiert.

Die Idee dabei ist, ein File-Objekt (das einen Pfad zu einer Datei darstellt) in jede Instanz dieser Klasse zu "verpacken" und zusätzliche Methoden zu definieren, die mit diesem File-Objekt umgehen und zusätzliche Informationen liefern. Theoriefrage: mit welchem Mechanismus der Objektorientierung würde man normalerweise so eine Erweiterung programmieren? Sie dürfen Sich aussuchen, ob Sie diesen Mechanismus benutzen möchten; der Aufgabentext bleibt dabei, dass die Klasse "Datei" ein java.io.File in einer Variable namens "file" enthält.

P4 Konstante definieren In der Hauptklasse, definieren Sie eine Konstante namens "ORDNER" vom Typ java.io.File, die den Pfad zum Testordner enthält. Wenn dieser Ordner in ihrem Projektordner liegt, enthält der Pfad nur seinen Namen (ohne / oder \).

P5 Daten laden Programmieren Sie eine Funktion dateienLaden(), die ein DefaultListModel<Datei> zurückliefert, die den Inhalt des Testordners darstellt (ein Exemplar der Klasse "Datei" pro Datei).

Ist diese Funktion static?

Sie dürfen selber entscheiden, in welcher Klasse (Main-Klasse oder Klasse "Datei") Sie sie definieren.

Um den Inhalt eines Ordners zu lesen, benutzt man seine Methode listFiles().

P6 Daten anzeigen Im main(), bevor das Hauptfenster geöffnet wird, rufen Sie die Funktion dateienLesen() und speichern Sie ihr Ergebnis (die Liste der Dateien als DefaultListModel) in eine sinnvolle Variable.

Fügen Sie eine JList ins Hauptfenster hinzu. An der Stelle, wo diese JList initialisiert wird, sorgen Sie dafür, dass Sie das DefaultListModel der Dateien als Model bekommt.

Testen Sie. Ups, wir müssen das Aussehen verbessern.

P7 toString() überladen In der Klasse "Datei", überladen Sie die Methode toString() so, dass nur der Name der Datei angezeigt wird. Den bekommt man aus dem java.io.File mit seiner Methode getName().

P8 Methode programmieren In der Klasse "Datei", definieren Sie eine Methode "istBild()", die true zurückliefert, wenn es eine Bilddatei ist, und false wenn nicht.

Bilddateien werden an ihrer Endung erkannt: ".png", ".jpg", ".jpeg", ".PNG", ".JPG" oder ".JPEG" sind Bilder.

Ob eine Zeichenkette eine bestimmte Endung hat oder nicht, testet man mit String.endsWith(...).

P9 Anzeige verbessern In der Klasse "Datei", ergänzen Sie die Methode toString() so, dass Bilddateien mit einem * vor dem Dateinamen angezeigt werden, und die anderen Dateien mit einem Leerzeichen. Benutzen Sie dafür die Methode istBild().

Testen Sie.

P10 Bild anzeigen Im Hauptfenster, fügen Sie ein JLabel hinzu. Bei der Initialisierung der JList, registrieren Sie einen ListSelectionListener und lassen Sie von Eclipse oder von WindowBuilder den entsprechenden Code generieren. Dieser ListSelectionListener soll dafür sorgen, dass:

- Wenn eine Datei in der JList markiert ist, und diese Datei ein Bild ist, dieses Bild ins JLabel angezeigt wird,

- Wenn keine Datei in der JList markiert ist, oder die markierte Datei kein Bild ist, das JLabel nichts anzeigt.

Ein Bild lässt man in einem JLabel mit Hilfe von einem ImageIcon anzeigen; suchen Sie den Konstruktor von ImageIcon, der eine Bilddatei lädt.

P11 Bildgröße anzeigen Im Hauptfenster, fügen Sie ein JLabel hinzu, der die Pixelgröße (Breite x Höhe) des ausgeẅahlten Bildes anzeigt, oder den Text "kein Bild", wenn kein Bild ausgewählt ist. Diese Informationen bekommt man vom ImageIcon.

P12 Bildreduzierung Nach dem Laden des Bildes in einem ImageIcon, benutzen Sie ImageIcon.getImage().getScaledInstance(), um eine kleinere Version des Bildes zu gewinnen, und zeigen diese im JLabel an (mit einem zweiten ImageIcon). Die Skalierung muss die Proportionen beachten!

0