This simple example creates an area with 2 rectangle areas marked in red.
I want to push the right area by n pixels towards the right using the VBox margin method but nothing happens. Why is margin not working in this example ? It works in scene builder though..
public class LayoutContainerTest extends Application {
#Override
public void start(Stage primaryStage) {
VBox areaLeft = new VBox();
areaLeft.setStyle("-fx-background-color: red;");
areaLeft.setPrefSize(100, 200);
VBox areaMiddle = new VBox();
areaMiddle.setStyle("-fx-background-color: red;");
areaMiddle.setPrefSize(100, 200);
VBox areaRight = new VBox();
areaRight.setStyle("-fx-background-color: red;");
areaRight.setPrefSize(100, 200);
VBox.setMargin(areaRight, new Insets(0,0,0,50));
HBox root = new HBox(areaLeft,areaMiddle,areaRight);
root.setSpacing(30);
Scene scene = new Scene(root, 600, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
You are using VBox.setMargin() but should be using the HBox method instead:
HBox.setMargin(areaRight, new Insets(0, 0, 0, 50));
The reason being, you are setting the margins for the children of a VBox, while areaRight is the child of a HBox. If you were to use your code and then place areaRight into a VBox, you would be seeing the margin as expected.
You mention that it works in SceneBuilder, but if you inspect the actual FXML code, you'll see that SceneBuilder is correctly using HBox:
<VBox fx:id="areaRight" prefHeight="200.0" prefWidth="100.0" style="-fx-background-color: red;">
<HBox.margin>
<Insets left="50.0" />
</HBox.margin>
</VBox>
Related
I have code in the form:
<BorderPane>
...
<right>
<GridPane>
...
</GridPane>
</right>
...
</BorderPane>
Obviously, now the GridPane takes a big space right of my BorderPane. What I'd like to do is add a button (or another element) that minimizes and maximizes the GridPane, so it's only fully in the view of the user when it is really needed. How can I easily achieve this?
You can do what you want by setting the Visible and Managed properties of your GridPane off and on. The centre of the BorderPane will automatically expand to take over the entire width of the BorderPane. "Managed" controls whether or not the layout manager will leave space for the node, so if you just turn Visible off, then you'll have an unused area the size of your GridPane on the right. The following code demonstrates it, I put the buttons in a VBox with a border around it so that you can see how it expands:
public class ResizeRight extends BorderPane {
public ResizeRight() {
Button openButton = new Button("Open");
Button closeButton = new Button("Close");
GridPane gridPane = new GridPane();
gridPane.addRow(0, new Text("This is just some text"));
gridPane.addRow(1, new Text("This is just some more text"));
VBox vbox = new VBox(10, openButton, closeButton);
vbox.setBorder(new Border(new BorderStroke(Color.BLACK,
BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
setCenter(vbox);
setRight(gridPane);
setPadding(new Insets(10));
openButton.setOnAction(evt -> {
gridPane.setVisible(true);
gridPane.setManaged(true);
});
closeButton.setOnAction(evt -> {
gridPane.setVisible(false);
gridPane.setManaged(false);
});
}
}
Run it from something like this:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new ResizeRight(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I would like to make an animation when I hover a button but if I use CSS, there is no transitions, the properties change instantly. I tried to extend the button class and set the onMouseEntered property but if I do that, I can't open the FXML file with SceneBuilder anymore because it doesn't know my subclass extends the Button class. So what can I do to have all of the buttons have a transition on hover or click ?
There are several ways you can accomplish this, and even let it work on Scene Builder.
I'd start by subclassing the button skin, where you can add the event handlers with your animations. In this case let's have a fade in/fade out animation:
MyButtonSkin.java
public class MyButtonSkin extends ButtonSkin {
public MyButtonSkin(Button control) {
super(control);
final FadeTransition fadeIn = new FadeTransition(Duration.millis(100));
fadeIn.setNode(control);
fadeIn.setToValue(1);
control.setOnMouseEntered(e -> fadeIn.playFromStart());
final FadeTransition fadeOut = new FadeTransition(Duration.millis(100));
fadeOut.setNode(control);
fadeOut.setToValue(0.5);
control.setOnMouseExited(e -> fadeOut.playFromStart());
control.setOpacity(0.5);
}
}
Now you can apply this custom skin to a regular JavaFX Button:
One way, by code:
MyApplication.java
#Override
public void start(Stage primaryStage) throws IOException {
Button btn = new Button("Button");
btn.setSkin(new MyButtonSkin(btn));
StackPane root = new StackPane(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
Another option is via css, let's add a style.css file:
style.css
.button {
-fx-skin: 'your.package.name.MyButtonSkin';
}
and now:
MyApplication.java
#Override
public void start(Stage primaryStage) throws IOException {
Button btn = new Button("Button");
StackPane root = new StackPane(btn);
Scene scene = new Scene(root, 300, 250);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
In both cases, you will have the button animations working when you run your application:
Scene Builder
If you add now a regular JavaFX Button to an FXML file:
FXML.fxml
<AnchorPane id="AnchorPane" prefWidth="300" prefHeight="250" stylesheets="#style.css" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="your.package.name.FXMLController">
<children>
<Button layoutX="100" layoutY="100" text="Button" />
</children>
</AnchorPane>
Scene Builder won't know about the custom skin (whether it was applied by code or via css), and it won't display or preview it.
In this case, this solution is good enough, since after all Scene Builder is just a designer tool, and the animation will work when you run your application.
But if you really want to preview the animation from Scene Builder, you still can do it, but for this you need to:
Subclass Button, and
Import this class (jar) into Scene Builder
So let's create MyButton class:
MyButton.java
public class MyButton extends Button {
public MyButton() {
super();
}
public MyButton(String text) {
super(text);
}
#Override
protected Skin<?> createDefaultSkin() {
return new MyButtonSkin(this);
}
}
Now build your project. At least should contain MyButton and MyButtonSkin), but it can contain a demo application class to test it:
#Override
public void start(Stage primaryStage) throws IOException {
MyButton btn = new MyButton("Button");
StackPane root = new StackPane(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
Import the project into Scene Builder:
Open Scene Builder, and select JAR/FXML Manager -> Add Library/FXML from file system. Locate your jar, and click Import Component:
Now you'll be able to drag a MyButton control from the Custom panel to the top left.
FXML.fxml
<?import javafx.scene.layout.AnchorPane?>
<?import your.package.name.MyButton?>
<AnchorPane prefHeight="250.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MyButton layoutX="100" layoutY="100" text="Button" />
</children>
</AnchorPane>
Finally, when you click Preview -> Show Preview in Window, you will be able to see the animation.
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.
Shrinking an AnchorPane does NOT shrink/move ANY of its children nodes, because they stay in their layout positions.
Shrinking any other Pane except AnchorPane DOES shrink/move its children nodes except from ImageView nodes.
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:
Set the shadow effect on the root pane using a custom pane that drows a shadow outside its layout bounds and crops its inside content, so it has a transparent content.
The root pane can be anything else than AnchorPane.
Clip the pane that holds the main content to its inside bounds.
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
I need to scroll down to a certain label component in Java FX. How do I do that ? I have ids attached to label component.
<ScrollPane>
<VBox fx:id="menuItemsBox">
<Label text="....."/>
<Label text="....."/>
<Label text="....."/>
....
<Label text="....."/>
</VBox>
</ScrollPane>
You can set the scrollPane's vValue to the Node's LayoutY value. Since the setVvlaue() accepts value between 0.0 to 1.0, you need to have computation to range your input according to it.
scrollPane.setVvalue(/*Some Computation*/);
This must be set after the stage's isShowing() is true.
Complete Example
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ScrollPane pane = new ScrollPane();
VBox box = new VBox();
IntStream.range(1, 10).mapToObj(i -> new Label("Label" + i)).forEach(box.getChildren()::add);
pane.setContent(box);
Scene scene = new Scene(pane, 200, 50);
primaryStage.setScene(scene);
primaryStage.show();
// Logic to scroll to the nth child
Bounds bounds = pane.getViewportBounds();
// get(3) is the index to `label4`.
// You can change it to any label you want
pane.setVvalue(box.getChildren().get(3).getLayoutY() *
(1/(box.getHeight()-bounds.getHeight())));
}
}
I'm working in NetBeans 8 with Java 8/JavaFX.
I have an application that starts a main stage based on an fxml file.
There is a menu option for a user to bring up a second stage on request. The function that opens the window looks like this:
#FXML
private void openChildWindow(ActionEvent event) throws Exception {
Group root = new Group();
Stage stage = new Stage();
AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
root.getChildren().add(frame);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
The content of second.fxml looks like this (after all the import statements):
<AnchorPane id="AnchorPane" prefHeight="680.0" prefWidth="1020.0" xmlns:fx="http://javafx.com/fxml/1" >
<stylesheets><URL value="#css/mycss.css" /></stylesheets>
<children>
<TabPane AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="10.0"
AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="0.0" >
<Tab text="A" closable="false"></Tab>
</TabPane>
</children>
</AnchorPane>
The problem: when I click on the corner of the second window to drag and resize it, the contents are not resizing. What am I missing or what do I need so that it will auto-resize?
Group is not directly resizable as stated in its javadoc. So it does not being resized by the scene while the window is resized from the corner. You can use the subclasses of Pane instead of. For example try with
VBox root = new VBox();
Instead of using a Group, please put your AnchorPane directly into your Scene
#FXML
private void openChildWindow(ActionEvent event) throws Exception {
Stage stage = new Stage();
AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
Scene scene = new Scene(frame );
stage.setScene(scene);
stage.show();
}
A Group cannot be resized, "A Group will take on the collective bounds of its children and is not directly resizable." http://docs.oracle.com/javafx/2/api/javafx/scene/Group.html
Replace Group with a different of layout such as StackPane, or BorderPane.
I struggled with the same thing, if we only had read the documentation. :D