我有ScrollPane
,其中包含显示图片的TilePane
。每当我删除其中一个图像时,ScrollPane
会跳回到顶部,这在尝试删除多个图像时非常烦人。有没有办法控制滚动行为?
我在Windows 7上运行此代码:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class ScrollbarProblem extends Application{
private TilePane imagePane;
private ScrollPane scroller;
@Override
public void start(Stage primaryStage) {
imagePane = new TilePane();
scroller = new ScrollPane();
scroller.setContent(imagePane);
BorderPane root = new BorderPane(scroller, null, null, null, null);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
for(int i = 0; i < 20; i++){
Image img = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/SIPI_Jelly_Beans_4.1.07.tiff/lossy-page1-220px-SIPI_Jelly_Beans_4.1.07.tiff.jpg");
final ImageView imageView = new ImageView(img);
final Button button = new Button("Delete");
final StackPane stackPane = new StackPane();
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
imagePane.getChildren().remove(stackPane);
}
});
stackPane.getChildren().addAll(imageView, button);
imagePane.getChildren().add(stackPane);
}
}
public static void main(String[] args) {
launch(args);
}
}
答案 0 :(得分:3)
ScrollPane
确保在视口内显示焦点Node
。单击的Button
具有焦点。这意味着删除了焦点节点,JavaFX尝试将焦点移动到ScrollPane
内容中的另一个节点,在这种情况下是第一个Button
。 ScrollPane
将此节点滚动到视图中,这解释了观察到的行为。
解决方案是将每个focusTraversable
的{{1}}属性设置为Button
。
另一个仍允许您通过 Tab 更改焦点的选项将覆盖false
的{{1}}方法,因为这是导致此行为的方法:
onTraverse
答案 1 :(得分:2)
问题似乎是由于焦点恢复到磁贴窗格的第一个元素中的按钮引起的。一个解决方法是:
Image img = new Image(
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/SIPI_Jelly_Beans_4.1.07.tiff/lossy-page1-220px-SIPI_Jelly_Beans_4.1.07.tiff.jpg");
for (int i = 0; i < 20; i++) {
final ImageView imageView = new ImageView(img);
final Button button = new Button("Delete");
final StackPane stackPane = new StackPane();
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
int index = imagePane.getChildren().indexOf(stackPane);
imagePane.getChildren().remove(index);
int numPanes = imagePane.getChildren().size();
if (numPanes > 0) {
imagePane.getChildren().get(Math.min(index, numPanes-1)).requestFocus();
}
}
});
stackPane.getChildren().addAll(imageView, button);
imagePane.getChildren().add(stackPane);
}
答案 2 :(得分:0)
此答案基于Fabian答案。在某些JavaFx imp中,onTraverse
上没有ScrollPaneSkin
方法(请参阅javafx.scene.control.skin.ScrollPaneSkin与javafx.scene.control.skin.ScrollPaneSkin),因此它不可能是@Override
,因此我致力于通用解决方案,其中将不包括程序员每次更改按钮的要求,但可以从根本上解决问题,并可以设置FocusTravers
= false
,无论如何Node
(Button
imp)已添加到视图树。
此帮助程序类:
public class FocusTraversHelper {
static private ListChangeListener listChangeListener;
static {
listChangeListener=new ListChangeListener<Node>() {
@Override
public void onChanged(Change<? extends Node> c) {
if(c.next()&&c.wasAdded()){
Node b =c.getAddedSubList().get(0);
if(b!=null&&(b instanceof Parent || b instanceof Button) ){
cancelFocusTravers(b);
}
}
}};
}
static private void registerAddEvent(Parent parent){
parent.getChildrenUnmodifiable().removeListener(listChangeListener);
parent.getChildrenUnmodifiable().addListener(listChangeListener);
}
static public void cancelFocusTravers(Node node){
if(node instanceof Parent){
registerAddEvent((Parent)node);
for (Node n: ((Parent) node).getChildrenUnmodifiable()) {
cancelFocusTravers(n);
}
}
if(node instanceof Button){
node.setFocusTraversable(false);
}
}
}
用途类似于:
FocusTraversHelper.cancelFocusTravers(root);
希望它将对某人有所帮助
注意:如果还有其他类似TextField
的控件,则应对其进行修改以使其起作用(也许使用Node
而不是Button
)。
注意:在多次删除和添加同一节点的情况下,需要使用remove-> add,因此只有一个侦听器