我有一个可以通过按下按钮滚动的scrollPane,使用以下代码
@FXML
public void scrollUp(ActionEvent event) {
if (scrollPane.getVvalue() > scrollPane.getVmin()) {
scrollPane.setVvalue(scrollPane.getVvalue() - scrollPaneIncrement);
}
}
@FXML
public void scrollDown(ActionEvent event) {
if (scrollPane.getVvalue() < scrollPane.getVmax()) {
scrollPane.setVvalue(scrollPane.getVvalue() + scrollPaneIncrement);
}
}
按下按钮时,会从onAction事件中调用这些函数。
现在我需要在按下按钮的同时上下滚动scrollPane。
任何人都知道如何解决这个问题?
答案 0 :(得分:0)
使用AnimationTimer
更新每个渲染帧上的滚动位置。您可以使用isArmed()
方法检查按钮是否被按下,并相应地更新滚动条的值。
这是一个SSCCE:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class ScrollWhileButtonDown extends Application {
@Override
public void start(Stage primaryStage) {
ScrollPane scroller = new ScrollPane();
Pane pane = new Pane();
pane.setMinHeight(1000);
scroller.setContent(pane);
Button upButton = new Button("Up");
Button downButton = new Button("Down");
HBox controls = new HBox(10, upButton, downButton);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
Scene scene = new Scene(new BorderPane(scroller, null, null, controls, null), 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
final double scrollSpeed = 0.5 ; // scrollpane units per second
AnimationTimer timer = new AnimationTimer() {
private long lastUpdate = 0 ;
@Override
public void handle(long time) {
if (lastUpdate > 0) {
long elapsedNanos = time - lastUpdate ;
double elapsedSeconds = elapsedNanos / 1_000_000_000.0 ;
double delta = 0 ;
if (upButton.isArmed()) {
delta = -scrollSpeed * elapsedSeconds ;
}
if (downButton.isArmed()) {
delta = scrollSpeed * elapsedSeconds ;
}
double newValue =
clamp(scroller.getVvalue() + delta, scroller.getVmin(), scroller.getVmax());
scroller.setVvalue(newValue);
}
lastUpdate = time ;
}
};
timer.start();
}
private double clamp(double value, double min, double max) {
return Math.min(max, Math.max(min, value));
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
以下是我使用ReactFX(版本2.0-M2或更高版本)对@ James_D回答的变体。虽然它不是更简洁,但如果您熟悉EventStream
,则可能更容易阅读。它具有额外的好处,即(隐藏的)动画计时器仅在按下其中一个按钮时触发,而不是一直触发。这是通过在没有按下按钮时切换到观察特殊流never()
的技巧来实现的,这不会产生运行时成本。不滚动时,您可以观察到略低的CPU使用率。
import static org.reactfx.EventStreams.*;
import static org.reactfx.util.Tuples.*;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import org.reactfx.EventStream;
public class ScrollWhileButtonDown extends Application {
private static enum BTN { UP, DOWN, NONE }
private static final double scrollSpeed = 0.5 ; // scrollpane units per second
@Override
public void start(Stage primaryStage) {
ScrollPane scroller = new ScrollPane();
Pane pane = new Pane();
pane.setMinHeight(1000);
scroller.setContent(pane);
Button upButton = new Button("Up");
Button downButton = new Button("Down");
HBox controls = new HBox(10, upButton, downButton);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
Scene scene = new Scene(new BorderPane(scroller, null, null, controls, null), 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
EventStream<BTN> armedButtons = merge(
valuesOf(upButton .armedProperty()).map(armed -> armed ? BTN.UP : BTN.NONE),
valuesOf(downButton.armedProperty()).map(armed -> armed ? BTN.DOWN : BTN.NONE));
EventStream<Double> deltas = armedButtons.flatMap(btn -> {
switch(btn) {
case UP: return elapsedSeconds().map(sec -> sec * -scrollSpeed);
case DOWN: return elapsedSeconds().map(sec -> sec * scrollSpeed);
default: return never();
}
});
deltas.subscribe(delta -> scroller.setVvalue(scroller.getVvalue() + delta));
}
private static EventStream<Double> elapsedSeconds() {
return animationTicks()
.accumulate(t(0L, -1L), (state, now) -> state.map((d, last) -> {
return t(last == -1L ? 0L : now - last, now);
}))
.map(t -> t._1 / 1_000_000_000.0);
}
public static void main(String[] args) {
launch(args);
}
}