使用JavaFx制作数字微调器。主要提供一种思路将代码继承到自己的项目中。
父类RootView完成初始化任务,初始化FXML,调用 initializeData(),initializeView()
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.math.BigDecimal;
public class NumberSpinnerView<T extends Number> extends RootView implements ChangeListener<String> {
@FXML
private TextField textFieldNumber;
@FXML
private Button buttonSubtraction;
@FXML
private Button buttonAdd;
private Number minValue;
private Number maxValue;
// 标度值/精度/保留小数位
private Integer scaleValue;
// 分度值
private Number scaleInterVal;
public NumberSpinnerView() {
this(null, null);
}
/**
* @param minValue 最小值
* @param maxValue 最大值
*/
public NumberSpinnerView(Number minValue, Number maxValue) {
this(minValue, maxValue, null);
}
/**
* @param minValue 最小值
* @param maxValue 最大值
* @param scaleValue 标度值/精度/保留小数位
*/
public NumberSpinnerView(Number minValue, Number maxValue, Integer scaleValue) {
this(minValue, maxValue, scaleValue, null);
}
/**
* @param minValue 最小值
* @param maxValue 最大值
* @param scaleValue 标度值/精度/保留小数位
* @param scaleInterVal 分度值
*/
public NumberSpinnerView(Number minValue, Number maxValue, Integer scaleValue, Number scaleInterVal) {
this.setNumberSpinnerValue(minValue, maxValue, scaleValue, scaleInterVal);
}
// 根据标度计算分度值
private Number getScaleInterValByScaleValue() {
Number scaleInterVal = new Double(1);
for (int i = 0; i < this.scaleValue; i++) {
scaleInterVal = (double) scaleInterVal / 10;
}
return scaleInterVal;
}
// 按钮禁用绑定
private void refreshButtonDisabled() {
BigDecimal number = this.toBigDecimalFromString(this.getTextFieldNumber());
int compareMax = number.compareTo(this.toBigDecimalFromString(this.maxValue.toString()));
int compareMin = number.compareTo(this.toBigDecimalFromString(this.minValue.toString()));
this.buttonAdd.setDisable(compareMax != -1);
this.buttonSubtraction.setDisable(compareMin != 1);
}
@Override
protected void initializeData() {
this.minValue = 0;
this.maxValue = Long.MAX_VALUE;
this.scaleValue = BigDecimal.ROUND_UP;
this.scaleInterVal = this.getScaleInterValByScaleValue();
this.setTextFieldNumber(this.minValue);
}
@Override
protected void initializeView() {
this.getNumberProperty().addListener(this);
this.setPrepareImage();
}
public void setPrepareImage() {
Image imageButtonAdd = ResourceUtil.getImage("add", this.getClass().getClassLoader());
if (imageButtonAdd == null) {
Logger.error("Failed while getting add image");
} else {
this.buttonAdd.setGraphic(new ImageView(imageButtonAdd));
}
Image imageButtonSubtraction = ResourceUtil.getImage("sub", this.getClass().getClassLoader());
if (imageButtonSubtraction == null) {
Logger.error("Failed while getting sub image");
} else {
this.buttonSubtraction.setGraphic(new ImageView(imageButtonSubtraction));
}
}
@FXML
private void onClickAdd() {
BigDecimal oldValue = this.toBigDecimalFromString(this.getTextFieldNumber());
BigDecimal newValue = oldValue.add(new BigDecimal(this.scaleInterVal.toString()));
newValue = newValue.min(new BigDecimal(this.maxValue.toString()));
this.setTextFieldNumber(newValue);
}
@FXML
private void onClickSubtraction() {
BigDecimal oldValue = this.toBigDecimalFromString(this.getTextFieldNumber());
BigDecimal newValue = oldValue.subtract(new BigDecimal(this.scaleInterVal.toString()));
newValue = newValue.max(new BigDecimal(this.minValue.toString()));
this.setTextFieldNumber(newValue);
}
public void setTextFieldNumberEditable(boolean editable) {
this.textFieldNumber.setEditable(editable);
}
public void setNumberSpinnerValue(Number minValue, Number maxValue, Integer scaleValue, Number scaleInterVal) {
if (minValue != null) {
this.minValue = minValue;
}
if (maxValue != null) {
this.maxValue = maxValue;
}
if (scaleValue != null) {
this.scaleValue = scaleValue;
}
this.scaleInterVal = this.getScaleInterValByScaleValue();
if (scaleInterVal != null) {
this.scaleInterVal = scaleInterVal;
}
this.setTextFieldNumber(this.minValue);
}
public String getTextFieldNumber() {
return this.textFieldNumber.getText();
}
public BigDecimal toBigDecimalFromString(String value) {
BigDecimal decimal = new BigDecimal(value);
return decimal.setScale(this.scaleValue, BigDecimal.ROUND_DOWN);
}
public void setTextFieldNumber(Number value) {
this.textFieldNumber.setText(value.toString());
}
public StringProperty getNumberProperty() {
return this.textFieldNumber.textProperty();
}
public void selectAllText() {
this.textFieldNumber.selectAll();
}
public void requestFocusText() {
this.textFieldNumber.requestFocus();
}
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
NumberSpinnerView<T> self = NumberSpinnerView.this;
try {
BigDecimal value = self.toBigDecimalFromString(newValue);
value = value.min(new BigDecimal(self.maxValue.toString()));
value= value.max(new BigDecimal(self.minValue.toString()));
String num = (value + ".") == newValue ? newValue : value.toString();
self.textFieldNumber.setText(num);
} catch (Exception e) {
self.textFieldNumber.setText(oldValue);
}
self.refreshButtonDisabled();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.*?>
<fx:root maxWidth="1.7976931348623157E308" type="VBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<HBox maxWidth="1.7976931348623157E308" VBox.vgrow="ALWAYS">
<children>
<TextField fx:id="textFieldNumber" maxWidth="1.7976931348623157E308" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets />
</HBox.margin>
</TextField>
<Button fx:id="buttonSubtraction" disable="true" mnemonicParsing="false" onAction="#onClickSubtraction" />
<Button fx:id="buttonAdd" mnemonicParsing="false" onAction="#onClickAdd" />
</children>
</HBox>
</children>
</fx:root>
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.Callback;
import javafx.util.StringConverter;
import javafx.util.converter.IntegerStringConverter;
public class NumberSpinnerCell<S, T extends Number> extends TableCell<S, T> {
private ObjectProperty<StringConverter<T>> converter;
public static <S, T extends Number> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn() {
return forTableColumn((StringConverter) new IntegerStringConverter());
}
public static <S, T extends Number> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(StringConverter<T> var0) {
return forTableColumn(var0, null, null);
}
public static <S, T extends Number> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(
StringConverter<T> var0, Number minValue, Number maxValue) {
return forTableColumn(var0, minValue, maxValue, null, null);
}
/**
* @param var0 类型转换器
* @param minValue 最小值
* @param maxValue 最大值
* @param scaleValue 标度值/精度/保留小数位
* @param scaleInterVal 分度值
* @param <S>
* @return
*/
public static <S, T extends Number> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(
StringConverter<T> var0, final Number minValue, final Number maxValue, final Integer scaleValue, final Number scaleInterVal) {
return new Callback<TableColumn<S, T>, TableCell<S, T>>() {
@Override
public TableCell<S, T> call(TableColumn<S, T> param) {
return new NumberSpinnerCell<S, T>(var0, minValue, maxValue, scaleValue, scaleInterVal);
}
};
}
private NumberSpinnerView numberSpinnerView;
public NumberSpinnerCell() {
this((StringConverter<T>) new IntegerStringConverter(), null, null);
}
public NumberSpinnerCell(StringConverter<T> var1, Number minValue, Number maxValue) {
this(var1, minValue, maxValue, null, null);
}
public NumberSpinnerCell(StringConverter<T> var1, Number minValue, Number maxValue, Integer scaleValue, Number scaleInterVal) {
this.converter = new SimpleObjectProperty<>(this, "converter");
this.setConverter(var1);
this.numberSpinnerView = new NumberSpinnerView(minValue, maxValue, scaleValue, scaleInterVal);
setGraphic(null);
this.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
NumberSpinnerCell<S, T> self = NumberSpinnerCell.this;
if (event.getCode() == KeyCode.ENTER) {
String number = self.getNumberSpinnerView().getTextFieldNumber();
self.commitEdit(self.getConverter().fromString(number));
} else if (event.getCode() == KeyCode.ESCAPE) {
self.cancelEdit();
}
event.consume();
}
});
}
public final ObjectProperty<StringConverter<T>> converterProperty() {
return this.converter;
}
public final void setConverter(StringConverter<T> var1) {
this.converterProperty().set(var1);
}
public final StringConverter<T> getConverter() {
return (StringConverter<T>) this.converterProperty().get();
}
@Override
public void startEdit() {
if (!this.isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
this.setText(null);
this.setGraphic(this.getNumberSpinnerView());
this.getNumberSpinnerView().setTextFieldNumber(this.getItem());
this.getNumberSpinnerView().selectAllText();
this.getNumberSpinnerView().requestFocusText();
}
}
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
this.setText(null);
this.setGraphic(null);
} else {
this.setText(this.myText());
}
}
private String myText() {
if (this.getItem() != null) {
return this.getItem().toString();
} else {
return "";
}
}
@Override
public void commitEdit(T newValue) {
super.commitEdit(newValue);
this.setItem(newValue);
this.setGraphic(null);
this.setText(this.myText());
}
@Override
public void cancelEdit() {
super.cancelEdit();
this.setGraphic(null);
this.setText(this.myText());
}
public NumberSpinnerView getNumberSpinnerView() {
return this.numberSpinnerView;
}
}
通过调用接口
this.columnID.setCellValueFactory(new PropertyValueFactory<PermissionFilter, Double>("columnID"));
this.columnID.setCellFactory(NumberSpinnerCell.<PermissionFilter, Double>forTableColumn(new DoubleStringConverter(), -1, 1, BigDecimal.ROUND_DOWN, null));
public static <S, T extends Number> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(
StringConverter<T> var0, final Number minValue, final Number maxValue, final Integer scaleValue, final Number scaleInterVal)
var0 :为继承StringConverter<T> 的类型转换器,默认为IntegerStringConverter
minValue:设置数字微调器的最小值, 默认为0
maxValue:设置数字微调器的最大值,默认为 Long.MAX_VALUE
scaleValue:设置数字微调器的标度值/保留几个小数位,默认值为 (BigDecimal.ROUND_UP)0
scaleInterVal:设置数字微调器的分度值,默认根据scaleValue标度值计算的一个单位