Textfeldformat für IBAN

Hallo,

ich möchte meine Anwendung von den bisherigen Kontodaten (Kontonummer und BLZ) auf SEPA umstellen und Texteingabefelder für IBAN und BIC einfügen. Die IBAN kann ja bis maximal 34 Zeichen lang werden, in Deutschland sind es 22 Zeichen. Für die bessere Lesbarkeit der langen IBAN gibt es ja die Einteilung in Viererblöcke. Jetzt wollte ich mal fragen, ob man ein Textfeld so formatieren kann, dass nach jedem 4. Zeichen automatisch ein Zwischenraum eingefügt wird? Also, nur in der Anzeige im Textfeld und nicht in der String-Variable selbst? Vorgefertigte Lösungen wie z.B. Formatter für JFormattedTextFields gibt es wahrscheinlich nicht? Oder müsste man das über einen DocumentFilter machen, der nach jedem 4. Zeichen ein Leerzeichen einfügt und diese beim Speichern in die Datenbank wieder entfernt? Dann müsste die maximale Eingabelänge des Textfeldes ja die 34 Zeichen plus die Leerzeichen nach jeder 4. Stelle bekommen. Oder sollte man die IBAN gleich so formatiert als String mit den Leerzeichen in die DB schreiben und nur wenn anders benötigt, die Leerzeichen wieder entfernen? Soweit ich das mitgekriegt habe, ist der Standard bzw. Pflicht die ganze IBAN ohne Leerzeichen, und nur zur besseren Lesbarkeit z.B. ausgedruckt mit den Viererblöcken vorgesehen.
Eine weitere Möglichkeit, die mir einfallen würde: für jeden Viererblock ein Texteingabefeld (mit 4 Zeichen) zu machen, die dann beim Speichern zusammengefügt werden. Erscheint mir aber nicht besonders praktisch, eher umständlich.

Würde mich über ein paar Tipps, Erfahrungswerte oder Anregungen zu dem Thema freuen…

Schau Dir mal das [JAPI]JFormattedTextfield[/JAPI] an.

bye
TT

JFTF ist eine Möglichkeit, ich würde über einen DocumentListener/(Filter?) arbeiten, und für die Weiterverarbeitung die Trennzeichen wieder entfernen.
Hätte den Vorteil, dass man dieses Document auch für weitere Components nutzen könnte. Aber generell bleibt halt nur

  1. Für die Anzeige Trennzeichen einfügen oder
  2. Für die Verarbeitung Trennzeichen entfernen :stuck_out_tongue:

[QUOTE=Timothy_Truckle;78476]Schau Dir mal das [JAPI]JFormattedTextfield[/JAPI] an.

bye
TT[/QUOTE]

Ja, hatte ich mir ja auch schon angeschaut und erwähnt. Aber kannst du mir sagen, wie ich es damit hinkriegen kann?

Cool wäre ja, wenn man ein Eingabefeld grafisch so formatieren könnte, wie auf einem Überweisungsformular, also das Eingabefeld unterteilt in Felder für jedes einzelne Zeichen, die vielleicht auch noch in Viererblöcke unterteilt.

[quote=Camino]Ja, hatte ich mir ja auch schon angeschaut und erwähnt. Aber kannst du mir sagen, wie ich es damit hinkriegen kann?[/quote]Ich nicht, aber der zweite Treffer bei Google:
http://java-tutorial.org/jformattedtextfield.html

[quote=Camino;78491]Cool wäre ja, wenn man ein Eingabefeld grafisch so formatieren könnte, wie auf einem Überweisungsformular, also das Eingabefeld unterteilt in Felder für jedes einzelne Zeichen, die vielleicht auch noch in Viererblöcke unterteilt.[/quote]Das ist doch 'ne
Fingerübung…
[spoiler]```public class EingabeTest {
public static void main(String[] args) {
JPanel eingabeZeile = new JPanel(new GridLayout(1, 0));
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(new JLabel());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(new JLabel());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(new JLabel());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());

	JFrame frame = new JFrame("Test");
	frame.getContentPane().add(eingabeZeile);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.pack();
	frame.setVisible(true);
}

private static JTextField createStelle() {
	JTextField stelle = new JTextField(1);
	stelle.addKeyListener(new KeyAdapter() {

		@Override
		public void keyTyped(KeyEvent arg0) {
			System.out.println(arg0);
			((JTextField) arg0.getSource()).transferFocus();
		}
	});
	stelle.addFocusListener(new FocusAdapter() {

		@Override
		public void focusGained(FocusEvent e) {
			JTextField stelle = (JTextField) e.getSource();
			String text = stelle.getText();
			stelle.setSelectionStart(0);
			stelle.setSelectionEnd(text.length());
		}
	});
	return stelle;
}

}```[/spoiler]

bye
TT

[QUOTE=Timothy_Truckle;78494]Ich nicht, aber der zweite Treffer bei Google:
http://java-tutorial.org/jformattedtextfield.html

Das ist doch 'ne
Fingerübung…
[spoiler]```public class EingabeTest {
public static void main(String[] args) {
JPanel eingabeZeile = new JPanel(new GridLayout(1, 0));
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(new JLabel());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(new JLabel());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(new JLabel());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());
eingabeZeile.add(createStelle());

	JFrame frame = new JFrame("Test");
	frame.getContentPane().add(eingabeZeile);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.pack();
	frame.setVisible(true);
}

private static JTextField createStelle() {
	JTextField stelle = new JTextField(1);
	stelle.addKeyListener(new KeyAdapter() {

		@Override
		public void keyTyped(KeyEvent arg0) {
			System.out.println(arg0);
			((JTextField) arg0.getSource()).transferFocus();
		}
	});
	stelle.addFocusListener(new FocusAdapter() {

		@Override
		public void focusGained(FocusEvent e) {
			JTextField stelle = (JTextField) e.getSource();
			String text = stelle.getText();
			stelle.setSelectionStart(0);
			stelle.setSelectionEnd(text.length());
		}
	});
	return stelle;
}

}```[/spoiler]

bye
TT[/QUOTE]

OK, hab das mal getestet. Sieht schonmal garnicht so schlecht aus… Müsste man eventuell noch weiter ausbauen, z.B. dass nur 1 Zeichen pro Textfeld zulässig ist, und dass der gesamte Text ausgelesen und weiter verarbeitet werden kann, evtl. auch nochmal mit Grösse, Farbe, Rahmen usw. rumspielen… Müsste ich aber auch hinkriegen. Hmm, muss ich jetzt mal überlegen, ob ich das so in das Formular bei mir einbauen werde, also vom Aussehen und der Funktionalität her. Vielen Dank jedenfalls für diesen Tipp.

Das andere mit dem JFormattedTextField hab ich mir zwar durchgelesen, aber irgendwie ist mir nicht klar, wie ich da den Abstand (oder ein Leerzeichen) nach jedem 4. Zeichen hinkriegen kann. Vielleicht versuch ich es doch mal mit einem DocumentListener und dem Hinzufügen und Entfernen der Leerzeichen, je nach Anzeigen oder Verarbeiten.

besser?
[spoiler]```
public class EingabeTest {
public static void main(String[] args) {

	JFrame frame = new JFrame("Test");
	final EingabeTest eingabeTest = new EingabeTest();
	eingabeTest.setNumber("DE01234567890123");
	frame.getContentPane().add(eingabeTest.getEingabeZeile());
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.pack();
	frame.addWindowListener(new WindowAdapter() {
		@Override
		public void windowClosing(WindowEvent e) {
			System.out.println(eingabeTest.getNumber());
		}
	});
	frame.setVisible(true);
}

private final JPanel eingabeZeile = new JPanel(new GridLayout(1, 0));
private List<JTextField> _stellen = new ArrayList<JTextField>();

public EingabeTest() {
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(new JLabel());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(new JLabel());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(new JLabel());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
	getEingabeZeile().add(createStelle());
}

public void setNumber(String string) {
	char[] charArray = string.toCharArray();
	for (int i = 0; i < charArray.length; i++) {
		_stellen.get(i).setText(String.valueOf(string.charAt(i)));
	}
}

public String getNumber() {
	StringBuilder numberString = new StringBuilder();
	int i = 0;
	for (JTextField stelle : _stellen) {
		if ((++i) % 5 != 0)
			numberString.append(stelle.getText());
	}
	return numberString.toString();
}

private JTextField createStelle() {
	JTextField stelle = new JTextField(1);
	_stellen.add(stelle);
	stelle.setHorizontalAlignment(SwingConstants.CENTER);
	stelle.addKeyListener(new KeyAdapter() {

		@Override
		public void keyTyped(KeyEvent arg0) {
			System.out.println(arg0);
			((JTextField) arg0.getSource()).transferFocus();
		}
	});
	stelle.addFocusListener(new FocusAdapter() {

		@Override
		public void focusGained(FocusEvent e) {
			JTextField stelle = (JTextField) e.getSource();
			String text = stelle.getText();
			stelle.setSelectionStart(0);
			stelle.setSelectionEnd(text.length());
		}
	});
	return stelle;
}

public JPanel getEingabeZeile() {
	return eingabeZeile;
}

}```[/spoiler]

bye
TT

Na ja, eigentlich schon, bin aber gerade mal wieder an mehreren Stellen gleichzeitig am arbeiten. Hatte ja auch geschrieben, dass ich das eigentlich selbst hinkriegen müsste. Aber trotzdem vielen Dank für deine Hilfe…

Ich würde einfach ein MaskFormatter verwenden.
http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html

http://www.dpunkt.de/java/Programmieren_mit_Java/Oberflaechenprogrammierung/57.html

Ich würde daraus eine eigene Klasse machen, die man dann auch einfach wiederverwenden kann. An der zweiten Lösung hapert es noch mit Backspace und ein paar anderen Dingen (Ctrl-V pastet eine Nummer komplett in die erste Stelle etc.), aber im großen und ganzen ist sie schon nicht verkehrt.

Ich hab’s jetzt erst mal mit einem JFormattedTextField und MaskFormatter gelöst.

MaskFormatter formatter = null;
		try {
			formatter = new MaskFormatter("UUAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AA");
		} catch (ParseException e) {
			e.printStackTrace();
		}
		formatter.setValidCharacters("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");

ftfIBAN = new JFormattedTextField(formatter);
ftfIBAN.setFocusLostBehavior(JFormattedTextField.PERSIST);

Soweit ich weiss, können in der IBAN maximal 34 Zeichen enthalten sein. Die ersten beiden nur Grossbuchstaben, dann eine zweistellige Prüfsumme und die restlichen Zeichen Grossbuchstaben und Ziffern. Im MaskFormatter war eine Mischung aus Grossbuchstabenund Ziffern nicht möglich. Entweder Buchstaben UND Ziffern oder nur Grossbuchstaben (Kleinbuchstaben werden beim eingeben in Grossbuchstaben umgewandelt). Deshalb die beiden “UU” am Anfang des MaskFormatter und die Methode setValidCharacters.

Die Leerzeichen der Maske sind nun aber im String (getText()) mit drin und müssen nachträglich entfernt werden, zur Speicherung in der DB und weiteren Verarbeitung.

Kannst doch einen eigenen MaskFormatter schreiben der sowas kann, also von dem jetzigen erben…

Musst mal versuchen ob getValue geht…

Hmm, irgendwie komm ich mit dem JFormattedTextField noch nicht ganz klar. Hatte das ja umgeändert von einem JTextField, bei dem es nur die getText- oder setText-Methoden gab. Jetzt hab ich noch die Methoden mit den values. So wie ich das verstanden habe, gibt es den Unterschied zwischen text und value, dass value der Wert für das Textfeld ist, und text der durch den MaskFormatter formatierte und angezeigte String. D.h. die Methode setValue sollte doch eigentlich den Wert als formatierten String im Textfeld anzeigen.

Ich hatte also dem JFormattedtextField mit setValue einen String übergeben. Wenn ich gleich danach getValue aufrufe, kann ich mir auf der Konsole diesen Wert anzeigen lassen. Aber im Textfeld selbst erscheint nichts. Keine Ahnung, vielleicht weil der value nicht mit dem MaskFormatter übereinstimmt? Dieser hatte ja 34 Zeichen. Die mit setValue gesetzte IBAN jedoch nur 22.

Ergänzung:
Wenn ich anstatt mit setValue den Wert mit setText an das JFormattedTextField übergebe, und danach commitEdit aufrufe, dann wird der Wert formatiert (4er Blöcke mit Leerzeichen) angezeigt, der Wert mit getValue hat dann aber auch diese Leerzeichen im String. Irgendwie blick ich da noch nicht so richtig durch…

*** Edit ***

Ich versteh die Funktionsweise des JFormattedTextFields in Verbindung mit dem MaskFormatter nicht.

Wenn ich die Mask festlege, muss ich den String, welchen ich mit setValue ins Textfeld setze, genauso formatieren, wie die Mask, also genauso viele Zeichen und auch die Leerzeichen zwischen den Viererblöcken. Wenn ich die Leerzeichen weglasse oder der Wert nicht ganz genau mit der Mask übereinstimmt, dann wird zwar der Wert gesetzt (ist mit getValue abrufbar), aber nichts ins Textfeld geschrieben.

Wenn ich den String (ohne Leerzeichen) mit setText ins Textfeld schreibe, wird er dort auch formatiert (wie bei der Mask angegeben) angezeigt, aber getValue liefert null.

So richtig versteh ich den Sinn und die Funktionalität von dem JFormattedTextField sowie value und text nicht so richtig. Wann und wozu brauche ich value und wie setze ich es richtig ein? Vielleicht kann mir das jemand erklären. Hab nun auch schon diverse Internetseiten durchgelesen, bin daraus aber auch nicht schlauer geworden.

Keep the following in mind when using a formatted text field:

A formatted text field’s text and its value are two different properties, and the value often lags behind the text.

The text property is defined by the JTextField class. This property always reflects what the field displays. The value property, defined by the JFormattedTextField class, might not reflect the latest text displayed in the field. While the user is typing, the text property changes, but the value property does not change until the changes are committed.

To be more precise, the value of a formatted text field can be set by using either the setValue method or the commitEdit method. The setValue method sets the value to the specified argument. The argument can technically be any Object, but the formatter needs to be able to convert it into a string. Otherwise, the text field does not display any substantive information.

The commitEdit method sets the value to whatever object the formatter determines is represented by the field’s text. The commitEdit method is automatically called when either of the following happens:

When the user presses Enter while the field has the focus.
By default, when the field loses the focus, for example, when the user presses the Tab key to change the focus to another component. You can use the setFocusLostBehavior method to specify a different outcome when the field loses the focus.

Quelle: How to Use Formatted Text Fields (The Java™ Tutorials > Creating a GUI With Swing > Using Swing Components)

Wenn du ein kleines, ausführbares Beispiel postest mit deinem aktuellen Stand, bastel ich wohl gerne mit.

Ja, gerne. Muss aber gleich erst mal los. Ich versuch mal heute Abend oder morgen tagsüber ein kleines ausführbares Beispiel zu erstellen und hier reinzustellen.

Den zitierten Text hatte ich auch schon gelesen, aber irgendwie versteh ich das mit dem value bzw. text noch nicht so richtig. Und durch das praktische Ausprobieren bin ich auch nicht schlauer geworden.

Danke und bis später

OK, ich hab gestern nochmal ein bisschen weiter experimentiert und recherchiert, und denke, dass ich so langsam dahinter gekommen bin, wie das JFormattedTextField mit dem MaskFormatter funktioniert.

  • Ich hab die Maske abgeändert:
    vorher war es “UU## AAAA AAAA AAAA AAAA AAAA AAAA AAAA AA”,
    jetzt hab ich “UU## **** **** **** **** **** **** **** **”
    Dadurch kann die Anzahl der eingegebenen Zeichen auch weniger sein, als die maximale Anzahl von 34, die in der Maske bestimmt sind.

  • durch MaskFormatter.setValueContainsLiteralCharacters(false); werden die Leerzeichen, welche in der Maske zwischen den Viererblöcken angegeben sind, im value entfernt.

Das scheint jetzt so wohl zu funktionieren. Werde es mal in die Anwendung einbauen und genauer testen.

Hier kommt trotzdem nochmal der kurze, ausführbare Code:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;

public class KontoTextfeld2 {
	
	private JFormattedTextField textField;
	
	private String value1 = "DE99 1234 5678 9012 3456 7890 1234 5678 90";
	private String value2 = "DE99123456789012345678901234567890";
	private String value3 = "DE99123456789012345678";

	
	public KontoTextfeld2() {
		
		JFrame frame = new JFrame("Test");
        frame.setSize(200, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		try {
			//MaskFormatter formatter = new MaskFormatter("UU## AAAA AAAA AAAA AAAA AAAA AAAA AAAA AA");
			MaskFormatter formatter = new MaskFormatter("UU## **** **** **** **** **** **** **** **");
			formatter.setValidCharacters("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ");
			formatter.setValueContainsLiteralCharacters(false);

			textField = new JFormattedTextField(formatter);
			textField.setColumns(28);
			textField.setFocusLostBehavior(JFormattedTextField.PERSIST);
			
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		JButton printButton = new JButton("Anzeigen");
		
		printButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				
				// vordefinierte values einsetzen
				//textField.setValue(value3);

				
				try {
					textField.commitEdit();
				} catch (ParseException e) {
					e.printStackTrace();
				}
				
				
				System.out.println("IBAN (text): " + textField.getText());
				System.out.println("IBAN-Länge (text): " + textField.getText().length());
				System.out.println("IBAN: " + (String)textField.getValue());
				System.out.println("IBAN-Länge (value): " + ((String)textField.getValue()).length());
			}
		});

        
        JPanel panel = new JPanel();
        panel.add(textField);
        panel.add(printButton);
        
        frame.getContentPane().add(panel);

        frame.pack();
        frame.setVisible(true);
		
	}
	
	
    public static void main(String[] args) {
    	new KontoTextfeld2(); 
    }

}

Kannst ja nochmal durchschauen, wenn du möchtest, und was dazu sagen, wenn dir was auffällt…

OK, noch ne Kleinigkeit:
Wenn ich die Maske “UU## **** **** **** **** **** **** **** **” habe, dann gibt es eine ParseException, wenn die ersten 4 Zeichen nicht belegt sind, also z.B., wenn ich das Textfeld komplett leer lasse. Wenn ich die ersten vier Zeichen in der Maske ebenfalls durch * ersetze, dann gibt es diese Exception nicht. Logisch. Aber diese vier Masken-Zeichen waren halt praktisch, weil ja somit die ersten beiden Zeichen immer Grossbuchstaben sind (Kleinbuchstaben wurden automatisch in Grossbuchstaben umgewandelt) und die darauffolgenden beiden Zeichen (Prüfsumme) immer zwei Ziffern sind. Gibt es irgendwie doch ne Möglichkeit, diesen Maskenteil (“UU##”) beizubehalten, und den Value dann trotzdem zu übernehmen, auch wenn er z.B. komplett leer ist? Oder geht das nur mit “****” am Anfang der Maske?

Ich wollte auch die Prüfsumme kontrollieren, um zu schauen, ob diese stimmt, aber anscheinend (laut Wikipedia) gibt es da noch Abweichungen bei der Prüfsummenberechnung verschiedener Bankinstitute, somit diese Prüfsumme nicht immer stimmen muss. Hab ich also erstmal drauf verzichtet.

Hat irgendwer schon ähnliche Probleme lösen müssen und evtl. weitere Erfahrungen und Tipps mit IBAN/BIC? Gibt es noch etwas Wichtiges zu beachten?

Ich bin leider in Arbeit untergegangen und hatte bisher keine Zeit, draufzuschauen.

Kein Problem… Ist ja auch nur ein kleines Teil in der Anwendung und ich bin da noch in anderen Bereichen ziemlich augelastet.