JavaFX:如何为窗格创建幻灯片动画效果(在透明舞台内)

时间:2015-07-24 03:53:07

标签: javafx

我想要一些关于如何在用户按下按钮时为窗格实现幻灯片转换的指南,就像Material Design为滑动菜单做的那样。

这是一段视频link,说明了我的需求。

我尝试过ScaleTransition,TranslateTransition,但他们没有做到这一点。

我试图实现它的方式效率不高。

package com.helloworld;

import com.gluonhq.charm.glisten.animation.ShrinkExpandAnimation;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {

    ShrinkExpandAnimation anim;

    @Override
    public void start(Stage stage) {        
        Button btn = new Button("Click Me!");
        btn.setOnMouseClicked(e-> {
            System.out.println("swiping...");
            anim.play();
        });

        AnchorPane pane = new AnchorPane();
        pane.setStyle("-fx-background-color: coral");
        pane.getChildren().add(btn);

        // false to shrink or true to expand
        anim = new ShrinkExpandAnimation(pane, false);

        Scene scene = new Scene(new StackPane(pane), 640, 480);

        stage.setScene(scene);
        stage.show();
    }
}

---更新---

以下是从here下载的Gluon应用示例。这是一个gradle项目,我修改它以显示一个按钮而不是默认标签。

我希望在用户点击按钮时缩小AnchorPane。

我错过了什么?

public class SwipeMenuDemo extends Application {

    AnchorPane swapPane;
    Button btnMenu;
    boolean isExpanded = true;

    @Override
    public void start(Stage stage) {    
        Label swapPaneLabel = new Label("Expandable Pane");
        swapPaneLabel.setMinWidth(0);

        ImageView swapPaneImage = new ImageView("http://vignette1.wikia.nocookie.net/jfx/images/5/5a/JavaFXIsland600x300.png");
        swapPaneImage.setLayoutY(100);

        Label rootPaneLabel = new Label("Root Pane");
        rootPaneLabel.setStyle("-fx-font-size: 60;");
        rootPaneLabel.setLayoutX(180);
        rootPaneLabel.setLayoutY(180);

        swapPane = new AnchorPane();
        swapPane.setPrefSize(640, 440);
        swapPane.setMinWidth(0);
        swapPane.setLayoutY(40);
        swapPane.setStyle("-fx-background-color: coral; -fx-font-size: 52;");
        swapPane.getChildren().addAll(swapPaneImage, swapPaneLabel);

        btnMenu = new Button("Menu");
        btnMenu.setLayoutX(5);
        btnMenu.setLayoutY(5);
        btnMenu.setOnMouseClicked(e -> {
            if (isExpanded) hideSwapPane().play();
            else showSwapPane().play();
        });

        Button btnClose = new Button("Close");
        btnClose.setLayoutX(590);
        btnClose.setLayoutY(5);
        btnClose.setOnMouseClicked(e -> Platform.exit());

        AnchorPane rootPane = new AnchorPane();
        rootPane.setStyle("-fx-background-color: grey;");
        rootPane.getChildren().addAll(btnMenu, btnClose, rootPaneLabel, swapPane);

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

        stage.setScene(scene);
        stage.initStyle(StageStyle.UNDECORATED);
        stage.show();
    }

    private Animation hideSwapPane() {            
        btnMenu.setMouseTransparent(true);

        Animation collapsePanel = new Transition() {
            {
                setCycleDuration(Duration.millis(2500));
            }

            @Override
            protected void interpolate(double fraction) {
                swapPane.setPrefWidth(640 * (1.0 - fraction));
            }
        };

        collapsePanel.setOnFinished(e-> {
            isExpanded = false;
            btnMenu.setMouseTransparent(false);
        });

        return collapsePanel;
    }

    private Animation showSwapPane() {            
        btnMenu.setMouseTransparent(true);

        final Animation expandPanel = new Transition() {
            {
                setCycleDuration(Duration.millis(2500));
            }

            @Override
            protected void interpolate(double fraction) {
                swapPane.setPrefWidth(640 * fraction);
            }
        };

        expandPanel.setOnFinished(e-> {
            isExpanded = true;
            btnMenu.setMouseTransparent(false);
        });

        return expandPanel;
    }
}

---更新2 ---

我设法使用本机JavaFX API实现类似于我想要的东西,而没有外部库。

虽然,我遇到了一些问题。

  1. 缩小AnchorPane不会缩小/移动任何子节点,因为它们会保持在布局位置。
  2. 缩小除AnchorPane之外的任何其他窗格除了从ImageView节点外,它还缩小/移动其子节点。
  3. 接下来的两张图片说明了我遇到的第一个问题。

    这是一个AnchorPane(珊瑚色全宽;展开),位于AnchorPane(灰色的根窗格)内。

    expanded

    当您单击“菜单”按钮缩小/隐藏它时会发生这种情况。如您所见,珊瑚色窗格收缩/隐藏,但不是它的节点(Label,ImageView)

    collapsed

    我发布了整个代码来自己重现这个问题:

    {{1}}

    ---更新3 ---

    根据我的需要修改了Felipe Guizar Diaz为我提供的代码,因为我想在透明的舞台窗口上设置一个阴影效果。

    当我点击菜单按钮显示左侧窗格时,它会显示在阴影前面。即使在SceneBuilder中,我已经将StackPane放置在所有节点前面。

    当我按下显示菜单并开始播放开放转换时,这是“神器”...

    我该如何解决?

    shadow artifact

2 个答案:

答案 0 :(得分:18)

我是示例视频的作者。我将重复我在视频评论中所做的回复: “你应该把它想象成android中的导航抽屉,JavaFX中的导航抽屉将是一个AnchorPane,有2个孩子,首先是StackPane,相当于FrameLayout作为我们的工作主要内容,其中窗格的转换取决于左侧菜单中的所选项目,最终ListView作为我们的左侧菜单,其中translateXListview,等于translateX然后,当用户按下按钮时,您必须播放一个动画,其值为0prefWidth()。“ 您不应在两个动画的插值方法中使用AnchorPane(折叠面板,展开窗格),因为子节点不会调整大小,边距排列是public class FXMLDocumentController implements Initializable { @FXML private Button menu; @FXML private AnchorPane navList; @Override public void initialize(URL url, ResourceBundle rb) { //navList.setItems(FXCollections.observableArrayList("Red","Yellow","Blue")); prepareSlideMenuAnimation(); } private void prepareSlideMenuAnimation() { TranslateTransition openNav=new TranslateTransition(new Duration(350), navList); openNav.setToX(0); TranslateTransition closeNav=new TranslateTransition(new Duration(350), navList); menu.setOnAction((ActionEvent evt)->{ if(navList.getTranslateX()!=0){ openNav.play(); }else{ closeNav.setToX(-(navList.getWidth())); closeNav.play(); } }); } } 具有的唯一约束。

看看我做的这个例子。

https://github.com/marconideveloper/leftsidemenuexample

<AnchorPane xmlns:fx="http://javafx.com/fxml/1" id="AnchorPane" prefWidth="500" prefHeight="500"    fx:controller="leftslidemenusample.FXMLDocumentController">
    <children>

        <ToolBar AnchorPane.topAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" minHeight="56.0"   >
            <Button text="menu" fx:id="menu"  /> 
        </ToolBar>
        <StackPane fx:id="mainContent"  style="-fx-background-color:rgba(0,0,0,0.30)" AnchorPane.bottomAnchor="0.0" AnchorPane.topAnchor="56.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"    >
            <children>

            </children>
        </StackPane>
        <AnchorPane fx:id="navList" style="-fx-background-color:white" AnchorPane.topAnchor="56.0" AnchorPane.bottomAnchor="0.0" prefWidth="180.0" translateX="-180"   >
            <children>
                <Label text="left side menu"/>
            </children>
        </AnchorPane>

    </children>

</AnchorPane>

这是fxml:

LARGE_INTEGER

答案 1 :(得分:3)

最后,我完成了它。

他们的主要特点是:

  1. 使用自定义窗格在根窗格上设置阴影效果,该窗格在其布局边界外遮挡阴影并裁剪其内部内容,因此它具有透明内容。
  2. 根窗格可以是除AnchorPane之外的任何其他内容。
  3. 将包含主要内容的窗格剪切到其内部边界。
  4. 以下是控制这些效果的源代码片段:

    @Override
    public void initialize(URL url, ResourceBundle rb) {  
        ...
    
        Rectangle clip = new Rectangle(rootPaneWidth, rootPaneHeight);
        rootPane.setClip(clip);
        rootPane.getChildren().add(setupShadowPane());
    }
    
    private Pane setupShadowPane() {
        Pane shadowPane = new Pane();
        shadowPane.setStyle(
            "-fx-background-color: white;" +
            "-fx-effect: dropshadow(gaussian, black, " + shadowSize + ", 0, 0, 0);" +
            "-fx-background-insets: " + shadowSize + ";"
        );
    
        Rectangle innerBounds = new Rectangle();
        Rectangle outerBounds = new Rectangle();
        shadowPane.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
            innerBounds.relocate(newBounds.getMinX() + shadowSize, newBounds.getMinY() + shadowSize);
            innerBounds.setWidth(newBounds.getWidth() - shadowSize * 2);
            innerBounds.setHeight(newBounds.getHeight() - shadowSize * 2);
            outerBounds.setWidth(newBounds.getWidth());
            outerBounds.setHeight(newBounds.getHeight());
    
            Shape clip = Shape.subtract(outerBounds, innerBounds);
            shadowPane.setClip(clip);
        });
    
        return shadowPane;
    }
    

    半开幻灯片菜单

    semi-opened

    完全打开幻灯片菜单

    fully-opened

    幻灯片菜单已关闭

    closed