3

基础文章:[ https://community.oracle.com/thread/2552039?start=0&tstart=0][1]

我使用构造函数中的事件过滤器扩展了 Oracle 社区的解决方案,以获得更好的用户人体工程学。

JavaFX TimeTextField 提供时间处理所需的所有函数。不幸的是,Java8 的 JavaFX 标准中缺少它,在我看来,时间处理字段是必须的,因为在其他技术中它是可能的。

oracle社区的线程没有我想象的那么频繁,请测试并尝试改进它!我们需要...

特点: - 只接受数字键。- 验证给定时间格式 - (我的改进)当达到块的最后一个数字(如小时或分钟)时,光标跳转到下一个块。

package test;

import java.util.regex.Pattern;

import javafx.beans.binding.Bindings;

import javafx.application.Application;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.IndexRange;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
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 javafx.geometry.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");
            this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent inputevent) {
                    int c = TimeTextField.this.getCaretPosition();
                    if (c <= 7) {
                        if (!"1234567890:".contains(inputevent.getCharacter().toLowerCase())) {
                            inputevent.consume();
                        }
                    } else {
                        inputevent.consume();
                    }
                }
            });
            this.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent inputevent) {
                    boolean withMinutes = false;
                    if (TimeTextField.this.getText() != null && TimeTextField.this.getText().length() >= 5
                            && TimeTextField.this.getText().indexOf(":") == 2) {
                        withMinutes = true;
                    }
                    boolean withSeconds = false;
                    if (TimeTextField.this.getText() != null && TimeTextField.this.getText().length() == 8
                            && TimeTextField.this.getText().lastIndexOf(":") == 5) {
                        withSeconds = true;
                    }

                    int c = TimeTextField.this.getCaretPosition();
                    if (((c == 2 && withMinutes) || (c == 5 && withSeconds))
                            && (inputevent.getCode() != KeyCode.LEFT && inputevent.getCode() != KeyCode.BACK_SPACE)) {
                        TimeTextField.this.forward();
                        inputevent.consume();
                    }
                }
            });

        }

        public TimeTextField(String time) {
            super(time);
            // timePattern = Pattern.compile("\\d\\d:\\d\\d:\\d\\d");
            timePattern = Pattern.compile("([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]");
            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() > 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() > 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 < 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 < 0 || hours > 23) {
                    return false;
                }
                if (mins < 0 || mins > 59) {
                    return false;
                }
                if (secs < 0 || secs > 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);
            }

        }

    }
}
4

0 回答 0