I would like some guidelines on how to implement a slide in transition for a pane when user presses a button, just like Material Design does it for sliding menus.
This is a video link that illustrates my need.
I tried ScaleTransition, TranslateTransition, but they didn't do the trick.
The way I'm trying to implement it is not efficient.
// swipeMenuPane is builded in SceneBuilder and it is hidden,
// opacity = 0.0 and setX() = -getPrefWidth();
@FXML AnchorPane swipeMenuPane;
@FXML Button menuButton;
menuButton.setOnMouseClicked(e-> {
swipeMenuPane.setOpacity(1.0);
swipeTransition.play()
});
TranslateTransition swipeTransition = new TranslateTransition();
swipeTransition.setNode(swipeMenuPane);
swipeTransition.setDuration(Duration.millis(500));
swipeTransition.setToX(swipeMenuPane.getPrefWidth());
--- UPDATE ---
Here is the sample Gluon Application downloaded from here. It's a gradle project and I modified it to display a button instead of the default label.
I want to shrink the AnchorPane when user clicks the button.
What am I missing?
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();
}
}
--- UPDATE 2 ---
I managed to implement something similar to what I want using native JavaFX API and no external libraries.
Although, I ran into some issues.
The next two images illustrate the 1st issue I ran into.
This is an AnchorPane (with coral color at its full width; expanded) inside an AnchorPane (root pane with grey color).
And this is what happens when you click Menu button to shrink/hide it. As you can see the coral-colored pane got shrinked/hidden, but NOT its nodes (Label, ImageView)
I post the whole code to reproduce the issue yourself:
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;
}
}
--- UPDATE 3 ---
I modified the code Felipe Guizar Diaz provide me, according to my needs, since I want a dropshadow effect on my transparent stage window.
When I click the menu button to show the left pane it shows up in front of the shadow. Even though in SceneBuilder I've placed the StackPane with the dropshadow effect in front of all nodes.
This is the "artifact" when I press to show the menu and starts playing the open transition...
How can I fix it?
I am the author of the example video. I'll repeat the response that I did in the video comments:
"you should think of it as a navigation drawer in android, the navigation drawer in JavaFX would be an AnchorPane
with 2 children, first a StackPane
that is equivalent to a FrameLayout
working as our main content, where transitions of pane are made depending of the chosen item from the left side menu, and ultimately a ListView
as our left side menu with a negative translateX
that equals to the Listview
width. Then when the user presses a button you must play an animation that sest the value of translateX
to 0
."
You shouldn't use prefWidth()
in the interpolate method of the two animations (collapse Panel, expand Pane), because the children don't resize, the margin arrangement is the only constraint that the AnchorPane
has.
Check out this example that I did.
https://github.com/marconideveloper/leftsidemenuexample
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();
}
});
}
}
Here is the fxml:
<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>
Finally, I get it done.
They key features are:
Below is a snippet of the source code that controls these effects:
@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;
}
Slide Menu semi opened
Slide Menu fully opened
Slide Menu closed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With