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.
ich programmierer stets weiter an meinem GUI Projekt und der Code wird immer mehr und mehr.
Ich habe eine JFrame Klasse mit etlichen JList, JButtons, JFreeChart usw usf.
Ebenfalls in meiner JFrame befindet sich mein Actionlistener als innere Klasse der so gut wie alles behandelt, was passiert wenn ein Button geklickt wird oder wenn ein Mauszeiger über ein bestimmtes Fenster geht.
Der Code beläuft sich jetzt schon über 600 Zeilen, das alles ist im meiner Frame Klasse beinhaltet. Also es wird von Tag zu Tag voller.
Habt ihr tipps für mich, wie ich das besser handeln könnte? Ich habe mir schon gedanken gemacht das komplett in einer neuen Klasse zu realisieren, aber ich befürchte ich muss jedes mal ziemlich viele Referenzen an die Klasse aus dem Frame an meine neue Klasse übergeben.
Der JFrame ( Rahmen ) sollte lediglich die Hauptkomponente enthalten, welche sich aus vielen Weiteren Komponenten zusammensetzt. ( JPanel / JComponent )
(Kapselung von Zuständigkeit wäre da ein Stichwort )
Der Code beläuft sich jetzt schon über 600 Zeilen, das alles ist im meiner Frame Klasse beinhaltet. Also es wird von Tag zu Tag voller.
maximal 500 Zeilen pro Datei würde ich als Richtlinie erachten, gestalte es Atomarer.
Ebenfalls in meiner JFrame befindet sich mein Actionlistener als innere Klasse der so gut wie alles behandelt, was passiert wenn ein Button geklickt wird oder wenn ein Mauszeiger über ein bestimmtes Fenster geht.
hier ebenfalls, nicht der JFrame sollte den Listener enthalten, sondern der content.
du kannst auch mehrere Listener schreiben die dann für die Unterschiedlichen Events zuständig sind, anstatt alles in einen Listener zu stopfen.
Entweder als Innere Klasse oder direkt als Separate Klassen ( auch wieder Kapselung von Zuständigkeit )
Also würdest du die grafische Gestaltung, zb. durch einen Layoutmanager komplett außerhalb des Frame beschreiben, was nichts mit dem MainFrame zu tun hat?
Generell würdest du jedem Button eine eigene Klasse zusteuern oder alle Buttons in eine Klasse stopfen?
Also würdest du die grafische Gestaltung, zb. durch einen Layoutmanager komplett außerhalb des Frame beschreiben, was nichts mit dem MainFrame zu tun hat?
Nicht Zwingend, wenn du z.B eine Gui aufbauen möchtest und dir das BorderLayout genügt, dann kannst du diese Bereiche ruhig nutzen, falls nicht würde ich eher den weg nutzen und eine Zentrale Klasse erstellen die deine Gui repräsentiert und alle Komponenten enthält um deine GUI darzustellen..
Grundsätzlich solltest du aber immer mit Layoutmanagern arbeiten. [WR]( Null-Layouts are Evil )[/WR]
Generell würdest du jedem Button eine eigene Klasse zusteuern oder alle Buttons in eine Klasse stopfen?
nope, ich würde z.B jedem Panel einen Listener mitgeben , der für dessen Komponenten zuständig ist.
Angenommen du hast z.B einen Bereich welcher bestimmte Steuerungselemente in Form von JButtons enthält, dann würde ich hierfür eine Klasse erstellen welche dies Buttons enthält und für die Darstellung zuständig ist, sprich ein Jpanel ( z.B ButtonPane) und diese dann dem jeweiligen Bereich der Borderlayouts des Frames zuweisen , z.B an die Position : BorderLayout.PAGE_START;
Ich bin es nochmal. Beim Versuch die Listener/Eventhandlings auszulagern stoße ich immer wieder auf die Tatsache, dass es viel mühsamer ist, für jede Klasse einen Konstruktor zu schreiben der mit den etlichen Componenten erst mal zu recht kommen muss, damit der Austausch zwischen den Componenten in meinem Frame mit den Listener zustanden kommen kann.
Du kannst die Listener einfach als innere Klassen in deinen Komponenten definieren, wenn z.B eine Klasse die du von JPanel ableitest mehrere Komponenten beinhaltet, dann kannst du aus der inneren Klasse ( welche den Listener darstellt ) direkt auf Attribute der umschließenden Klasse zugreifen.
ganz grob umrissen :
Java:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class ListenerExample extends JPanel{
private JButton btn;
public ListenerExample(){
setPreferredSize(new Dimension(120,120));
btn = new JButton("Klick mich");
btn.addActionListener(new ExampleListener());
add(btn);
}
class ExampleListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
if(e.getSource() == btn) {
System.out.println("Button");
}
}
}
public static void main(String[] args){
JFrame f = new JFrame();
f.setContentPane(new ListenerExample());
f.pack();
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
oder falls du z.B viele Aktionen an mehreren stellen nutzen möchtest könntest du auch direkt Actions definieren ( welche von AbstractAction abgeleitet werden ) und du sie dann entweder als MenuItem behandeln oder auch als Event einem Button zuordnen könntest.
Java:
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
public class CloseAction extends AbstractAction{
private static final long serialVersionUID = 1L;
private JFrame frame;
public CloseAction(JFrame frame) {
super("Beenden");
this.frame = frame;
}
@Override
public void actionPerformed(ActionEvent e) {
frame.dispose();
}
}
Bei MouseListener oder auch MouseMotionListener sollten diese immer der Komponente "lauschen" welche du als content behandelst, also in deinem Frame sitzen hast.
in deinem Beispiel ist die Main-Methode innerhalb der JPanel Klasse. Bei mir ist die Main Methode in einer eigenen Klasse, komplett isoliert.
Und da fängt das Problem an, ich habe ein Frame Klasse(abg. von JFrame) die erstellt im Konstruktor alle Buttons/JList/JCheckbox ect.. Dort wird auch direkt das Aussehen beschrieben. Sonst liegt nur noch eine innere ActionListener Klasse vor. Die ist, wie du weisst zu voll geworden.
Beim Versuch einen ActionListener zu schreiben der außerhalb des Frame liegt, müsste ich dem Konstruktor ziemlich viel übergeben, min. 10-15 Componenten. Auf mich wirkt das sehr unelegant.
Dann habe ich mir deinen letzten Post durchgelesen, und versucht eigene Buttonklassen zu schreiben die jeweils eine eigene innere Actionlistener-Klasse haben. Da habe ich das Problem dass ich es irgendwie nicht hinbekomme, die Speicherrefernzen so zu übergeben dass der Actionlistener reagiert.
Jetzt bin ich mittlerweile soweit, verschiedene Listener-Klassen zu erstellen.
Gibt es da einen richtigen Weg!?
Lieber 2-4 Listener klassen zu schreiben, um das übersichtlicher zu gestalten.
Oder jeweils eine eigene JList,Jcheckbox, JButton usw. Klasse? Ich glaube zweiteres wäre um einiges umfangreicher und ob es sinniger ist, kann ich noch nicht abschätzen.
Wenn es dir weiter hilft meine Frage besser zu verstehen, kann ich auch etwas code posten.
EDIT: Danke für den Code, damit konnte ich nahezu mein Fragezeichen beantworten hinsichtlich der Übergabe der Referenzen an eine eigene Button Klasse.
Nur frage ich mich gerade was du anders gemacht hast als ich zuvor!? Ich werde mal schauen, danek dir
EDIT2: Wenn ich mich recht erinnere hat es deswegen nicht geklappt weil ich von der falschen Klasse abgeleitet habe, entweder von JButton oder JFrame.
in deinem Beispiel ist die Main-Methode innerhalb der JPanel Klasse. Bei mir ist die Main Methode in einer eigenen Klasse, komplett isoliert.
das macht , insofern du richtig kapselst keinen unterschied wo diese sich befindet, sie sollte selbstverständlich immer in einer eigenen klasse stehen welche als einstiegspunkt deiner Anwendung dient.
ich schreibe dir fix ein Beispiel , warte eben
Also hier mal ohne umsetzung durch das MVC Pattern, welches ich dir aber dringend nahelege, alleine schon um die Komponenten voneinander getrennt zu halten ..
Gui.java
Java:
import javax.swing.JFrame;
public class Gui{
public static void main(String[] args){
JFrame f = new JFrame("Beispiel");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setContentPane(new MainPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
MainPanel.java
Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
public class MainPanel extends JPanel{
private static final long serialVersionUID = 1L;
private LeftPanel left;
public MainPanel(){
setPreferredSize(new Dimension(500,500));
setLayout(new BorderLayout());
left = new LeftPanel();
add(BorderLayout.LINE_START, left);
setListenerToComponents();
}
private void setListenerToComponents(){
left.setListener(new PanelListener(this));
}
class PanelListener implements ActionListener{
private JPanel main;
public PanelListener(JPanel main){
this.main = main;
}
@Override
public void actionPerformed(ActionEvent e){
if(e.getSource() == left.getButton()) {
main.setBackground(Color.BLUE);
}
}
}
}
LeftPanel.java
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class LeftPanel extends JPanel{
private static final long serialVersionUID = 1L;
private JButton btn;
public LeftPanel(){
setPreferredSize(new Dimension(120,150));
setBackground(Color.RED);
btn = new JButton("Linker Button");
add(btn);
}
public void setListener(ActionListener l){
btn.addActionListener(l);
}
public JButton getButton() {return btn;}
}
Hier befindet sich der Button über den der Hintergrund des Contents des MainPanel gesetzt wird , in der Klasse LeftPanel, in der Klasse MainPanel wird diese Initialisiert und diese enthält auch die innere Listener Klasse die sich darum kümmert was der Button letztendlich macht, dazu übergebe ich hier dem Listener eine Referenz auf den MainPanel ( this )
probiere es aus, falls du Fragen hast immer raus damit , aber wie gesagt ließ dich in das MVC / MVP Pattern ein , welches alles in verschiedene Schichten ( Model / Presenter | Controller / View ) aufteilt.
Erst mal vielen Dank dass du dir soviel Mühe gibst!
Nun habe ich eine Frage, dank deiner Hilfe könnte ich beide "Konzepte" umsetzen. Also entweder ich erschaffe mir zwei-drei ActionListener/MouseListener/MouseMotionListener und gebe den jeweiligen Buttons den jeweiligen Listener mit und habe endlich die ganzen Listener aus meiner Frame-Klasse raus. Oder ich mach es wie du es im letzten Beispiel gezeigt hast, für ein paar Compenten(JButton,JList usw) ein paar abgeleitete Klassen und implementiere dort die Eventhandlings.
Wie ist das aus der Sich des MVC? Ich blicke noch nicht ganz durch...
Das ist nie so ganz klar , letzten endes geht es darum die verschiedenen Schichten zu trennen, so hat z.B die Datenschicht ( also das Model ) keine Kenntnis darüber wie diese daten dann über den View dargestellt werden, Der Controller übernimmt in diesem Fall das Handling (Eventhandling), wenn du noch nicht sehr mit der OOP vertraut bist solltest du dich auch erst in die ganzen Grundlagen, und dann in Design Pattern einlesen, da das MVC Pattern quasi der König unter den Mustern ist und sich aus verschiedenen Mustern zusammen setzt. ( Strategy-Kompositum-Observer ).
Dazu ist zu sagen das das einsetzen von "Mustern" nicht immer sinnvoll ist sondern nur da wo man weiß das später viele Änderungen und oder Erweiterungen stattfinden.
Für kleine Projekte lohnt sich das meinstens nicht wirklich da es die Anzahl an Klassen / den Source Code unnötig aufbläht und es von der Struktur her wesentlich komplizierter macht.
Wie immer gibt es auch hier mehrere Möglichkeiten dieses Pattern umzusetzen, einmal über das implementieren von Observer / ableiten von Observable, oder durch das umsetzen des ganzen über Listener
Ich weiß das es mit Sicherheit gleich wieder einige geben werden die Schrien weil Sie es anders implementiert hätten, aber , hier mal ein recht kurz gehaltenes Beispiel von Observer und Observable, ich habe mir hier nicht die Mühe gemacht und habe den Button ( welcher die Daten des Models ändert, einfach mit in die View ( Die Sicht auf die Daten ) gepackt.
Ich hoffe es wird trotzdem klar worauf es hier hinausläuft : In der Gui ( hier der View selbst ) wird eine Aktion durchgeführt, diese ändert die Daten ( hier die Farbe ) des Models , das Model informiert den Beobachter ( Hier der View selbst ) darüber, und dieser ändert die Sicht ( Hier sich selbst ) auf das Model.
Der Controller initialisiert nur alles und setzt die view in der Gui, da ich nicht wie in so ziemlich allen Beispielen im Netz von von JFrame ableiten wollte.
(Man hätte auch den Controller zum Observer machen können btw..... )
Man kann es auch so implementieren das man das Model der View übergibt, ich fand es so aber treffender, hier kennt die View weder direkt das Model , noch umgekehrt, lediglich der Controller kennt View und Model, so bleibt das Model sowie die View komplett austauschbar / flexibel.
Ebenso kann ein Observer, gleichzeitig ein Observable sein , also Beobachter und beobachtetes Objekt, oder auch ein Observable mehrere Observer haben usw. usw... oder man implementiert das Pattern direkt inclusive Schnittstellen, oder auch nur mit Listenern , dies wollte ich dir aber aus gründen der Übersichlichkeit / und zum schonen meiner Nerven einfach mal ersparen :bae:
Schau es dir an , kopiere dir den Code, Teste es, fügen einen eigenen view dem Controller hinzu und schaue was passiert, oder korrigiert und oder Steinigt mich falls ich irgendetwas vergessen habe, hatte einen langen Tag
Quasi ringelpietz mit anfassen
Gui.java
Java:
import javax.swing.JFrame;
public class Gui{
public static void main(String[] args){
JFrame f = new JFrame("ColorChanger MVC");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setContentPane(new Controller().showView());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Controller.java
Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
public class Controller{
private Model model;
private View view;
public Controller(){
model = new Model();
view = new View();
view.addListener(new ButtonListener());
model.addObserver(view);
}
public JPanel showView(){
return view;
}
class ButtonListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
model.changeColor();
}
}
}
Model.java
Java:
import java.awt.Color;
import java.util.Observable;
public class Model extends Observable{
static int i = 0;
public final static int DEFAULT_COLOR = 0;
private Color color;
private Color[] colors =
{
Color.WHITE,
Color.BLUE,
Color.RED,
Color.GREEN
};
public Model(){
color = colors[DEFAULT_COLOR];
}
public void changeColor(){
if(i < colors.length - 1 ) {
i++;
}
else{
i = 0;
}
setChanged();
notifyObservers(this);
color = colors[i];
}
public Color getColor() {return color;}
}
View.java
Java:
iimport java.awt.Dimension;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class View extends JPanel implements Observer{
private JButton btn;
public View(){
setPreferredSize(new Dimension(250,150));
btn = new JButton("Klick mich");
add(btn);
}
public void addListener(ActionListener l){
btn.addActionListener(l);
}
@Override
public void update(Observable o, Object arg){
if(arg instanceof Model) {
setBackground(((Model)arg).getColor()); // DownCast auf Model
}
}
}