【发布时间】:2016-12-08 22:37:07
【问题描述】:
单击并按住向上/向下箭头按钮时,Spinner 更新速度很慢。有没有什么办法可以提高变化速度?
当我用鼠标单击、单击、单击时,微调器值的变化与我单击时一样快。如果我在每次按键时使用键盘上的向上/向下箭头或按住向上/向下箭头键,它也会快速变化。当我单击并按住箭头按钮时,我希望值能够快速改变。
有人知道怎么做吗?
【问题讨论】:
单击并按住向上/向下箭头按钮时,Spinner 更新速度很慢。有没有什么办法可以提高变化速度?
当我用鼠标单击、单击、单击时,微调器值的变化与我单击时一样快。如果我在每次按键时使用键盘上的向上/向下箭头或按住向上/向下箭头键,它也会快速变化。当我单击并按住箭头按钮时,我希望值能够快速改变。
有人知道怎么做吗?
【问题讨论】:
SpinnerSkin 中的SpinnerBehavior 每 750 毫秒触发一次更新。不幸的是,如果不使用反射来访问private 成员,就无法简单地设置/修改此行为。因此,在没有反射的情况下做到这一点的唯一方法是使用事件过滤器以更快的速度触发更新:
private static final PseudoClass PRESSED = PseudoClass.getPseudoClass("pressed");
@Override
public void start(Stage primaryStage) {
Spinner<Integer> spinner = new Spinner(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
class IncrementHandler implements EventHandler<MouseEvent> {
private Spinner spinner;
private boolean increment;
private long startTimestamp;
private static final long DELAY = 1000l * 1000L * 750L; // 0.75 sec
private Node button;
private final AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
if (now - startTimestamp >= DELAY) {
// trigger updates every frame once the initial delay is over
if (increment) {
spinner.increment();
} else {
spinner.decrement();
}
}
}
};
@Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
Spinner source = (Spinner) event.getSource();
Node node = event.getPickResult().getIntersectedNode();
Boolean increment = null;
// find which kind of button was pressed and if one was pressed
while (increment == null && node != source) {
if (node.getStyleClass().contains("increment-arrow-button")) {
increment = Boolean.TRUE;
} else if (node.getStyleClass().contains("decrement-arrow-button")) {
increment = Boolean.FALSE;
} else {
node = node.getParent();
}
}
if (increment != null) {
event.consume();
source.requestFocus();
spinner = source;
this.increment = increment;
// timestamp to calculate the delay
startTimestamp = System.nanoTime();
button = node;
// update for css styling
node.pseudoClassStateChanged(PRESSED, true);
// first value update
timer.handle(startTimestamp + DELAY);
// trigger timer for more updates later
timer.start();
}
}
}
public void stop() {
timer.stop();
button.pseudoClassStateChanged(PRESSED, false);
button = null;
spinner = null;
}
}
IncrementHandler handler = new IncrementHandler();
spinner.addEventFilter(MouseEvent.MOUSE_PRESSED, handler);
spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
if (evt.getButton() == MouseButton.PRIMARY) {
handler.stop();
}
});
Scene scene = new Scene(spinner);
primaryStage.setScene(scene);
primaryStage.show();
}
【讨论】:
我稍微修改了fabian的答案,以降低按住鼠标时微调器的速度:
private int currentFrame = 0;
private int previousFrame = 0;
@Override
public void handle(long now)
{
if (now - startTimestamp >= initialDelay)
{
// Single or holded mouse click
if (currentFrame == previousFrame || currentFrame % 10 == 0)
{
if (increment)
{
spinner.increment();
}
else
{
spinner.decrement();
}
}
}
++currentFrame;
}
在停止计时器后,我们再次调整 previousFrame:
public void stop()
{
previousFrame = currentFrame;
[...]
}
【讨论】:
对 Fabian 的回答进行了小幅改进。对 MOUSE_RELEASED addEventerFilter 进行以下修改将停止单击与微调器关联的文本字段时导致的 NullPointerException。费边干杯!
spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
Node node = evt.getPickResult().getIntersectedNode();
if (node.getStyleClass().contains("increment-arrow-button") ||
node.getStyleClass().contains("decrement-arrow-button")) {
if (evt.getButton() == MouseButton.PRIMARY) {
handler.stop();
}
}
});
【讨论】:
在某些情况下,更改更新速度的替代方法可能是调整每次更新的值递增/递减量。
SpinnerValueFactory.IntegerSpinnerValueFactory intFactory =
(SpinnerValueFactory.IntegerSpinnerValueFactory) spinner.getValueFactory();
intFactory.setAmountToStepBy(100);
参考:http://news.kynosarges.org/2016/10/28/javafx-spinner-for-numbers/
【讨论】: