带有TitledPane的JavaFX ScrollPane PannableProperty

时间:2014-12-20 11:16:21

标签: javafx scrollpane

我正在使用javafx构建一个GUI应用程序,当用户从任何地方拖动内容时,需要ScrollPane中的PannableProperty工作。

在oracle docs中,他们谈到" pannableProperty":

  

指定用户是否应该能够使用平移视口   老鼠。如果鼠标事件到达ScrollPane(即,如果鼠标   包含的节点或其子节点之一不阻止事件)   然后咨询pannable以确定是否应该使用事件   淘选。

所以我的问题是鼠标事件无法到达ScrollPane ..

任何人都知道如何使它成为可能?

这是一个测试它的简单代码:

        ScrollPane root = new ScrollPane();
        root.setHbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
        root.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
        root.setPannable(true);

        VBox v = new VBox(10);

        TitledPane c1 = new TitledPane("test: ", new HBox(new Label("test: "), new TextField()));
        HBox c2 = new HBox(new Label("we are just in HBox  "), new TextField());
        Label c3 = new Label("I'm just a label and pannableProperty works here");
        TitledPane c4 = new TitledPane("test4", new HBox(new Label("test: "), new TextField()));
        AnchorPane c5 = new AnchorPane();
        c5.setPrefSize(100, 100);

        v.getChildren().addAll(c1, c2, c3, c4, c5);

        root.setContent(v);

        Scene scene = new Scene(root, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.show();

1 个答案:

答案 0 :(得分:1)

另一个棘手的问题: - )

TitledPane的默认外观实现是SkinBase的子类,默认构造函数(由TitledPaneSkin调用)执行此操作(缩短版本):

protected SkinBase(final C control) {       
    // Default behavior for controls is to consume all mouse events
    consumeMouseEvents(true);
}

所以我们需要扭转这种局面,遗憾的是你必须对此进行反思:

    TitledPane c1 = new TitledPane("test: ", new HBox(new Label("test: "), new TextField()));
    c1.skinProperty().addListener((w,o,n)-> {
        if(n instanceof SkinBase) {
            SkinBase<?> skinbase = (SkinBase<?>) n;
            try {
                Method m = SkinBase.class.getDeclaredMethod("consumeMouseEvents", Boolean.TYPE);

                AccessController.doPrivileged((PrivilegedAction<Void>) () -> {

                    boolean wasAccessible = m.isAccessible();
                    try {
                        m.setAccessible(true);
                        m.invoke(skinbase, false);
                        return null;
                    }
                    catch(ReflectiveOperationException e) { throw new IllegalStateException(e); }
                    finally {
                        m.setAccessible(wasAccessible);
                    }
                });
            } catch (ReflectiveOperationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });

现在它应该可以工作,至少在我的测试应用程序中是这样。


编辑#1:

执行此操作会在鼠标操作期间重置焦点,这会使TitledPane无法使用。所以我们现在正在搞乱焦点系统:

ScrollPane root = new ScrollPane();
root.setFocusTraversable(false);

Scene scene = new Scene(root, 300, 300);
scene.focusOwnerProperty().addListener((w,o,n)->
    if(n == root && o != null) {
        o.requestFocus();
    }
});

基本上我们在这里做的是,如果新聚焦的元素是ScrollPane,我们会重新聚焦以前聚焦的组件。