如何在节点处于活动状态时停止隐藏该节点?
How do I stop the hiding of a node when it is active?
我参考的代码:Show / Hide a Node within a stage
继上一个关于自动隐藏的问答:
感谢c0der解决上一题。
它有问题,如果它处于活动状态(如移动光标或单击),Vbox 节点仍会自动隐藏。当我移动光标或发生某些事件时,如何使 Vbox 节点保持可见而不是隐藏?
当前的行为是,如果我移动光标或单击 VBox 节点上的某些内容,5 秒后它仍然会隐藏。
Testinggg.java:
package testinggg;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Testinggg extends Application {
private TestController controller;
@Override
public void start(Stage stage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Test.fxml"));
Parent root = loader.load();
controller = loader.getController();
stage.setScene(new Scene(root));
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
TestController.java:
package testinggg;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
public class TestController implements Initializable {
@FXML private VBox statusContainer;
private TranslateTransition showStatus;
private TranslateTransition hideStatus;
private boolean showsStatus = false;
private static final int AUTO_HIDE_DELAY = 5;
public void toggleStatus() {
if( showsStatus ) {
hide();
}
else {
show();
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
showStatus.setByY(-1080.0);
showStatus.setOnFinished(event -> {
showsStatus = true;
autoHide();
});
hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
hideStatus.setByY(1080.0);
hideStatus.setOnFinished(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
showsStatus = false;
}
});
}
private void show(){
hideStatus.stop();
showStatus.play();
}
private void hide(){
showStatus.stop();
hideStatus.play();
}
private void autoHide(){
Duration duration = Duration.seconds(AUTO_HIDE_DELAY);
PauseTransition transition = new PauseTransition(duration);
transition.setOnFinished(evt ->{
if( showsStatus ) {
hide();
}
});
transition.play();
}
}
Test.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testinggg.TestController">
<children>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" prefHeight="1080.0" prefWidth="1920.0" StackPane.alignment="TOP_LEFT">
<children>
<Button mnemonicParsing="false" onAction="#toggleStatus" prefHeight="1080.0" prefWidth="1920.0" text="Button" />
</children>
</AnchorPane>
<VBox fx:id="statusContainer" maxHeight="1080.0" prefHeight="1080.0" translateY="1080.0" StackPane.alignment="BOTTOM_LEFT">
<children>
<AnchorPane prefHeight="668.0" prefWidth="1266.0">
<VBox.margin>
<Insets bottom="50.0" left="50.0" right="50.0" top="50.0" />
</VBox.margin>
<children>
<ImageView fitHeight="540.0" fitWidth="1820.0" layoutY="43.0" pickOnBounds="true">
<image>
<Image url="@../Rainbow%20Poro.png" />
</image>
</ImageView>
<ImageView fitHeight="44.0" fitWidth="153.0" layoutX="857.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../logo.png" />
</image>
</ImageView>
<ScrollPane layoutY="582.0" prefHeight="391.0" prefViewportHeight="208.0" prefViewportWidth="1266.0" prefWidth="1820.0">
<content>
<TextArea editable="false" layoutY="460.0" prefHeight="515.0" prefWidth="1804.0" text="Sometexthere SometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthere Some Text Here Some Text Here Some Text Here Some Text Here Some Text Here Some Text HereSome Text HereSome Text HereSome Text HereSome Text HereSome Text HereSome Text Here Some Text Here" />
</content>
</ScrollPane>
<Button layoutX="1775.0" mnemonicParsing="false" onAction="#toggleStatus" text="Close" />
</children>
</AnchorPane>
</children>
</VBox>
</children>
<stylesheets>
<URL value="@test1.css" />
</stylesheets>
</StackPane>
您可以保留对 PauseTransition
的引用并为每个输入事件(或仅特定事件)调用 playFromStart()
。这是 playFromStart()
的 documentation:
Plays an Animation
from initial position in forward direction.
It is equivalent to
animation.stop();
animation.setRate = setRate(Math.abs(animation.getRate()));
animation.jumpTo(Duration.ZERO);
animation.play();
Note:
playFromStart()
is an asynchronous call, Animation
may not start immediately.
您需要在事件 filter 中执行此操作,因为子节点可能会使用事件,这意味着它们不会到达事件 handler。不要使用过滤器中的事件,因为这会阻止子节点接收它们。
请注意,此方法只会在用户在其中执行某些操作时阻止状态容器隐藏。如果鼠标只是悬停在状态容器上而没有做任何事情,那么状态容器仍将 auto-hide。如果您不想那样,请考虑使用 ;如果您想暂停 auto-hide 计时器 "indefinitely" 直到用户将焦点从状态容器移开,该方法更有意义。我的方法更适合在自上次输入后 X 时间量后隐藏状态容器,无论如何。当然,如果找到合适的用例,您总是可以将这两种方法结合起来。
这是一个小例子:
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<StackPane xmlns="http://javafx.com/javafx/12.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.Controller" prefWidth="600.0" prefHeight="400.0">
<VBox fx:id="statusContainer" prefHeight="150" maxHeight="-Infinity"
style="-fx-background-color: firebrick;" StackPane.alignment="BOTTOM_CENTER"/>
<Button fx:id="toggleStatusBtn" text="Hide Status" onAction="#handleToggleStatus"/>
</StackPane>
控制器:
package com.example;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.InputEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
public class Controller {
@FXML private Button toggleStatusBtn;
@FXML private VBox statusContainer;
private TranslateTransition showHideAnimation;
private PauseTransition autoHideTimer;
private boolean statusShowing = true;
@FXML
private void initialize() {
showHideAnimation = new TranslateTransition(Duration.millis(250), statusContainer);
showHideAnimation.setFromY(0);
showHideAnimation.toYProperty().bind(statusContainer.prefHeightProperty());
autoHideTimer = new PauseTransition(Duration.seconds(5));
autoHideTimer.setOnFinished(event -> hideStatusContainer());
statusContainer.addEventFilter(InputEvent.ANY, event -> {
if (statusShowing) {
autoHideTimer.playFromStart(); // restart timer
} else if (event.getEventType() != MouseEvent.MOUSE_EXITED) {
showStatusContainer(); // user did something while container was hiding
}
});
}
@FXML
private void handleToggleStatus(ActionEvent event) {
event.consume();
if (statusShowing) {
hideStatusContainer();
} else {
showStatusContainer();
}
}
private void hideStatusContainer() {
if (statusShowing) {
statusShowing = false;
showHideAnimation.setRate(1);
showHideAnimation.play();
toggleStatusBtn.setText("Show Status");
}
}
private void showStatusContainer() {
if (!statusShowing) {
statusShowing = true;
showHideAnimation.setRate(-1); // reverse animation
showHideAnimation.play();
autoHideTimer.playFromStart();
toggleStatusBtn.setText("Hide Status");
}
}
}
注意: statusContainer
最初显示,auto-hide 功能在隐藏然后显示之前不会启动再次.
注意:上面的隐藏和显示都使用了相同的TranslateTransition
。这通常会使动画更流畅,尤其是在快速切换时。
您可以向控制器添加一个控制布尔值:
private boolean isStatusContainerBusy = false;
让感兴趣的事件切换它。例如鼠标进入时设置为true,鼠标退出时设置为false:
statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
并用它来控制auto-hide:
transition.setOnFinished(evt ->{
if( showsStatus ) {
if(isStatusContainerBusy) {
transition.play(); //start auto hide transition again
}else{
hide();
}
}
});
综合起来:
public class TestController {
@FXML private VBox statusContainer;
private TranslateTransition showStatus;
private TranslateTransition hideStatus;
private boolean showsStatus = false;
private static final int AUTO_HIDE_DEALY = 5;
private boolean isStatusContainerBusy = false;
@FXML void initialize() {
showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
showStatus.setByY(-100.0);
showStatus.setOnFinished(event -> {
showsStatus = true;
autoHide();
});
hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
hideStatus.setByY(100.0);
hideStatus.setOnFinished(event -> showsStatus = false);
statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
}
public void toggleStatus() {
if( showsStatus ) {
hide();
}
else {
show();
}
}
private void show(){
hideStatus.stop();
showStatus.play();
}
private void hide(){
showStatus.stop();
hideStatus.play();
}
private void autoHide() {
Duration duration = Duration.seconds(AUTO_HIDE_DEALY);
PauseTransition transition = new PauseTransition(duration);
transition.setOnFinished(evt ->{
if( showsStatus ) {
if(isStatusContainerBusy) {
transition.play(); //start auto hide transition again
}else{
hide();
}
}
});
transition.play();
}
}
我参考的代码:Show / Hide a Node within a stage
继上一个关于自动隐藏的问答:
感谢c0der解决上一题。
它有问题,如果它处于活动状态(如移动光标或单击),Vbox 节点仍会自动隐藏。当我移动光标或发生某些事件时,如何使 Vbox 节点保持可见而不是隐藏?
当前的行为是,如果我移动光标或单击 VBox 节点上的某些内容,5 秒后它仍然会隐藏。
Testinggg.java:
package testinggg;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Testinggg extends Application {
private TestController controller;
@Override
public void start(Stage stage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Test.fxml"));
Parent root = loader.load();
controller = loader.getController();
stage.setScene(new Scene(root));
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
TestController.java:
package testinggg;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
public class TestController implements Initializable {
@FXML private VBox statusContainer;
private TranslateTransition showStatus;
private TranslateTransition hideStatus;
private boolean showsStatus = false;
private static final int AUTO_HIDE_DELAY = 5;
public void toggleStatus() {
if( showsStatus ) {
hide();
}
else {
show();
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
showStatus.setByY(-1080.0);
showStatus.setOnFinished(event -> {
showsStatus = true;
autoHide();
});
hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
hideStatus.setByY(1080.0);
hideStatus.setOnFinished(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
showsStatus = false;
}
});
}
private void show(){
hideStatus.stop();
showStatus.play();
}
private void hide(){
showStatus.stop();
hideStatus.play();
}
private void autoHide(){
Duration duration = Duration.seconds(AUTO_HIDE_DELAY);
PauseTransition transition = new PauseTransition(duration);
transition.setOnFinished(evt ->{
if( showsStatus ) {
hide();
}
});
transition.play();
}
}
Test.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testinggg.TestController">
<children>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" prefHeight="1080.0" prefWidth="1920.0" StackPane.alignment="TOP_LEFT">
<children>
<Button mnemonicParsing="false" onAction="#toggleStatus" prefHeight="1080.0" prefWidth="1920.0" text="Button" />
</children>
</AnchorPane>
<VBox fx:id="statusContainer" maxHeight="1080.0" prefHeight="1080.0" translateY="1080.0" StackPane.alignment="BOTTOM_LEFT">
<children>
<AnchorPane prefHeight="668.0" prefWidth="1266.0">
<VBox.margin>
<Insets bottom="50.0" left="50.0" right="50.0" top="50.0" />
</VBox.margin>
<children>
<ImageView fitHeight="540.0" fitWidth="1820.0" layoutY="43.0" pickOnBounds="true">
<image>
<Image url="@../Rainbow%20Poro.png" />
</image>
</ImageView>
<ImageView fitHeight="44.0" fitWidth="153.0" layoutX="857.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../logo.png" />
</image>
</ImageView>
<ScrollPane layoutY="582.0" prefHeight="391.0" prefViewportHeight="208.0" prefViewportWidth="1266.0" prefWidth="1820.0">
<content>
<TextArea editable="false" layoutY="460.0" prefHeight="515.0" prefWidth="1804.0" text="Sometexthere SometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthere Some Text Here Some Text Here Some Text Here Some Text Here Some Text Here Some Text HereSome Text HereSome Text HereSome Text HereSome Text HereSome Text HereSome Text Here Some Text Here" />
</content>
</ScrollPane>
<Button layoutX="1775.0" mnemonicParsing="false" onAction="#toggleStatus" text="Close" />
</children>
</AnchorPane>
</children>
</VBox>
</children>
<stylesheets>
<URL value="@test1.css" />
</stylesheets>
</StackPane>
您可以保留对 PauseTransition
的引用并为每个输入事件(或仅特定事件)调用 playFromStart()
。这是 playFromStart()
的 documentation:
Plays an
Animation
from initial position in forward direction.It is equivalent to
animation.stop(); animation.setRate = setRate(Math.abs(animation.getRate())); animation.jumpTo(Duration.ZERO); animation.play();
Note:
playFromStart()
is an asynchronous call,Animation
may not start immediately.
您需要在事件 filter 中执行此操作,因为子节点可能会使用事件,这意味着它们不会到达事件 handler。不要使用过滤器中的事件,因为这会阻止子节点接收它们。
请注意,此方法只会在用户在其中执行某些操作时阻止状态容器隐藏。如果鼠标只是悬停在状态容器上而没有做任何事情,那么状态容器仍将 auto-hide。如果您不想那样,请考虑使用
这是一个小例子:
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<StackPane xmlns="http://javafx.com/javafx/12.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.Controller" prefWidth="600.0" prefHeight="400.0">
<VBox fx:id="statusContainer" prefHeight="150" maxHeight="-Infinity"
style="-fx-background-color: firebrick;" StackPane.alignment="BOTTOM_CENTER"/>
<Button fx:id="toggleStatusBtn" text="Hide Status" onAction="#handleToggleStatus"/>
</StackPane>
控制器:
package com.example;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.InputEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
public class Controller {
@FXML private Button toggleStatusBtn;
@FXML private VBox statusContainer;
private TranslateTransition showHideAnimation;
private PauseTransition autoHideTimer;
private boolean statusShowing = true;
@FXML
private void initialize() {
showHideAnimation = new TranslateTransition(Duration.millis(250), statusContainer);
showHideAnimation.setFromY(0);
showHideAnimation.toYProperty().bind(statusContainer.prefHeightProperty());
autoHideTimer = new PauseTransition(Duration.seconds(5));
autoHideTimer.setOnFinished(event -> hideStatusContainer());
statusContainer.addEventFilter(InputEvent.ANY, event -> {
if (statusShowing) {
autoHideTimer.playFromStart(); // restart timer
} else if (event.getEventType() != MouseEvent.MOUSE_EXITED) {
showStatusContainer(); // user did something while container was hiding
}
});
}
@FXML
private void handleToggleStatus(ActionEvent event) {
event.consume();
if (statusShowing) {
hideStatusContainer();
} else {
showStatusContainer();
}
}
private void hideStatusContainer() {
if (statusShowing) {
statusShowing = false;
showHideAnimation.setRate(1);
showHideAnimation.play();
toggleStatusBtn.setText("Show Status");
}
}
private void showStatusContainer() {
if (!statusShowing) {
statusShowing = true;
showHideAnimation.setRate(-1); // reverse animation
showHideAnimation.play();
autoHideTimer.playFromStart();
toggleStatusBtn.setText("Hide Status");
}
}
}
注意: statusContainer
最初显示,auto-hide 功能在隐藏然后显示之前不会启动再次.
注意:上面的隐藏和显示都使用了相同的TranslateTransition
。这通常会使动画更流畅,尤其是在快速切换时。
您可以向控制器添加一个控制布尔值:
private boolean isStatusContainerBusy = false;
让感兴趣的事件切换它。例如鼠标进入时设置为true,鼠标退出时设置为false:
statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
并用它来控制auto-hide:
transition.setOnFinished(evt ->{
if( showsStatus ) {
if(isStatusContainerBusy) {
transition.play(); //start auto hide transition again
}else{
hide();
}
}
});
综合起来:
public class TestController {
@FXML private VBox statusContainer;
private TranslateTransition showStatus;
private TranslateTransition hideStatus;
private boolean showsStatus = false;
private static final int AUTO_HIDE_DEALY = 5;
private boolean isStatusContainerBusy = false;
@FXML void initialize() {
showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
showStatus.setByY(-100.0);
showStatus.setOnFinished(event -> {
showsStatus = true;
autoHide();
});
hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
hideStatus.setByY(100.0);
hideStatus.setOnFinished(event -> showsStatus = false);
statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
}
public void toggleStatus() {
if( showsStatus ) {
hide();
}
else {
show();
}
}
private void show(){
hideStatus.stop();
showStatus.play();
}
private void hide(){
showStatus.stop();
hideStatus.play();
}
private void autoHide() {
Duration duration = Duration.seconds(AUTO_HIDE_DEALY);
PauseTransition transition = new PauseTransition(duration);
transition.setOnFinished(evt ->{
if( showsStatus ) {
if(isStatusContainerBusy) {
transition.play(); //start auto hide transition again
}else{
hide();
}
}
});
transition.play();
}
}