Swing JTextField Sync View -> Model

OlliL

Bekanntes Mitglied
Hallo,

ich habe eine Eingabemaske welche aus JTextField Items besteht.
Wie befüllt man am besten das Model im MVC-Kontext?

Aktuell befülle ich das Model in meinem Controller wenn die "save" Action ausgeführt wird (Wert aus der View Lesen mittels getWert und ins Model schreiben mittels setWert).
Danach überführe ich das Model aber direkt wieder in ein Transfer-Objekt und übergebe dieses Transfer-Objekt dann einer EJB zur Ablage in der DB. Bei dem Ansatz ist das Model meiner Meinung nach ziemlich sinnfrei.

Was mir eher vorschwebt...

Nach der erfolgten Eingabe des Anwenders im JTextField werden die Daten sofort im Model abgelegt und es kann eine rudimentäre Fehlerprüfung (Format, Pflichtfeld) im Model erfolgen. Via Observer Pattern wird dann der Fehler an die View zurückgemeldet.

Nun habe ich aber ein Problem das füllen des Models anständig zu bewerkstelligen. Meine Idee ist ein FocusAdapter auf dem JTextfield:

Java:
		nameField.addFocusListener(new FocusAdapter() {
	        @Override
	        public void focusLost(FocusEvent e) {
	        	System.out.println("->"+nameField.getText());
	        	model.setUsername(nameField.getText());
	        }
	      });

Nun ist mein Save-Button aber als DefaultButton konfiguriert, kann also jederzeit mittels [Enter] angesprungen werden.

Bei diesem Vorgang verliert mein JTextField jedoch nicht den Fokus, der FocusListener wird also nicht ausgelöst, und es erfolgt keine Ablage der Daten im Model.

Ich hatte die Idee im Controller welcher die actionPerformed meines Buttons handelt einfach den Fokus auf den Button zu legen - das hilft mir aber nicht, da der requestFocus() auf dem Button offensichtlich erst ausgeführt wird von Swing, wenn actionPerformed des Buttons durchgelaufen ist.

Einen DocumentListener hatte ich verworfen, da dieser ja bei jeder Änderung mein Model ändern würde und ggf. Fehler auslösen würde (z.B. wenn min 10 Zeichen eingegeben werden müssen).

Also... wie macht mans richtig? 😉
 
ein KeyBoardFocusManager ist auch "zu spät"
Java:
		KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(       new KeyEventDispatcher()  { 
            public boolean dispatchKeyEvent(KeyEvent e){
                if(e.getID() == KeyEvent.KEY_PRESSED){
                	if(e.getKeyCode() == KeyEvent.VK_ENTER) {
                		saveButton.requestFocus();
                		System.out.println("lala");
                	}
                }
                return false;
            } 
		});

Es erfolgt zwar erst die Ausführung vom dispatchKeyEvent, dann jedoch von actionPerformed von dem Button, und dann erst wird das focusLost Event des JTextFields abgearbeitet.
 
das geht - aber kommt mir vor wie durchs Knie ins Auge - gehts nicht besser?
Wenn ich mir jetzt vorstelle 10 Textfelder zu haben... oh mein Gott...

Alternative 2 wäre gar nicht so schlecht, wenn man Function-Pointer auf "model.setUsername()" in Java übergeben könnte, dann könnte man das Zeugs nämlich in ein von JTextField abgeleitete Klasse packen, den zu verwendenden "setter" übergeben und hätte eine generische Lösung.

Alternative 1

Java:
		nameField = new JTextField(30);
		nameField.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				model.setUsername(nameField.getText());
			}
		});

		passField = new JPasswordField(30);
		passField.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				model.setPassword(new String(passField.getPassword()));
			}
		});

		KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
				new KeyEventDispatcher() {
					public boolean dispatchKeyEvent(KeyEvent e) {
						if (e.getID() == KeyEvent.KEY_PRESSED
								&& e.getKeyCode() == KeyEvent.VK_ENTER
									&& e.getSource() instanceof JTextField) {
							if (e.getSource().equals(nameField)) {
								model.setUsername(nameField.getText());
							} else if (e.getSource().equals(passField)) {
								model.setPassword(new String(passField.getPassword()));
							}
						}
						return false;
					}
				});

Alternative 2

Java:
		nameField = new JTextField(30);
		nameField.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				model.setUsername(nameField.getText());
			}
		});
		nameField.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent ae)
            {
            	model.setUsername(nameField.getText());
            	logonButton.doClick();
            	nameField.requestFocus();
            }
        });

		passField = new JPasswordField(30);
		passField.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				model.setPassword(new String(passField.getPassword()));
			}
		});
		passField.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent ae)
            {
            	model.setPassword(new String(passField.getPassword()));
            	logonButton.doClick();
            	passField.requestFocus();
            }
        });
 
Zuletzt bearbeitet:
Der Thread leidet an Selbstgesprächen 😉
Was ist hiervon zu halten?

Java:
import Model;

public class MTextField extends JTextField {

	private static final long	serialVersionUID	= 432602729470291786L;
	private int					id;
	private Model				model;

	public MTextField(int arg0, int id, Model model, final JButton button) {
		super(arg0);
		this.id = id;
		this.model = model;
		this.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				publishChange();
			}
		});
		this.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent ae) {
				publishChange();
				if(button != null) {
					button.doClick();
				}
				requestFocus();
			}
		});

	}

	private void publishChange() {
		model.setProperty(id, this.getText());
	}
}

In meinem Model:

Java:
public class LogonModel extends Observable implements Model{
...
	public void setProperty(int propertyId, Object obj) {
		switch(propertyId) {
			case PropertyConstants.PROPERTY_USERNAME:
				setUsername((String)obj);
				break;
			case PropertyConstants.PROPERTY_PASSWORD:
				setPassword((String)obj);
				break;
		}
	}
}

In der View:
Java:
		nameField = new MTextField(30, PropertyConstants.PROPERTY_USERNAME,m,logonButton);

Das Model-If:
Java:
public interface Model {
	public void setProperty(int propertyId, Object obj);
}
 

Zurück
Oben