HOWTO: GUI mit Swing – Teil 4: Interaktion mit der GUI


Zur Erinnerung die bereits besprochenen und noch geplanten Themen hier noch eine Auflistung selbiger:

      Aufgabe aus Teil 3

      Aufgabe überspringen

      Aufgabe aus Teil 3 war es, nachdem wir die Anzeige Maske fertiggestellt hatten, auch die Edit Maske zu konstruieren.

      EditJPanel

      Damit wir wahlweise die Daten anzeigen, oder bearbeiten können, habe ich in meiner Lösung folgenden Weg eingeschlagen: Das ShowDataJPanel wurde umgebaut zum DataJPanel, welches im Konstruktor nun einen Boolean Parameter entgegennimmt, durch den entschieden wird ob die Daten mittels Label oder Textfield angezeigt werden. Anhand dieses Booleans wird auch entschieden ob die statistsichen Daten angezeigt werden oder der Absende Button.

      public DataJPanel(boolean useTextfields) { //...

      Unsere Maske wird nun immer noch gleich aufgebaut wie vorher, allerdings instanziert sie nicht von Haus aus ein ShowDataJLabel, sondern überlässt einer Factory Klasse die Entscheidung ob es entweder das JLabel oder aber ein EditDataJTextField erzeugt indem es das useTextfields Boolean daran durchschleust.

      JComponent kdnrContent = ShowOrEditJComponentFactory.getJComponent(
      				"0123456", useTextfields);

      Dabei wird das Wissen um den Aufbau des erhaltenen Objekts bewusst klein gehalten, um nicht zu hohe Abhängigkeiten zu erhalten. Das JComponent wird durch folgende Factory erzeugt:

      package at.nullpointer.guiprototype.showdata;
      
      import java.awt.Color;
      import java.awt.Dimension;
      import java.awt.Font;
      
      import javax.swing.JComponent;
      
      public class ShowOrEditJComponentFactory {
      
      	private static final Font font = new Font("Dialog", Font.PLAIN, 18);
      	private static final Dimension textFieldSize = new Dimension(480, 30);
      
      	private static JComponent createJLabel(String caption) {
      		return new ShowDataJLabel(caption);
      	}
      
      	private static JComponent createJTextField(String caption) {
      		return new EditDataJTextField(caption);
      	}
      
      	public static JComponent getJComponent(String caption, boolean isTextField) {
      		JComponent erg = null;
      		if (isTextField) {
      			erg = createJTextField(caption);
      			erg.setMaximumSize(textFieldSize);
      			erg.setMinimumSize(textFieldSize);
      			erg.setPreferredSize(textFieldSize);
      		} else {
      			erg = createJLabel(caption);
      		}
      
      		// Schriftfarbe
      		erg.setForeground(Color.BLACK);
      		// undurchsichtig (notwendig um einen Hintergrund zu zeichnen)
      		erg.setOpaque(true);
      		// Veränderung der Schrift
      		erg.setFont(font);
      
      		return erg;
      	}
      }

      Die beiden Klassen ShowDataJLabel und EditDataJTextField, die erst von der Factory erzeugt werden und dann anhand des allgemeinen Verhaltens der JComponent etwas an unsere Optik angepasst werden sind selbst relativ simpel gehalten:

      package at.nullpointer.guiprototype.showdata;
      
      import javax.swing.JTextField;
      
      public class EditDataJTextField extends JTextField {
      
      	public EditDataJTextField(String caption) {
      		this();
      		this.setText(caption);
      	}
      
      	public EditDataJTextField() {
      		super();
      	}
      }
      package at.nullpointer.guiprototype.showdata;
      
      import javax.swing.JLabel;
      
      public class ShowDataJLabel extends JLabel {
      
      	public ShowDataJLabel() {
      
      		super();
      	}
      
      	public ShowDataJLabel(String caption) {
      		this();
      		this.setText(caption);
      	}
      }
      Der Button

      Natürlich darf in der DataJPanel der Button zum Absenden nicht fehlen, der jedoch nur angezeigt werden soll, wenn Textfelder genutzt werden.

      if (useTextfields) {
      
      			// Zuletzt aktiv
      			JPanel lastPanel = new JPanel();
      			lastPanel.setLayout(new BoxLayout(lastPanel, BoxLayout.LINE_AXIS));
      			JButton btnOk = new JButton("OK");
      			JButton btnCancel = new JButton("Abbrechen");
      			lastPanel.add(Box.createHorizontalGlue());
      			lastPanel.add(btnOk);
      			lastPanel.add(Box.createRigidArea(rowSmall));
      			lastPanel.add(btnCancel);
      			this.add(lastPanel);
      			this.add(Box.createRigidArea(lineSmall));
      
      		} else {
      Aufruf der Sichten

      Jetzt muss nur noch der Aufruf der Sichten angepasst werden, der analog zum Aufruf der Daten Ansicht erfolgt. Bei Beiden gilt es jedoch nun ein Boolean zu übergeben, damit die Factory entscheiden kann, was angezeigt werden soll. Unsere fertige Eingabemaske sieht nun wie folgt aus:

      Edit Sicht

      Die vollständigen Sourcen dazu findet ihr wie immer am Schluss dieses Artikels, gemeinsam mit der nächsten Aufgabe.

      Ein Button erwacht zum Leben

      Um aufzuzeigen wie man einen Button zum Leben erweckt erzeugen wir uns als erstes eine sehr simple GUI:

      package at.nullpointer.learningswing.demojframe;
      
      import javax.swing.JButton;
      import javax.swing.JFrame;
      
      public class InputDemo {
      	int count = 0;
      	String text = " mal geklickt";
      	JButton btn = null;
      
      	public static void main(String[] args){
      		InputDemo gui = new InputDemo();
      		gui.draw();
      	}	
      
      	public void draw(){
      		JFrame frame = new JFrame();
      		btn = new JButton(count+text);
      		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      		frame.getContentPane().add(btn);
      		frame.pack();
      		frame.setVisible(true);
      	}
      }

      JButton Event Demo Wenn wir diese ausführen bekommen wir folgendes einfach nur einen Button angezeigt, bei dessen Betätigung nichts passiert. Das wollen wir nun ändern. Dafür brauchen wir 2 Dinge, etwas das passieren soll, und einen Weg wie wir rausfinden ob der Button geklickt wurde.

      Unschwer an der Beschriftung des Buttons zu erraten, wollen wir, dass jedes mal wenn er betätigt wird, sich seine Beschriftung ändert und die Anzahl der Klicks korrekt dargestellt wird. Dazu erweitern wir unser Demo um folgende Methode:

      	private void increaseCount(){
      		count++;
      		btn.setText(count+text);
      	}

      Testen lässt sie sich ganz einfach indem wir den Aufruf der Methode am Ende der Main Methode hinzufügen.

      Um nun auf die User Aktion reagieren zu können setzt Java ein Modell ein, das sich Event Handling nennt. Klickt ein User auf den Button wird ein Event (repräsentiert durch ein Event Objekt) abgefeuert. Diese Funktionalität bietet der JButton, ohne dass wir sie selbst implementieren müssten. Jedoch fehlt uns noch ein Weg dieses Event auszulesen. Es gibt verschiedenste Events (nachzusehen im java.awt.event Package) und unser JButton wirft eines vom SubTyp ActionEvent. Jedes dieser Eventtypen kann durch einen eigenen Listener ausgelesen werden. Für unser ActionEvent ist also ein ActionListener notwendig. Dies ist ein Interface, welches wir allerdings schon selbst implementieren müssen. Wir erweitern also unser InputDemo und lassen es das Interface implementieren

      public class InputDemo implements ActionListener {

      Damit wir das Interface vollständig umsetzen, müssen wir auch noch die von ihm geforderte Methode erzeugen. In diesem Fall ist das die actionPerformed Methode, in welcher wir unsere increaseCount methode aufrufen

      	public void actionPerformed(ActionEvent e) {
      		increaseCount();
      	}

      Starten wir nun das Programm sehen wir, dass das existieren eines Listeners alleine noch keine Auswirkungen hat, dem Button muss erst gesagt werden, dass da ein ActionListener auf seine Events wartet. Dazu stellt der JButton wieder eine entsprechende Methode zur Verfügung, welche wir bei der Erzeugung des JButtons aufrufen.

      		btn.addActionListener(this);

      image Und siehe da, unser Count beginnt nun beim Betätigen des Buttons zu steigen und zu steigen!

      ActionListener im Prototypen

      Der Cancel Button

      Unser neu erworbenes Wissen wollen wir jetzt natürlich im Prototypen anwenden. Wir wollen den Abbrechen Button mit Funktionalität belegen, dazu erzeugen wir uns zuerst im Prototypen entsprechende Methoden, die dazu führen dass statt der Editansicht die Showansicht angezeigt wird.

      Eine Methode die die Ansichten zeichnet haben wir ja bereits geschrieben: fillFrameWithDataContent

      Also benötigen wir nur noch eine Methode die das Frame von seinem alten Inhalt befreit:

      	public static JFrame removeJPanel(JFrame frame){
      		Component[] components = frame.getContentPane().getComponents();
      		for(Component component : components){
      			if (component instanceof JPanel){
      				frame.remove(component);
      			}
      		}
      		return frame;
      	}

      Danach erzeugen wir eine eigene Klasse, die wir EditActionListener taufen. Diese wird auf den Klick mit der Maus reagieren. Dabei arbeiten wir der Einfachheit halber in diesem Beispiel mit den static Methoden der Prototype Class. Ein Vorgehen welches ich ansonsten nicht befürworten würde.

      package at.nullpointer.guiprototype.listener;
      
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      
      import at.nullpointer.guiprototype.Prototype;
      
      public class EditActionListener implements ActionListener {
      
      	public void actionPerformed(ActionEvent e) {
      		Prototype.removeJPanel(Prototype.getJFrame());
      		Prototype.fillFrameWithDataContent(Prototype.getJFrame(), false).setVisible(true);
      	}
      }

      Zuerst wird unsere neue Methode aufgerufen, dannach befüllen wir das JFrame wieder. Achtung, das JFrame wird dann mittels Aufruf der setVisible Methode erneut für den Benutzer dargestellt, um die Änderungen sichtbar zu machen.

      Nun noch in der Klasse DataJPanel den EditActionListener dem Cancelbutton hinzugefügt und schon reagiert unser Prototyp richtig bei einem Klick auf den Abbrechen Button!

      Der OK Button

      Wie ihr vielleicht an der Benennung des EditActionListener erkannt habt, soll dieser nicht nur für den Cancel Button zuständig sein, sondern auch für den OK Button. Wir lassen ihn daher auch auf Events des OKButtons anhand der addActionListener Methode lauschen.

      Klicken wir nun auf den OK Button sehen wir unser ActionListener nicht zwischen den zwei Buttons unterscheiden kann. Der Klick führt zum selben Ergebnis wie ein Klick auf den Cancel Button. Damit der ActionListener differenzieren kann, nutzen wir die Informationen aus, die das Event, welches der actionPerformed Methode mitgegeben wurden, enthält. Folgende Methoden stehen dabei zur Verfügung:

      getActionCommand() liefert den CommandString der der Action entspricht
      getModifiers() liefert ein int, welches die Kontrolltasten die dabei gedrückt wurden repräsentiert
      getSource() liefert das Objekt auf dem das Event durchgeführt wurde
      getWhen() liefert den Zeitpunkt des Events zurück

      Wir verwenden die getActionCommand() Methode um unser Button Objekt zu identifizieren. Dieses liefert uns den ID-String zurück, den wir bei der Buttonerzeugung angegeben haben, also hierbei “OK”. Nur im falle eines Clicks auf OK führen wir zusätzlich das Absenden der Daten aus.

      	public void actionPerformed(ActionEvent e) {
      		if(e.getActionCommand().equals("OK")){
      			Prototype.sendData();
      		}
      		Prototype.removeJPanel(Prototype.getJFrame());
      		Prototype.fillFrameWithDataContent(Prototype.getJFrame(), false).setVisible(true);
      	}

      Die Methode sendData() würde nun auf den Firmenserver zurückgreifen und Daten an die Datenbank senden. Da dies den Rahmen des Tutorials sprengen würde wollen wir hier nur die Stringbestätigung ausgeben, die unsere Mocksoftware für die Gesendeten Daten retourniert.

      	public static void sendData() {
      		MockCompanySoftware mcs = new MockCompanySoftware();
      		JOptionPane.showMessageDialog(null, mcs.setKundendaten(null));
      	}

      Auch der OK Button wurde nun mit Funktionalität ausgestattet.

      Vorschau und Aufgabenstellung

      Nun da die beiden Buttons reagieren fehlt uns noch das Menü. Hierfür ist ebenfalls ein ActionListener zu implementieren. Hier noch die Ausgangs-Sourcen für diese Aufgabenstellung.

      Viel Erfolg dabei!

      Im nächsten und abschließenden Teil dieser kleinen Serie möchte ich noch kurz die Möglichkeiten für Nebenläufigkeit in Java Swing aufzeigen.

      Ähnliche Artikel:

      Schreibe einen Kommentar

      Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

      *