JavaFX 2.2 プロジェクトに取り組んでいますが、TextField
コントロールの使用に問題があります。ユーザーがそれぞれに入力できる文字数を制限したいTextField
。ただし、プロパティやmaxlengthのようなものが見つかりません。同じ問題がSwingに存在し、この方法で解決されました。JavaFX 2.2 で解決するにはどうすればよいですか?
9 に答える
java8u40 では、新しいクラス TextFormatter を取得しました。その主な役割の 1 つは、テキスト入力の変更がコンテンツにコミットされる前にフックを提供することです。そのフックでは、提案された変更を承認/拒否したり、変更したりすることさえできます。
OPの自己回答で解決される要件は
- ルール: テキストの長さを n 文字未満に制限する
- 変更: ルールに違反している場合は、最後の n 文字を入力テキストとして保持し、最初の余分な文字を削除します
TextFormatter を使用すると、次のように実装できます。
// here we adjust the new text
TextField adjust = new TextField("scrolling: " + len);
UnaryOperator<Change> modifyChange = c -> {
if (c.isContentChange()) {
int newLength = c.getControlNewText().length();
if (newLength > len) {
// replace the input text with the last len chars
String tail = c.getControlNewText().substring(newLength - len, newLength);
c.setText(tail);
// replace the range to complete text
// valid coordinates for range is in terms of old text
int oldLength = c.getControlText().length();
c.setRange(0, oldLength);
}
}
return c;
};
adjust.setTextFormatter(new TextFormatter(modifyChange));
余談:
- 変更をリッスンしながらプロパティを変更すると、予期しない副作用が発生する可能性があります
- キーレベルのイベントで提案された解決策はすべて壊れています (貼り付け/プログラムによる変更を処理できません)
ここで説明されているアプローチと同様のことを行うことができます: http://fxexperience.com/2012/02/restricting-input-on-a-textfield/
class LimitedTextField extends TextField {
private final int limit;
public LimitedTextField(int limit) {
this.limit = limit;
}
@Override
public void replaceText(int start, int end, String text) {
super.replaceText(start, end, text);
verify();
}
@Override
public void replaceSelection(String text) {
super.replaceSelection(text);
verify();
}
private void verify() {
if (getText().length() > limit) {
setText(getText().substring(0, limit));
}
}
};
私の問題を解決するために使用した完全なコードは、以下のコードです。Sergey Grinev が行ったように TextField クラスを拡張し、空のコンストラクターを追加しました。maxlength を設定するために、setter メソッドを追加しました。最初に TextField のテキストを確認してから置き換えます。これは、maxlength を超える文字の挿入を無効にしたいためです。そうしないと、TextField の最後に maxlength + 1 文字が挿入され、TextField の最初の文字が削除されます。
package fx.mycontrols;
public class TextFieldLimited extends TextField {
private int maxlength;
public TextFieldLimited() {
this.maxlength = 10;
}
public void setMaxlength(int maxlength) {
this.maxlength = maxlength;
}
@Override
public void replaceText(int start, int end, String text) {
// Delete or backspace user input.
if (text.equals("")) {
super.replaceText(start, end, text);
} else if (getText().length() < maxlength) {
super.replaceText(start, end, text);
}
}
@Override
public void replaceSelection(String text) {
// Delete or backspace user input.
if (text.equals("")) {
super.replaceSelection(text);
} else if (getText().length() < maxlength) {
// Add characters, but don't exceed maxlength.
if (text.length() > maxlength - getText().length()) {
text = text.substring(0, maxlength- getText().length());
}
super.replaceSelection(text);
}
}
}
fxml ファイル内で、 (TextFieldLimited クラスが存在するパッケージの) インポートをファイルの先頭に追加し、TextField タグをカスタム TextFieldLimited に置き換えました。
<?import fx.mycontrols.*?>
.
.
.
<TextFieldLimited fx:id="usernameTxtField" promptText="username" />
コントローラークラス内で、
上部 (プロパティ宣言)、
@FXML
private TextFieldLimited usernameTxtField;
初期化メソッド内で、
usernameTxtField.setLimit(40);
それで全部です。
文字数を制限し、数値入力を強制するためのより簡単な方法を使用しています。
public TextField data;
public static final int maxLength = 5;
data.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try {
// force numeric value by resetting to old value if exception is thrown
Integer.parseInt(newValue);
// force correct length by resetting to old value if longer than maxLength
if(newValue.length() > maxLength)
data.setText(oldValue);
} catch (Exception e) {
data.setText(oldValue);
}
}
});
以下のコードは、カーソルの位置を変更して、ユーザーが誤って入力を上書きしないようにします。
public static void setTextLimit(TextField textField, int length) {
textField.setOnKeyTyped(event -> {
String string = textField.getText();
if (string.length() > length) {
textField.setText(string.substring(0, length));
textField.positionCaret(string.length());
}
});
}
このメソッドにより、TextField はすべての処理を完了できます (コピー/貼り付け/元に戻すことができます)。拡張クラスを作成する必要はありません。また、変更のたびに新しいテキストをどうするか (ロジックにプッシュするか、以前の値に戻すか、さらには変更するか) を決定できます。
// fired by every text property change
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
あなたの場合、このロジックを内部に追加するだけです。完璧に動作します。
// For example 10 characters
if (newValue.length() >= 10) ((StringProperty)observable).setValue(oldValue);