I'm learning about javafx ScrollBar. I know little about ScrollPane but I want to get grip on all controls. Here I tried to use javafx ScrollBar. but it is not working.
Main Class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.Parent;
import javafx.stage.Stage;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
public class ScrollBarExample extends Application{
#Override
public void start(Stage stage)throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("scroll.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
Controller Class:
import javafx.scene.control.Label;
import javafx.scene.control.ScrollBar;
import javafx.fxml.FXML;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class ScrollController{
#FXML private Label label;
#FXML private ScrollBar scroll;
public void initialize(){
scroll.valueProperty().addListener(new ChangeListener<Number>(){
#Override
public void changed(ObservableValue<? extends Number> value, Number oldValue, Number newValue){
label.setLayoutX(newValue.doubleValue());
}
});
}
}
FXML Code:
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.ScrollBar?>
<?import javafx.scene.layout.Pane ?>
<?import javafx.scene.control.Label ?>
<GridPane prefWidth="300" prefHeight="250" xmlns:fx="http://javafx.com/fxml" fx:controller="ScrollController" >
<children>
<ScrollBar fx:id="scroll" min="0" max="100" GridPane.rowIndex="0" GridPane.columnIndex="0" />
<Label fx:id="label" text="Move Me" GridPane.rowIndex="1" GridPane.columnSpan="2" GridPane.columnIndex="0" />
</children>
</GridPane>
So what are the rules to use ScrollBar? On which elements it could use or not?
The problem here is the parent, i.e. the GridPane. A GridPane sets the layoutX property itself. If this is changed, during the next layout pulse GridPane simply moves the Node "back where it belongs".
To make this work you could use the translateX property instead. The translateX property denotes the horizontal distance the node should be moved from it's layout position. With this approach it would also be easier to bind the properties instead of using a listener:
label.translateXProperty().bind(scroll.valueProperty());
Alternatively you could use a parent that does not set the layout position itself (like Pane) or set the managed property to false to prevent the parent from modifying the child's position and size. Note that in this case you need to set the layoutY yourself, since this property isn't set by the Parent either.
Related
I want this part of my code with different languge so I am using bundles. I am trying to set the value of text in fxml file with "%" sign to have the value in the bundle.properties file.
I have the following code in the fxml file using Scenebuilder:
<?xml version="1.0" encoding="UTF-8"?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox fx:id="iconBox" alignment="CENTER" maxHeight="-Infinity" maxWidth="-
Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="80.0"
prefWidth="80.0" xmlns="http://javafx.com/javafx/18"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.sarf.main.HomeIconsController">
<children>
<FontAwesomeIcon fx:id="iconName" glyphName="STAR" size="26" />
<Label fx:id="iconLabel" text="%anyThing">
<opaqueInsets>
<Insets />
</opaqueInsets>
<VBox.margin>
<Insets top="20.0" />
</VBox.margin>
</Label>
</children>
</VBox>
I am using properties file and trying to set the value of the text from the Controller of the fxml file.
here is the code in the controller:
package com.sarf.main;
import com.sarf.main.models.HomeIcon;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
//import javafx.scene.layout.VBox;
public class HomeIconsController implements Initializable{
//#FXML
//private VBox iconBox;
#FXML
private FontAwesomeIcon iconName;
#FXML
private Label iconLabel;
#Override
public void initialize(URL url, ResourceBundle rb) {
iconLabel.setText("%grocery");
}
}
the main.java file:
package com.sarf.main;
import java.io.IOException;
import java.util.Locale;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.sql.*;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Locale local = new Locale("en_UK");
ResourceBundle bundle =
ResourceBundle.getBundle("com.sarf.main.resources.bundle", local);
Parent rootPane =
FXMLLoader.load(getClass().getResource("fxml/home_icons.fxml"), bundle);
Scene scene = new Scene(rootPane);
primaryStage.setTitle("SARF");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
the resources file is simple. I am just at the beginning of the project. but i faced this porblem. anyway the resources file for now includes one entry:
grocery= Market
when I run the code, the label text is shown like : "%grocery", % sign is interpreted as plain text and the value is not shown as it is in the bundle.properties file.
any help?
Resource resolution is performed by the FXMLLoader when the FXML is parsed. So
<Label fx:id="iconLabel" text="%grocery" />
will result in the text of the label being set to the result of calling getString("grocery") on the resource bundle.
Your code sets the text twice, first in the FXML file, but then again in the initialize() method. The initialize() method is invoked after the text has been set by parsing the FXML file, and of course just sets the text literally to the value provided. So in your code the label's text is initially set to "Market", but then immediately reset to "%grocery".
Remove the line
iconLabel.setText("%grocery");
from the initialize() method and you will see the result you intended.
If you really want to reference the resource bundle in the controller, you can do so as it is passed to the initialize() method. If you remove text="%grocery" from the FXML file, you can do
iconLabel.setText(rb.getString("grocery"));
in the initialize() method (instead of the line you currently have) and that will work too.
Related: see Accessing localisation ResourceBundle inside JavaFX FXML Controller for more information about accessing the resource bundle in the controller.
I would like to create a scrollpane and button from the controller and display it in fxml, is that possible? This my code but it doesn't work
controller
package application;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
public class controller {
ScrollPane scrollPane = new ScrollPane();
Button button = new Button("My Button");
public void initialize() {
button.setPrefSize(400, 300);
scrollPane.setContent(button);
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
}
}
main
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final Parent parent = FXMLLoader.load(getClass().getResource("ui.fxml"));
primaryStage.setTitle("ScrollPane Demo ");
primaryStage.setScene(new Scene(parent,600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);}
}
fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<VBox id="vbox" prefHeight="400" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller">
<children>
<Pane prefHeight="200.0" prefWidth="200.0" />
</children>
</VBox>
You need to include your vbox in your controller, like this:
#FXML
private VBox vbox;
After that, add your scrollpane to your vbox's children:
vbox.getChildren().add(scrollPane);
Note that, you not added your button to your scrollpane's content, so your scrollpane is currently empty!
Edit: why you want to add nodes via fxml and from code too? Use this solution if it is really necessary, anyway use fxml to build your UI.
I would like to change the MenuItem's text to bold when the user clicks on it. To manage to do this I'm using setStyle, but unfortunately it has no visible effect. The css makes no effect when the user selects the MenuItem option. The css styling only has an effect in initialize method.
I created a minimal reproducible example for the problem:
MAIN:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public final class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
Scene scene = new Scene(root, 850.0, 650.0);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MAIN FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="475.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="MainController">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu fx:id="menu" mnemonicParsing="false" text="Switch Chart">
<items>
<MenuItem text="MenuItem1" />
<MenuItem onAction="#setFontBold" text="MenuItem2" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
</BorderPane>
MAIN CONTROLLER:
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
public class MainController implements Initializable {
#FXML private Menu menu;
private List<MenuItem> menuItems;
#Override
public void initialize(URL location, ResourceBundle resources) {
menuItems = menu.getItems();
menuItems.get(0).setStyle("-fx-font-weight: bold");
}
public void setFontBold() {
menuItems.get(1).setStyle("-fx-font-weight: bold");
System.out.println(menuItems.get(1).getStyle());
System.out.println("Font set to bold in menuitem with index 1 has no effect...");
}
}
So I get the following css effects, even after selecting the first indexed menu item (so onAction=#setFontBold not changes anything visually).
No bold effect. - only if you set the style in initialize()
I'm curious about why it makes no difference? It definitely sets the style to bold, but it is displayed as a regular font even after changing it to bold.
EDIT: JavaFX version: 8.0.211-b10
If I would have to stick to javafx 8 and switching was the requirement, I would have used RadioMenuItem. It does the job as expected, but does not make MenuItem text bold.
From official docs, I found following excerpt that would be useful.
ToggleGroup toggleGroup = new ToggleGroup();
RadioMenuItem radioItem1 = new RadioMenuItem("Option 1");
radioItem.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println("radio toggled");
}
});
radioItem1.setToggleGroup(toggleGroup);
RadioMenuItem radioItem2 = new RadioMenuItem("Option 2");
radioItem.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println("radio toggled");
}
});
radioItem2.setToggleGroup(toggleGroup);
I have an ImageView which I have configured so that it is resized to fit its parent AnchorPane with the two following lines of code (both the AnchorPane and the image are square).
preview.fitWidthProperty().bind(previewPane.maxWidthProperty());
preview.fitHeightProperty().bind(previewPane.maxWidthProperty());
I have also set a fitWidth of 450 px in an fxml file that injects all nodes. When I create an instance of this object however it opens the preview with a width equal to that of the loaded image (2000 px) and not 450 px. The resizing functionality works as expected and it is possible to shrink it down to 450 px. I was wondering however if it is possible to make sure it opens up with a default size different from that of the width of the image but also making sure so that it grows and shrinks with its parent.
I have tried setting loads of different default widths in both java code and fxml but the only thing that seems to work is unbinding the fitWidth which I don't want.
Do you have any suggestions as to how I might solve this?
Main:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainTest extends Application {
#FXML private AnchorPane previewPane;
#FXML private ImageView preview;
#Override
public void start(Stage primaryStage) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Test.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setClassLoader(getClass().getClassLoader());
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
preview.fitWidthProperty().bind(previewPane.maxWidthProperty());
preview.fitHeightProperty().bind(previewPane.maxWidthProperty());
Scene scene = new Scene(previewPane);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="previewPane" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fx:id="preview" fitHeight="450.0" fitWidth="450.0" layoutX="55.0" layoutY="36.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="#Blue%20Eyes%20White%20Dragon.png" />
</image>
</ImageView>
</children>
</AnchorPane>
EDIT: Here is a MCVE of my problem.
The structure of your project is wrong. Also, to fix the problem you are having, you need to set the Scene size.
Code to get the ImageView from the Controller
//Code in the Main
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
ImageView preView = fXMLDocumentController.getPreview();
//Code in the Controller
public ImageView getPreview()
{
return preview;
}
Code to set the Scene's size
stage.setWidth(450);
stage.setHeight(450);
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication213 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
ImageView preView = fXMLDocumentController.getPreview();
Scene scene = new Scene(root);
stage.setWidth(450);
stage.setHeight(450);
stage.setScene(scene);
stage.show();
preView.fitHeightProperty().bind(stage.heightProperty());
preView.fitWidthProperty().bind(stage.widthProperty());
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.ImageView;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private ImageView preview;
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}
public ImageView getPreview()
{
return preview;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="previewPane" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication213.FXMLDocumentController">
<children>
<ImageView fx:id="preview" fitHeight="450.0" fitWidth="450.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="#Blue%20Eyes%20White%20Dragon.png" />
</image>
</ImageView>
</children>
</AnchorPane>
Finally, you need to uncheck Preserve Ratio. The FXML should already have this unchecked. I am pointing this out because it's important.
It seems there is an issue in setting focus (and handling keyboard events) on the JavaFX scenes that have only shapes (e.g. Rectangle, Circle, etc). Since formatting FXML is not easy at stackoverflow, I described the issue and a workaround in my blog:
http://yakovfain.com/2015/02/13/javafx-8-the-keyboard-events-are-not-being-processed-if-a-scene-has-only-shapes/
I'd appreciate if someone could find a better solution or explain what do I do wrong.
Thanks
This works for me.
The only change I made from Yakov's original solution is to remove the button work around hack and make the request focus call after the stage had been shown.
I think in Yakov's original implementation, the group containing the shapes was not shown on a stage at the time focus was requested for it - so it never got focus. By ensuring that we wait until the stage is displayed before requesting focus, everything works fine.
sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.Group?>
<?import javafx.scene.shape.Rectangle?>
<Group fx:id="theGroup" onKeyPressed="#keyHandler" focusTraversable="true"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="Controller">
<Rectangle fill="BLUE" height="300.0" stroke="BLACK"
strokeType="INSIDE" width="300.0"/>
</Group>
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setScene(new Scene(root, 300, 300));
primaryStage.show();
Controller controller = loader.<Controller>getController();
controller.focus();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
import javafx.fxml.FXML;
import javafx.scene.Group;
import javafx.scene.input.KeyEvent;
public class Controller {
#FXML
private Group theGroup;
public void keyHandler(KeyEvent event) {
System.out.println("A Key was pressed");
}
public void focus() {
theGroup.requestFocus();
}
}