使用FXML检测鼠标在JavaFX中何时“停止移动”

时间:2015-08-24 02:03:50

标签: java javafx

我希望能够检测鼠标何时移动以及何时不使用JavaFX移动到AnchorPane上。

我目前在我的窗格上有一个'鼠标移动事件',当移动鼠标时会输出一些文本,但是当鼠标不移动时还希望包含一些输出。
我仍然难以理解如何在保持相对简单的同时实现问题的后半部分。

每当鼠标移动时,下面输出“移动”,但当鼠标停止时,没有任何反应。我可以看到这是因为checkMovement仅在移动时被调用。

是否存在反函数或类似函数 - 这会反过来?

我相信我可以使用特定的事件处理程序和/或计时器来实现我想要的东西,但这对于看似非常简单的事情来说过于复杂。
我错过了一些完全明显的东西吗?
我只是很擅长使用FX,所以请随意指出我应该以不同的方式做任何事情。

修改 我忘了指定我使用FXML来定义控件。 我是否需要将此过程放在控制器(AppController.java)中,或者为了“易用”而将它留在Main.java中?
我怎么能这样做/有可能吗? 显然,mouseMovementMethod需要修改。

AppController.Java

public class AppController实现Initializable {

    boolean moved = false;

    @FXML 
    private AnchorPane anchorPane;

    @FXML
    private ImageView killpic;

    @FXML
    private ImageView processpic;

    @FXML
    private Text exit;

   @FXML
   private Button processAnimalButton;

   /*
    * ...Other Variables
    */



    @FXML 
    void mouseMovedMethod(Event event){
        //Mouse Movement Event
    }

    @FXML
    void closeMenu(Event event) {
        Stage stage = (Stage) exit.getScene().getWindow();
        stage.close();
    }

    @FXML
    void selectProcess(Event event) {
        Image killimg = new Image("/images/knife_blood.png");
        Image proimg = new Image("/images/p_selected.png");
        killpic.setImage(killimg);
        processpic.setImage(proimg);
    }

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        Image killimg = new Image("/images/knife_blood_selected.png");
        Image proimg = new Image("/images/p.png");
        killpic.setImage(killimg);
        processpic.setImage(proimg);
    }

}

Main.java:

//*Required imports*//

public class Main extends Application {
    Stage thestage;

    @Override
    public void start(Stage primaryStage) {
        try {
            AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("popup.fxml"));
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            thestage = primaryStage;    
            primaryStage.setScene(scene);
            primaryStage.show();

        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

2 个答案:

答案 0 :(得分:1)

创建MouseEvent.ANY的事件侦听器。在该事件侦听器中,您将自己的属性mouseX和mouseY设置为事件的鼠标位置值。除此之外,创建一个永久运行的AnimationTimer并检查鼠标坐标是否在“delta”ms的间隔内没有变化。如果他们没有,请调用您的处理代码。

示例:

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;


public class Main extends Application {

    Scene scene;
    MouseStatus mouseStatus = new MouseStatus();

    Label infoLabel;

    @Override
    public void start(Stage primaryStage) {

        Group root = new Group();

        infoLabel = new Label();
        root.getChildren().add( infoLabel);

        scene = new Scene(root, 640, 480);

        primaryStage.setScene(scene);
        primaryStage.show();

        addInputListeners();

    }


    private void addInputListeners() {

        scene.addEventFilter(MouseEvent.ANY, e -> {

            infoLabel.setText("Moving");

            mouseStatus.setX(e.getX());
            mouseStatus.setY(e.getY());
            mouseStatus.setPrimaryButtonDown(e.isPrimaryButtonDown());
            mouseStatus.setSecondaryButtonDown(e.isSecondaryButtonDown());

        });

        AnimationTimer loop = new AnimationTimer() {

            long deltaNs = 30_000_000;

            double currX;
            double currY;
            long currNs;

            double prevX;
            double prevY;
            long prevNs;

            @Override
            public void handle(long now) {

                currX = mouseStatus.x;
                currY = mouseStatus.y;
                currNs = now;

                if( currNs - prevNs > deltaNs) {

                    if( prevX == currX && prevY == currY) {
                        infoLabel.setText("Stopped");
                    }

                    prevX = currX;
                    prevY = currY;
                    prevNs = currNs;
                }

            }
        };
        loop.start();

    }



    public class MouseStatus {

        double x;
        double y;
        boolean primaryButtonDown;
        boolean secondaryButtonDown;

        public double getX() {
            return x;
        }
        public void setX(double x) {
            this.x = x;
        }
        public double getY() {
            return y;
        }
        public void setY(double y) {
            this.y = y;
        }
        public boolean isPrimaryButtonDown() {
            return primaryButtonDown;
        }
        public void setPrimaryButtonDown(boolean primaryButtonDown) {
            this.primaryButtonDown = primaryButtonDown;
        }
        public boolean isSecondaryButtonDown() {
            return secondaryButtonDown;
        }
        public void setSecondaryButtonDown(boolean secondaryButtonDown) {
            this.secondaryButtonDown = secondaryButtonDown;
        }


    }

    public static void main(String[] args) {
        launch(args);
    }

}

答案 1 :(得分:1)

这并不像你认为的那么简单的原因是"鼠标不移动的概念"没有你想象的那么明确。底层操作系统在一些深埋的内环中检测输入事件,例如鼠标的移动。该循环需要一段有限的时间才能运行,因此鼠标在循环运行时总是显得静止。操作系统将以较低的频率向JavaFX工具包报告这些移动,而JavaFX工具包可能会进一步限制那些移动的报告时间。

因此,要定义"鼠标不移动",你真的不得不求助于"在最后x"中没有检测到鼠标移动,其中{{ 1}}是时间的一些度量(实时,例如纳秒,或帧渲染的数量)。所以你真的必须以某种方式实际实现这个逻辑,并决定计算多少时间"不要移动"。

@MadProgrammer在评论中建议一个实现(我认为这个实现更简单),而@Roland建议另一个实现。

这是@ MadProgrammer建议的实现:

x

这是@Roland建议的实现:

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TrackMouseStopped extends Application {

    private final long MIN_STATIONARY_TIME = 100_000_000 ; // nanoseconds 

    @Override
    public void start(Stage primaryStage) {
        Pane pane = new Pane();

        BooleanProperty mouseMoving = new SimpleBooleanProperty();
        mouseMoving.addListener((obs, wasMoving, isNowMoving) -> {
           if (! isNowMoving) {
               System.out.println("Mouse stopped!");
           }
        });

        PauseTransition pause = new PauseTransition(Duration.millis(MIN_STATIONARY_TIME / 1_000_000));
        pause.setOnFinished(e -> mouseMoving.set(false));

        // Note: if you want to consider the mouse having moved for
        // other events (e.g. dragging), you can do
        // pane.addEventHandler(MouseEvent.ANY, e -> { ... }); here
        pane.setOnMouseMoved(e -> {
            mouseMoving.set(true);
            pause.playFromStart();
        });

        Scene scene = new Scene(pane, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}