我正在使用JavaFX构建GUI,在调整鼠标大小时,我在知道鼠标位置方面遇到了问题。其想法是,如果鼠标位于GUI的边缘,它将变为一个双面箭头,指示您现在可以按下鼠标并调整窗口大小。
我需要鼠标指针在此边缘的位置,但我不知道如何做到这一点。我需要知道窗口的大小调整方向。
更新添加了新选项并讨论了优缺点。
这很棘手。问题是窗口会跟踪其顶部、左侧、宽度和高度。当它从右边或底部调整大小时,事情很容易:宽度或高度会发生变化。但当从左侧调整其大小时,x和宽度都必须更改。这两个变化不是原子性的,因为x和width存储为两个独立的属性。
第一种方法是跟踪鼠标坐标,看看它是在左半部分还是在右半部分。(很明显,你可以对高度做同样的事情。)这种方法与任何实现细节无关,但用户可能会因为非常小心地使用鼠标而导致它失败。如果将鼠标移动到窗口的右边缘,精确到窗口边界的像素,然后调整大小,则可能会看到不正确的输出。
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
class MouseLocation {
double x,y ;
}
MouseLocation mouseLocation = new MouseLocation();
scene.setOnMouseMoved(event -> {
mouseLocation.x = event.getX();
mouseLocation.y = event.getY();
});
primaryStage.widthProperty().addListener((obs, oldWidth, newWidth) -> {
if (mouseLocation.x < primaryStage.getWidth() / 2) {
System.out.println("Resized from left");
} else {
System.out.println("Resized from right");
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
第二种方法是跟踪窗口的最后一个已知水平范围(minX和maxX),并在宽度变化时更新该范围。然后您可以检查minX或maxX是否已更改。这种方法的问题在于它依赖于未记录的实现细节。它显示(在我的系统上,使用当前版本等),当从左侧调整窗口大小时,首先更改x,然后更改宽度。如果在随后的版本中发生变化,以下内容将被破坏:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
class MutableDouble {
double value ;
}
MutableDouble windowLeftEdge = new MutableDouble();
primaryStage.widthProperty().addListener((obs, oldWidth, newWidth) -> {
if (primaryStage.getX() == windowLeftEdge.value) {
System.out.println("Resized from right");
} else {
System.out.println("Resized from left");
}
windowLeftEdge.value = primaryStage.getX() ;
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
第三种方法(我能想到)是将快速发生的变化合并为一个单一的变化。这对于正确编程来说有点棘手,所以我没有从头开始,而是使用了第三方ReactFX框架,该框架对"事件流"进行建模,并有一个内置机制来组合快速连续发生的事件。这可能是本文提出的三种解决方案中最稳健的一种,但代价是一定程度的复杂性或包含外部框架。
import java.time.Duration;
import org.reactfx.Change;
import org.reactfx.EventStream;
import org.reactfx.EventStreams;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ReactFXVersion extends Application {
@Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
ObservableValue<Bounds> windowBounds = Bindings.createObjectBinding(() ->
new BoundingBox(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight()),
primaryStage.xProperty(), primaryStage.yProperty(), primaryStage.widthProperty(), primaryStage.heightProperty());
EventStream<Change<Bounds>> bounds = EventStreams.changesOf(windowBounds)
.reduceSuccessions((previousChange, nextChange) ->
new Change<>(previousChange.getOldValue(), nextChange.getNewValue()),
Duration.ofMillis(10));
bounds.subscribe(boundsChange -> {
Bounds newBounds = boundsChange.getNewValue();
Bounds oldBounds = boundsChange.getOldValue();
if (newBounds.getWidth() != oldBounds.getWidth()) {
if (newBounds.getMinX() != oldBounds.getMinX()) {
System.out.println("Resized from left");
} else if (newBounds.getMaxX() != oldBounds.getMaxX()) {
System.out.println("Resized from right");
}
}
if (newBounds.getHeight() != oldBounds.getHeight()) {
if (newBounds.getMinY() != oldBounds.getMinY()) {
System.out.println("Resized from top");
} else if (newBounds.getMaxY() != oldBounds.getMaxY()) {
System.out.println("Resized from bottom");
}
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}