Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This works perfectly : </p> <pre><code> import java.util.regex.Pattern; import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.IntegerBinding; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyIntegerWrapper; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.IndexRange; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class TimeTextFieldTest extends Application { @Override public void start(Stage primaryStage) { VBox root = new VBox(5); root.setPadding(new Insets(5)); Label hrLabel = new Label(); Label minLabel = new Label(); Label secLabel = new Label(); TimeTextField timeTextField = new TimeTextField(); hrLabel.textProperty().bind(Bindings.format("Hours: %d", timeTextField.hoursProperty())); minLabel.textProperty().bind(Bindings.format("Minutes: %d", timeTextField.minutesProperty())); secLabel.textProperty().bind(Bindings.format("Seconds: %d", timeTextField.secondsProperty())); root.getChildren().addAll(timeTextField, hrLabel, minLabel, secLabel); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } public static class TimeTextField extends TextField { enum Unit {HOURS, MINUTES, SECONDS}; private final Pattern timePattern ; private final ReadOnlyIntegerWrapper hours ; private final ReadOnlyIntegerWrapper minutes ; private final ReadOnlyIntegerWrapper seconds ; public TimeTextField() { this("00:00:00"); } public TimeTextField(String time) { super(time); timePattern = Pattern.compile("\\d\\d:\\d\\d:\\d\\d"); if (! validate(time)) { throw new IllegalArgumentException("Invalid time: "+time); } hours = new ReadOnlyIntegerWrapper(this, "hours"); minutes = new ReadOnlyIntegerWrapper(this, "minutes"); seconds = new ReadOnlyIntegerWrapper(this, "seconds"); hours.bind(new TimeTextField.TimeUnitBinding(Unit.HOURS)); minutes.bind(new TimeTextField.TimeUnitBinding(Unit.MINUTES)); seconds.bind(new TimeTextField.TimeUnitBinding(Unit.SECONDS)); } public ReadOnlyIntegerProperty hoursProperty() { return hours.getReadOnlyProperty(); } public int getHours() { return hours.get() ; } public ReadOnlyIntegerProperty minutesProperty() { return minutes.getReadOnlyProperty(); } public int getMinutes() { return minutes.get(); } public ReadOnlyIntegerProperty secondsProperty() { return seconds.getReadOnlyProperty(); } public int getSeconds() { return seconds.get(); } @Override public void appendText(String text) { // Ignore this. Our text is always 8 characters long, we cannot append anything } @Override public boolean deleteNextChar() { boolean success = false ; // If there's a selection, delete it: final IndexRange selection = getSelection(); if (selection.getLength()&gt;0) { int selectionEnd = selection.getEnd(); this.deleteText(selection); this.positionCaret(selectionEnd); success = true ; } else { // If the caret preceeds a digit, replace that digit with a zero and move the caret forward. Else just move the caret forward. int caret = this.getCaretPosition(); if (caret % 3 != 2) { // not preceeding a colon String currentText = this.getText(); setText(currentText.substring(0, caret) + "0" + currentText.substring(caret+1)); success = true ; } this.positionCaret(Math.min(caret+1, this.getText().length())); } return success ; } @Override public boolean deletePreviousChar() { boolean success = false ; // If there's a selection, delete it: final IndexRange selection = getSelection(); if (selection.getLength()&gt;0) { int selectionStart = selection.getStart(); this.deleteText(selection); this.positionCaret(selectionStart); success = true ; } else { // If the caret is after a digit, replace that digit with a zero and move the caret backward. Else just move the caret back. int caret = this.getCaretPosition(); if (caret % 3 != 0) { // not following a colon String currentText = this.getText(); setText(currentText.substring(0, caret-1) + "0" + currentText.substring(caret)); success = true ; } this.positionCaret(Math.max(caret-1, 0)); } return success ; } @Override public void deleteText(IndexRange range) { this.deleteText(range.getStart(), range.getEnd()); } @Override public void deleteText(int begin, int end) { // Replace all digits in the given range with zero: StringBuilder builder = new StringBuilder(this.getText()); for (int c = begin; c&lt;end; c++) { if (c % 3 != 2) { // Not at a colon: builder.replace(c, c+1, "0"); } } this.setText(builder.toString()); } @Override public void insertText(int index, String text) { // Handle an insert by replacing the range from index to index+text.length() with text, if that results in a valid string: StringBuilder builder = new StringBuilder(this.getText()); builder.replace(index, index+text.length(), text); final String testText = builder.toString(); if (validate(testText)) { this.setText(testText); } this.positionCaret(index + text.length()); } @Override public void replaceSelection(String replacement) { final IndexRange selection = this.getSelection(); if (selection.getLength()==0) { this.insertText(selection.getStart(), replacement); } else { this.replaceText(selection.getStart(), selection.getEnd(), replacement); } } @Override public void replaceText(IndexRange range, String text) { this.replaceText(range.getStart(), range.getEnd(), text); } @Override public void replaceText(int begin, int end, String text) { if (begin==end) { this.insertText(begin, text); } else { // only handle this if text.length() is equal to the number of characters being replaced, and if the replacement results in a valid string: if (text.length() == end - begin) { StringBuilder builder = new StringBuilder(this.getText()); builder.replace(begin, end, text); String testText = builder.toString(); if (validate(testText)) { this.setText(testText); } this.positionCaret(end); } } } private boolean validate(String time) { if (! timePattern.matcher(time).matches()) { return false ; } String[] tokens = time.split(":"); assert tokens.length == 3 ; try { int hours = Integer.parseInt(tokens[0]); int mins = Integer.parseInt(tokens[1]); int secs = Integer.parseInt(tokens[2]); if (hours &lt; 0 || hours &gt; 23) { return false ; } if (mins &lt; 0 || mins &gt; 59) { return false ; } if (secs &lt; 0 || secs &gt; 59) { return false ; } return true ; } catch (NumberFormatException nfe) { // regex matching should assure we never reach this catch block assert false ; return false ; } } private final class TimeUnitBinding extends IntegerBinding { final Unit unit ; TimeUnitBinding(Unit unit) { this.bind(textProperty()); this.unit = unit ; } @Override protected int computeValue() { // Crazy enum magic String token = getText().split(":")[unit.ordinal()]; return Integer.parseInt(token); } } } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload