我引用的代码:Show / Hide a Node within a stage
遵循上一个有关自动隐藏的问题和答案:How to show / hide / auto hide a node
感谢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>
答案 0 :(得分:2)
您可以保留对PauseTransition
的引用,并为每个输入事件(或仅特定事件)调用playFromStart()
。这是playFromStart()
中的documentation:
从初始位置向前播放
Animation
。等效于
animation.stop(); animation.setRate = setRate(Math.abs(animation.getRate())); animation.jumpTo(Duration.ZERO); animation.play();
注意:
playFromStart()
是一个异步调用,Animation
可能不会立即开始。
您将要在事件 filter 中执行此操作,因为子节点可能会占用事件,这意味着它们不会到达事件 handler 。不要使用过滤器中的事件,因为这将阻止子节点接收事件。
请注意,这种方法只会在用户在其中进行某些操作时阻止状态容器隐藏。如果鼠标只是悬停在状态容器上而不执行任何操作,则状态容器仍将自动隐藏。如果您不想这样做,请考虑使用@coder's approach;如果您想无限期地暂停自动隐藏计时器,直到用户将焦点从状态容器移开,则该方法更有意义。我的方法更适合于在自上次输入以来经过 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
最初显示,并且自动隐藏功能直到隐藏后再次显示才起作用。
注意:上面的显示和隐藏使用相同的TranslateTransition
。通常,这可以使动画更流畅-尤其是在快速切换时。
答案 1 :(得分:2)
您可以将控件布尔值添加到控制器:
private boolean isStatusContainerBusy = false;
让感兴趣的事件切换它。例如,当鼠标进入时将其设置为true,而当鼠标退出时将其设置为false:
statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
并使用它来控制自动隐藏:
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();
}
}