好吧,继承我的情况。当编辑器为空时,我需要禁用两个微调按钮,这是我需要完成这个“自定义”组件的最后一块。继承我的SSCCE。
焦点丢失时:默认值设置为零,文本更新。
它只接受带有2位小数的十进制值,它只接受货币或百分比值。
没有别的东西可以补充。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class test extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
VBox v = new VBox();
v.setPadding(new Insets(20));
Spinner<Double> spinner = new Spinner<>();
spinner.setEditable(true);
Button dummy = new Button("dummy focus");
v.getChildren().addAll(spinner,dummy);
//----------------------------------HERE IS EVERYTHING RELATED TO THE SPINNER---------------------------------------------
spinner.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0, 100));
spinner.getValueFactory().setValue(0.0);
spinner.getEditor().textProperty().addListener((obs,old,gnu)->{
if(gnu.isEmpty()) {
System.out.println("empty, buttons should be disabled here, they will be disabled after this ");
spinner.getValueFactory().setValue(0.0);
return;
}
System.out.println("enabling buttons");
if(!gnu.matches("^\\d*\\.?\\d*$")) {
try {
spinner.getEditor().setText(old);
spinner.getValueFactory().setValue(Double.parseDouble(old));
}catch (NumberFormatException e) {
System.out.println("invalid string, previous value was empty, no biggie you are safe: Current value : "+spinner.getValueFactory().getValue());
}
} else {
if((Double.parseDouble(gnu)*100)%1!=0) {
spinner.getEditor().setText(old);
spinner.getValueFactory().setValue(Double.parseDouble(old));
}
/*
* You can use this to validate inside a range, for example. PERCENTAGES : 0 ~ 100
*
double val = Double.parseDouble(gnu)*100;
if(val%1!=0 || val>10000 || val<0) {
spinner.getEditor().setText(old);
spinner.getValueFactory().setValue(Double.parseDouble(old));
}
*/
}
});
spinner.getEditor().setOnKeyPressed(e->{
switch (e.getCode()) {
case UP:
spinner.increment(1);
break;
case DOWN:
spinner.decrement(1);
break;
default:
break;
}
});
spinner.setOnScroll(e->{
if(e.getDeltaY()>0)
spinner.increment(1);
else
spinner.decrement(1);
});
spinner.getEditor().focusedProperty().addListener((obs,old,niu)->{
if(!niu && spinner.getEditor().getText().isEmpty()) {
spinner.getEditor().setText("0");
spinner.getValueFactory().setValue(0.0);
}
});
//-----------------------------------------------------------------------------------------------------------------------------------------
Scene sc = new Scene(v);
primaryStage.setScene(sc);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
编辑:
它也会发生在按键和滚动事件中。
我担心你不能只禁用微调器的按钮。但是在Editor
(实际上是TextField
)之后设置值是空的呢?通过使用这样的解决方案,您在单击按钮后不会出现任何异常 - 值仅从0
增加。我修改了你的gnu.isEmpty()
代码。
if(gnu.isEmpty()) {
System.out.println("empty, buttons should be disabled here, they will be disabled after this ");
double valueToSet = 0.0;
spinner.getValueFactory().setValue(valueToSet);
Platform.runLater(() -> spinner.getEditor().setText(Double.toString(valueToSet)));
return;
}
另一件事是,你的代码允许将'0'作为第一个数字,即使之后还有另一个数字。检查代码,应该解决问题(用if(!gnu.matches("^\\d*\\.?\\d*$"))
开头的整个if / else语句交换):
if (!isDouble(gnu)) {
gnu = old;
}
spinner.getEditor().setText(gnu);
isDouble
是一种方法:
private boolean isDouble(String string) {
boolean startsWithZero =
string.startsWith("0") &&
(string.length() > 1) &&
(!string.startsWith("0."));
boolean minusZeroCondition =
string.startsWith("-0") &&
(string.length() > 2) &&
(!string.startsWith("-0."));
boolean containsTypeSpecificLetters =
Pattern.matches(".*[a-zA-Z].*", string);
boolean isEmpty = string.equals("");
boolean isMinus = string.equals("-");
try {
Double.parseDouble(string);
return !(startsWithZero || minusZeroCondition || containsTypeSpecificLetters);
} catch (IllegalArgumentException exception) {
return isEmpty || isMinus;
}
}
我也在寻找一种只禁用按钮的方法。我需要禁用更改Spinner
的值,同时仍然允许复制/粘贴,所以我做了一些挖掘源代码。
我发现,虽然按钮实际上不是Spinner
对象本身的一部分,但它们是Spinner
的SpinnerSkin
(它们也不是Button
s,但是StackPane
s)的一部分,所以我设法禁用以下按钮(在Kotlin):
val editing = SimpleBooleanProperty(false)
val spinner = Spinner<Int>()
spinner.skinProperty().addListener { observable, oldValue, newValue ->
// only bind if the skin is an instance of `SpinnerSkin`
if (newValue != null && newValue is SpinnerSkin<*>) {
(skin as SpinnerSkin<*>).children
// only select the children that are `StackPane`s (the buttons)
.filter { it is StackPane }
// bind the `disableProperty` of the buttons to our property for whether we're editing
.forEach { disableProperty().bind(editing.not()) }
}
}
我不得不听取属性更改,因为skinProperty未在初始化时设置,但仅在CSS处理完毕后才设置。如果你完全确定你的微调器已经显示并且设置了皮肤,你可以改为调用getSkin
。