如何在 JavaFX 中的 SplitPane Divider 上检测鼠标拖动事件

Posted

技术标签:

【中文标题】如何在 JavaFX 中的 SplitPane Divider 上检测鼠标拖动事件【英文标题】:How to detect mouse-dragged event on SplitPane Divider in JavaFX 【发布时间】:2021-01-18 06:39:51 【问题描述】:

我在 JavaFx 中使用 SplitPane 用分隔符分割两个节点。我正在寻找一种区分两种情况的方法。一种情况是分隔线在屏幕宽度变化时响应移动,第二种情况是我使用鼠标将分隔线拖动到某个位置。当使用鼠标拖动分隔线时,我需要向分隔线添加一个侦听器。我该怎么做?

【问题讨论】:

如果屏幕尺寸发生变化,每个分隔线的positionProperty不都保持不变(它们只是比例)吗?所以,虽然我没有对此进行测试,但监听这些属性应该只响应用户移动它们。 @James_D 我已经尝试过了。问题是当屏幕尺寸发生变化时,SplitPane 分隔器被设计为自动改变其位置,移动到屏幕边缘(实际发生调整大小的位置)。所以它不会停留在它的位置。如果是这样,那么问题就解决了。 【参考方案1】:

您需要一种方法来了解分隔线位置的变化何时是窗口调整大小的结果。或者,更正式地说,场景调整大小。

您可以在场景进行布局时使用addPreLayoutPulseListener 设置一个标志,然后在调整大小事件处理结束时使用Platform.runLater 清除该标志。虽然该标志为真,但您可以假设 SplitPane 分隔线位置的任何变化是由于场景布局,而不是由于用户操作。

这是一个例子:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.BorderPane;

public class DividerListenerTest
extends Application 
    private boolean windowResizing;

    @Override
    public void start(Stage stage) 
        Label left = new Label("Left");
        left.setMinSize(1, 1);
        left.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        left.setPadding(new Insets(200));

        Label right = new Label("Right");
        right.setMinSize(1, 1);
        right.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        right.setPadding(new Insets(200));

        SplitPane splitPane = new SplitPane(left, right);

        splitPane.getDividers().get(0).positionProperty().addListener(
            (o, oldPos, newPos) -> dividerResized(oldPos, newPos));

        Scene scene = new Scene(new BorderPane(splitPane));
        scene.addPreLayoutPulseListener(() -> 
            windowResizing = true;
            Platform.runLater(() -> 
                windowResizing = false;
            );
        );

        stage.setScene(scene);
        stage.setTitle("Divider Listener Test");
        stage.show();
    

    private void dividerResized(Number oldPos,
                                Number newPos) 
        if (windowResizing) 
            return;
        

        System.out.println("User moved divider position to " + newPos);
    

    public static class Main 
        public static void main(String[] args) 
            Application.launch(DividerListenerTest.class, args);
        
    

请注意,addPreLayoutPulseListener 是从 JavaFX 9 开始添加的,在旧版本中不可用。

Java 8

对于旧版本的 JavaFX,您可以通过覆盖场景根目录的 layoutChildren() 方法来完成相同的操作:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.BorderPane;

public class DividerListenerTest
extends Application 
    private boolean windowResizing;

    @Override
    public void start(Stage stage) 
        Label left = new Label("Left");
        left.setMinSize(1, 1);
        left.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        left.setPadding(new Insets(200));

        Label right = new Label("Right");
        right.setMinSize(1, 1);
        right.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        right.setPadding(new Insets(200));

        SplitPane splitPane = new SplitPane(left, right);

        splitPane.getDividers().get(0).positionProperty().addListener(
            (o, oldPos, newPos) -> dividerResized(oldPos, newPos));

        Scene scene = new Scene(new BorderPane(splitPane) 
            @Override
            protected void layoutChildren() 
                windowResizing = true;
                super.layoutChildren();
                Platform.runLater(() -> 
                    windowResizing = false;
                );
            
        );

        stage.setScene(scene);
        stage.setTitle("Divider Listener Test");
        stage.show();
    

    private void dividerResized(Number oldPos,
                                Number newPos) 
        if (windowResizing) 
            return;
        

        System.out.println("User moved divider position to " + newPos);
    

【讨论】:

感谢您的回答。我目前正在使用 Java8,有什么方法可以在 Java8 中执行此操作(或类似的操作)? @user14165899 更新了 Java 8 的答案。

以上是关于如何在 JavaFX 中的 SplitPane Divider 上检测鼠标拖动事件的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX: 布局SplitPane

JavaFX SceneBuilder:如何让textarea填充窗格?

vue-splitpane插件的使用

如何在 JavaFX 2.2 中绘制清晰、不透明的细线?

Ionic 2 SplitPane更改sass变量无法正常工作

Vue中 引入使用 vue-splitpane 实现窗格的拆分调节