JavaFx创建表格单元格仅接受数字?

时间:2015-04-19 02:06:05

标签: javafx javafx-8

我的TableView里面有一列只能接受数字。 并且我在onMouseClickListener上添加了鼠标单击进入编辑模式而不是双击单元格 我想要一种不允许用户输入除数字之外的任何字符的方法。我的代码是:

Callback<TableColumn<DailyDetails, String>, TableCell<DailyDetails, String>> defaultCellFactory
            = TextFieldTableCell.<DailyDetails>forTableColumn();
dailyCredit.setCellFactory(column -> {
        TableCell<DailyDetails, String> cell = defaultCellFactory.call(column);

        cell.setOnMouseClicked(e -> {
            if (!cell.isEditing() && !cell.isEmpty()) {
                cell.getTableView().edit(cell.getIndex(), column);
            }
        });
        return cell;
    });

我从头开始实现了表格单元格:

class NumberCell extends TableCell<DailyDetails, String> {

    private TextField textField;

    public NumberCell() {
    }

    @Override
    public void startEdit() {
        super.startEdit();

        if (textField == null) {
            createTextField();
        }

        setGraphic(textField);
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        textField.selectAll();
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();

        setText(String.valueOf(getItem()));
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }

    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);

        if (empty) {
            setText(null);
            setGraphic(null);
        } else {
            if (isEditing()) {
                if (textField != null) {
                    textField.setText(getString());
                }
                setGraphic(textField);
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            } else {
                setText(getString());
                setContentDisplay(ContentDisplay.TEXT_ONLY);
            }
        }
    }

    private void createTextField() {
        textField = new TextField(getString());
        //textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
        textField.lengthProperty().addListener(new ChangeListener<Number>(){
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
      if (newValue.intValue() > oldValue.intValue()) {
          char ch = textField.getText().charAt(oldValue.intValue());
          // Check if the new character is the number or other's
          if (!(ch >= '0' && ch <= '9' )) {
               // if it's not number then just setText to previous one
               textField.setText(textField.getText().substring(0,textField.getText().length()-1)); 
          }
     }
}

});
    }

    private String getString() {
        return getItem() == null ? "" : getItem().toString();
    }
}


Callback<TableColumn<DailyDetails, String>, 
        TableCell<DailyDetails, String>> cellFactory
            = (TableColumn<DailyDetails, String> p) -> new NumberCell();
    dailyDebit.setCellFactory(cellFactory);

问题是我丢失了鼠标监听器cell.setOnMouseClicked !!! 我如何再次获得单元格来分配监听器???

2 个答案:

答案 0 :(得分:4)

只是为了将新api驱动到每个人的大脑中:一个完整​​的例子,其中一个稍微不同的TextFormatter(比在other answer中)可以识别Locale并且(很脏!)连接到核心TextFieldTableCell,可以用于任何自定义编辑TableCell:

/**
 * Example of how-to use a TextFormatter in a editing TableCell.
 */
public class CellFormatting extends Application {

    private Parent getContent() {
        ObservableList<IntData> data = FXCollections.observableArrayList(
                new IntData(1), new IntData(2), new IntData(3)
                );
        TableView<IntData> table = new TableView<>(data);
        table.setEditable(true);
        TableColumn<IntData, Integer> column = new TableColumn<>("Data");
        column.setCellValueFactory(new PropertyValueFactory("data"));
        // core default: will throw exception on illegal values
        // column.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));

        NumberFormat format = NumberFormat.getIntegerInstance();
        UnaryOperator<TextFormatter.Change> filter = c -> {
            if (c.isContentChange()) {
                ParsePosition parsePosition = new ParsePosition(0);
                // NumberFormat evaluates the beginning of the text
                format.parse(c.getControlNewText(), parsePosition);
                if (parsePosition.getIndex() == 0 ||
                        parsePosition.getIndex() < c.getControlNewText().length()) {
                    // reject parsing the complete text failed
                    return null;
                }
            }
            return c;
        };
       column.setCellFactory(c -> new ValidatingTextFieldTableCell<>(
            // note: each cell needs its own formatter
            // see comment by @SurprisedCoconut   
            new TextFormatter<Integer>(
            // note: should use local-aware converter instead of core!
            new IntegerStringConverter(), 0,
            filter)));
        table.getColumns().add(column);
        VBox box = new VBox(table);
        return box;
    }

    /**
     * TextFieldTableCell that validates input with a TextFormatter.
     * <p>
     * Extends TextFieldTableCell, accesses super's private field reflectively.
     * 
     */
    public static class ValidatingTextFieldTableCell<S, T> extends TextFieldTableCell<S, T> {

        private TextFormatter<T> formatter;
        private TextField textAlias;

        public ValidatingTextFieldTableCell() {
            this((StringConverter<T>)null);
        }

        public ValidatingTextFieldTableCell(StringConverter<T> converter) {
            super(converter);
        }

        public ValidatingTextFieldTableCell(TextFormatter<T> formatter) {
            super(formatter.getValueConverter());
            this.formatter = formatter;
        }

        /**
         * Overridden to install the formatter. <p>
         * 
         * Beware: implementation detail! super creates and configures
         * the textField lazy on first access, so have to install after
         * calling super.
         */
        @Override
        public void startEdit() {
            super.startEdit();
            installFormatter();
        }

        private void installFormatter() {
            if (formatter != null && isEditing() && textAlias == null) {
                textAlias = invokeTextField();
                textAlias.setTextFormatter(formatter);
            }
        }

        private TextField invokeTextField() {
            Class<?> clazz = TextFieldTableCell.class;
            try {
                Field field = clazz.getDeclaredField("textField");
                field.setAccessible(true);
                return (TextField) field.get(this);
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }


    }

    public static class IntData {
        IntegerProperty data = new SimpleIntegerProperty(this, "data");
        public IntData(int value) {
            setData(value);
        }

        public void setData(int value) {
            data.set(value);
        }

        public int getData() {
            return data.get();
        }

        public IntegerProperty dataProperty() {
            return data;
        }
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setScene(new Scene(getContent()));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}    

顺便说一下,格式化程序是从another question重用的,其中的任务是将输入限制为Spinner。

答案 1 :(得分:2)

在TextField上使用TextFormatter,如下所示:

TextFormatter<String> formatter = new TextFormatter<String>( change -> {
    change.setText(change.getText().replaceAll("[^0-9.,]", ""));
    return change; 

});
textField.setTextFormatter(formatter);

向上使用Java8u40。你看。 G。来自Oracle站点的TableView example作为基础。