How to add an icon or a fontawesome icon to a JFXButton from the controller class?
public void initialize() {
JFXButton hamburger = new JFXButton();
}
Extending #Mayur Patel answer, use JFXButton constructor with text and graphic, see documentation JFXButton:
public JFXButton(String text, Node graphic) {
super(text, graphic);
initialize();
}
So in your case, it should look like this:
Image image = new Image(getClass().getResourceAsStream("icon.png"));
JFXButton hamburger = new JFXButton("Try me",image);
And with fontawesome icons (remember to import it first in your scene builder, if you are working with scene builder):
<JFXButton fx:id="delete_btn" text="Try me">
<graphic>
<FontAwesomeIconView fill="WHITE" glyphName="play" size="16.0"/>
</graphic>
</JFXButton>
Try this, it might work -
JFXButton hamburger = new JFXButton();
Image image = new Image(getClass().getResourceAsStream("icon.png"));
hamburger.setGraphic(new ImageView(image));
Related
I've searched the entire web (metaphorically) to find an explanation on how to create the jar file that can then be imported by the scene builder to add extra, custom components. Currently, I am trying to create a slider with a textField that displays it's value, with a biDirectional link using a NumberStringConverter. I have the classes all setup, but now I need to bundle them in a jar file, and that's the part that doesn't work for me. These are the classes:
FXML:
<fx:root type="javafx.scene.layout.HBox" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" fx:controller="main.java.valueSlider">
<Slider fx:id="slider" HBox.hgrow="ALWAYS"/>
<Label fx:id="label" text="value"/>
</fx:root>
Now I can just import this FXML file and use it as a shortcut to create a slider with a label, but I want there to be a controller involved for the biDirectional link. This is the controller class:
public class valueSlider extends HBox {
#FXML
private TextField value;
#FXML
private Slider slider;
private DoubleProperty sliderPos = new SimpleDoubleProperty();
private DoubleProperty
sliderMin = new SimpleDoubleProperty(),
sliderMax = new SimpleDoubleProperty();
public valueSlider() {
try {
FXMLLoader l = new FXMLLoader(getClass().getResource("valueSlider.fxml"));
l.setController(this);
l.setRoot(this);
l.load();
}
catch (IOException e) {
e.printStackTrace();
}
}
#FXML
private void initialize() {
slider.minProperty().bindBidirectional(sliderMin);
slider.maxProperty().bindBidirectional(sliderMax);
slider.valueProperty().bindBidirectional(sliderPos);
value.textProperty().bindBidirectional(sliderPos, new NumberStringConverter());
}
public double getSliderPos() {
return sliderPos.get();
}
public DoubleProperty sliderPosProperty() {
return sliderPos;
}
public void setSliderPos(double sliderPos) {
this.sliderPos.set(sliderPos);
}
}
So the question now is: how do I export these two classes in a jar file and so that I can load it into the scene builder?
Quick question: I made a new FXML file in the same package and tried to use the component like so:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.121" fx:controller="main.java.controller">
<children>
<valueSlider sliderPos="75"/>
</children>
</AnchorPane>
But when I tried to open the scene builder in the IDE (intelliJ IDEA) it said:
Failed to open the file in the Scene Builder
java.lang.ClassNotFoundException: Unresolved import
javafx.fxml.LoadException:
/G:/GitHub/customParts/src/main/resources/test.fxml
But it doesn't provide any more information, but when I remove the valueSlider component it works perfectly fine.
Please let me know if you know how to solve this problem.
Thanks in advance,
Lenardjee
I have a javafx datePicker and I want to be shown without text editor at all. I want only the button of the picker to be visible and active. Is there any way to do this. Of course I tried the datePicker.getEditor().setVisible(false) but there is stil an annoying place holder which I want to make invisible.
DatePickerView.fxml
<HBox>
<children>
<TextField/>
<DatePicker fx:id="datePicker">
<opaqueInsets>
<Insets/>
</opaqueInsets>
</DatePicker>
</children>
</Hbox>
DatePickerController.java
public class DatePickerComtroller implements Initializable{
#FXML
DatePicker datePicker;
#FXML
HBox container;
public DatePickerComtroller(){
}
public initialize(URL location, ResourceBundle resources){
datePicker.getEditor().setVisible(false);
}
As you didn't describe what you want to show instead of the textField, I assume that you want the button to fill the total width. Whatever, the collaborator that's responsible for creating, configuring and layouting the children of a control is its skin. So basically, you'll need a custom skin and tweak what you want to change, here: the layout.
What to do:
subclass DatePickerSkin
in its constructor, get hold of the arrowButton (it's private, but you can look it up via its style)
override layoutChildren, remove the editor and change the width of the button to fill the width
Some raw code example (obviously there's leeway for polishing :)
public class MyPickerSkin extends DatePickerSkin {
StackPane arrowButtonAlias;
public MyPickerSkin(DatePicker control) {
super(control);
arrowButtonAlias = (StackPane) control.lookup(".arrow-button");
}
#Override
protected void layoutChildren(double x, double y, double w, double h) {
super.layoutChildren(x, y, w, h);
// seems the removal must happen after super's layout (see OPs comment)
getChildren().remove(getEditor());
arrowButtonAlias.resizeRelocate(x, y, w, h);
}
}
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.
Can I change a Node's parent to another parent in FXML?
Suppose a parent named stackcon with a child node.
I now want to move this child node to different parent, e.g., named stackmain.
Is this possible? If yes, please give me a link or a example code.
This is just one of many ways how to do this.
MainView.fxml, just a simple view containing a button, and on button click the label should be moved from right to left and vice-versa (see the onMousePressed declaration):
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<left>
<VBox fx:id="lefty">
<Label fx:id="switchy" text="Switching text"></Label>
</VBox>
</left>
<center>
<Button fx:id="switchBtn" text="Switch" onMousePressed="#switchButtonPressed"></Button>
</center>
<right>
<VBox fx:id="righty">
</VBox>
</right>
</fx:root>
The controller MainView.java with its switchButtonPressed event handler:
public class MainView extends BorderPane {
#FXML private VBox lefty;
#FXML private VBox righty;
#FXML private Label switchy;
public MainView() {
URL fxmlFile = MainView.class.getResource("MainView.fxml");
FXMLLoader fxmlLoader = new FXMLLoader(fxmlFile);
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
public void switchButtonPressed(MouseEvent mouseEvent) {
if(lefty.getChildren().contains(switchy)) {
lefty.getChildren().remove(switchy);
righty.getChildren().add(switchy);
} else {
righty.getChildren().remove(switchy);
lefty.getChildren().add(switchy);
}
}
}
You see, I just check on button click if the label is on the left side. If so, remove it on the left side and add it on the right side.
Analogous, if it is on the right side, remove it there and add it on the left side.
it may vary depending on type of the pane of stackmain & stackcon , i'm assuming that you use AnchorPane
but it is something like this
Node childNode = stackcon.getChildren().get(indexOfChild);
stackcon.getChildren().remove(indexOfChild);
stackmain.getChildren().add(childNode);
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