Trying to set up an "onshowing" ActionEvent in Combobox in javafx - javafx

I'm using Scene Builder to build a javafx gui, and I want a ComboBox where something will happen "On Showing." It looks simple enough, but it crashes when I try to implement it. I created a simple version having nothing but a combobox, and it still crashes.
Here is the Main.java module:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
// BorderPane root = new BorderPane();
BorderPane root = (BorderPane) FXMLLoader.load(Main.class.getResource("Comboscreen.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Here is the fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<center>
<VBox alignment="CENTER" prefHeight="400.0" prefWidth="400.0" BorderPane.alignment="CENTER">
<children>
<ComboBox fx:id="ComboTestBox" onAction="#ComboDo" onShowing="#Showaction" prefWidth="150.0" promptText="Testbox" />
</children>
</VBox>
</center>
</BorderPane>
and here is the Controller:
package application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
public class Controller {
#FXML
public ComboBox<String> ComboTestBox;
#FXML
void ComboDo(ActionEvent event) {
System.out.println(" Option Chosen");
}
ObservableList<String> options =
FXCollections.observableArrayList(
"Option 1",
"Option 2",
"Option 3"
);
#FXML
void Showaction(ActionEvent event) {
System.out.println(" TestAction");
}
public void initialize() {
ComboTestBox.setItems(options);
}
}
It looks simple enough, but it crashes every time. If I remove the "onshowing" action, it works just fine. Any advice is appreciated, as I am pretty new at this.

Assuming by "crashing" you mean it gives you a runtime exception (for future reference, please include the stack trace, which has lots of information for you to diagnose the problem, in your question):
The onShowing handler is an EventHandler<Event>, not an EventHandler<ActionEvent>, so you need:
#FXML
void Showaction(Event event) {
System.out.println(" TestAction");
}
Note that if you're not using the Event parameter, you can omit it, and the FXMLLoader will still be able to map to the correct handler method:
#FXML
void Showaction() {
System.out.println(" TestAction");
}
However, if you include the parameter, it must be of the correct type.
As an aside, you should use proper naming conventions, i.e.
#FXML
void showAction(Event event) {
System.out.println(" TestAction");
}
with the corresponding change to your FXML file.

Related

How to create a scrollpane on fxml using 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.

Setting menu items css font style has no visible effect

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);

How to switch stages in JavaFX

I have a login stage (300 x 250), I want to open another main stage (fullscreen) if the credentials are correct.
I have figured out how to check the login credentials, but how can I close the login stage and open another stage?
If my application is supposed to work in one window I prefer using a GUI manager singleton class, which manages changing windows. Below I provided the complete code of a simple application which uses this mechanism. Let's assume all the files are in one package, called sample.
Main.java - you initialize the JavaFX components here:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/sample/root.fxml"));
try{
StackPane rootPane;
rootPane = loader.load();
GuiManager guiModel = GuiManager.getInstance();
guiModel.setRootPane(rootPane);
Scene scene = new Scene(rootPane);
primaryStage.setScene(scene);
primaryStage.show();
guiModel.changeWindow("/sample/firstwindow.fxml");
} catch (IOException exception) {
exception.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
root.fxml - all the windows are supposed to be based on it:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="rootPane"
xmlns="http://javafx.com/javafx/8.0.60"
xmlns:fx="http://javafx.com/fxml/1"
prefWidth="1" prefHeight="1"/>
firstwindow.fxml - first actual window which will be displayed:
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="sample.FirstWindowController">
<Label text="First window"/>
<Button text="Change window" onAction="#changeWindow"/>
</VBox>
FirstWindowController.java - a controller class of the first window:
package sample;
import javafx.fxml.FXML;
public class FirstWindowController {
#FXML
private void changeWindow() {
GuiManager.getInstance().changeWindow("/sample/secondwindow.fxml");
}
}
secondwindow.fxml - it will be displayed after clicking the button of the first window:
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="sample.SecondWindowController" >
<Label text="Second window"/>
<Button text="Change window" onAction="#changeWindow"/>
</VBox>
SecondWindowController.java - a controller class of the second window:
package sample;
import javafx.fxml.FXML;
public class SecondWindowController {
#FXML
private void changeWindow() {
GuiManager.getInstance().changeWindow("/sample/firstwindow.fxml");
}
}
GuiManager.java - a class that manages changing windows based on the root:
package sample;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.io.IOException;
public class GuiManager {
private StackPane rootPane;
private static GuiManager instance;
public static GuiManager getInstance() {
if (instance == null) {
instance = new GuiManager();
}
return instance;
}
private GuiManager() {}
public void changeWindow(String path) {
changeWindow(rootPane, path, this);
rootPane.setPrefWidth(-1);
rootPane.setPrefHeight(-1);
}
public static void changeWindow(Pane pane, String newWindowPath, Object callingController) {
Window window = pane.getScene().getWindow();
double x = window.getX() + getHorizontalMidpoint(window);
double y = window.getY() + getVerticalMidpoint(window);
ObservableList<Node> childrenList = pane.getChildren();
removeAllIncludedChildren(childrenList);
FXMLLoader loader = new FXMLLoader(callingController.getClass().getResource(newWindowPath));
try {
pane.getChildren().add(loader.load());
Stage primaryStage = (Stage) window;
primaryStage.setMinHeight(0);
primaryStage.setMinWidth(0);
window.sizeToScene();
window.setX(x - getHorizontalMidpoint(window));
window.setY(y - getVerticalMidpoint(window));
primaryStage.setMinHeight(window.getHeight());
primaryStage.setMinWidth(window.getWidth());
} catch (IOException exception) {
exception.printStackTrace();
}
}
private static double getHorizontalMidpoint(Window window) {
int horizontalBisectionCoefficient = 2;
return window.getWidth() / horizontalBisectionCoefficient;
}
private static double getVerticalMidpoint(Window window) {
int verticalBisectionCoefficient = 2;
return window.getHeight() / verticalBisectionCoefficient;
}
private static void removeAllIncludedChildren(ObservableList<Node> childrenList) {
for (int childIndex = 0; childIndex < childrenList.size(); childIndex++) {
childrenList.remove(childIndex);
}
}
public void setRootPane(StackPane rootPane) {
this.rootPane = rootPane;
}
}
I just run in the same issue and this answer solved my issue perfectly while being short and clean.
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
//Here I want to swap the screen!
Stage stageTheEventSourceNodeBelongs = (Stage) ((Node)event.getSource()).getScene().getWindow();
// OR
Stage stageTheLabelBelongs = (Stage) label.getScene().getWindow();
// these two of them return the same stage
// Swap screen
stage.setScene(new Scene(new Pane()));
}
PS.: Remember to click the original answer and upvote it. The guy deserves...
PPS.: I am not sure just copying an answer is okay(instead of just share the link through a comment) but since this doesnt have a correct answer yet i decided to do it for visibility.

Scene change vs Pane change

I'm relatively new to Java and espacially JavaFX. I'm trying to make a menu, which switches the displayed content on buttonclick. I've done this now by clearing the Pane and asigning a new fxml-file to it.
This is one method from my Controller:
protected void CustomStart(ActionEvent event) {
content.getChildren().clear();
try {
content.getChildren().add(
(Node) FXMLLoader.load(getClass().getResource(
"/view/CustomStartStructure.fxml")));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It works just fine so far but I wuld like to to it by changing the scenes as well.
I want to initiate the scenes whit a fxml-file in the Constructor. It works within another method. But if I try to initiate it in the constructor I get an InvocationTargetException caused by a RuntimeException caused by a StackOverflow error. If I do it in the other method, I get a NullPointerException when I try to change the Scene.
This is the constructor
public Game() throws IOException {
this.MainMenu = new Scene((GridPane) FXMLLoader.load(getClass()
.getResource("/view/MainMenuStructure.fxml")), 400, 400);
this.stage = new Stage();
this.stage.setScene(MainMenu);
}
This is the method in whicht the invocation works:
public void run() throws Exception {
/**
* Set the Scenes for the different menus by using the panels from the
* fxml-files
*/
this.MainMenu = new Scene((GridPane) FXMLLoader.load(getClass()
.getResource("/view/MainMenuStructure.fxml")), 400, 400);
MainMenu.getStylesheets().add(
getClass().getResource("/view/MainMenuDesign.css")
.toExternalForm());
this.SingleRaceMenu = new Scene((GridPane) FXMLLoader.load(getClass()
.getResource("/view/CustomStartStructure.fxml")), 400, 400);
/** Giving the Stage a Scene */
this.setStage(new Stage());
this.stage.setScene(MainMenu);
this.stage.show();
}
This is the Buttoncontroller:
protected void CustomStart(ActionEvent event) {
this.getStage().setScene(getSingleRaceMenu());
}
I hope you can give me an advice!
Here is a simple example which has two fxml files, both loaded into separate scenes and the scenes are set to the same Stage.
Controller is defined for only scene1.fxml, since this is a basic example of how you can change scene using a button event on a controller.
The important part in the example is to see how I fetch the current stage reference using the button reference, which is already a part of the scene graph :
((Stage)button.getScene().getWindow())
If you want to learn about how to switch scenes, and go back to previous scene you can go implement the following example, by loading the fxml's in their respective scene :
Loading new fxml in the same scene
Example
scene1.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="10.0" style="-fx-background-color: goldenrod;" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<children>
<Label text="Scene 1" />
<Button fx:id="button" mnemonicParsing="false" onAction="#changeScene" text="Change Scene" />
</children>
</VBox>
scene2.fxml
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="10.0" style="-fx-background-color: cyan;" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="You have switched to Scene 2" />
</children>
</VBox>
Scene1 Controller
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.io.IOException;
public class Controller {
#FXML
private Button button;
#FXML
public void initialize() {
}
#FXML
private void changeScene(ActionEvent event) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/scene2.fxml"));
Parent parent = loader.load();
((Stage)button.getScene().getWindow()).setScene(new Scene(parent, 200, 200));
} catch (IOException eox) {
eox.printStackTrace();
}
}
}
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader fxmlloader = new FXMLLoader(Main.class.getResource("/scene1.fxml"));
VBox root = fxmlloader.load();
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Output
On the matter of Scene change vs Pane change:
Since the Scene change closes and opens a new window, if you are on full screen, I ruled it out for my purpose.
Instead I allways load a new Parent into my scene which is smooth and quick.
Since I use FXML the only difference between the scenes is in fact the parent given by an FXML file. So it is sufficient for me to stick with different Parents.
Here is a snippet of my Controller Class:
public class GameController {
private Parent mainMenu;
private Stage stage;
private Scene scene;
/** Constructor which receives a Stage */
public GameController(Stage stage) {
this.stage = stage;
}
public void start() {
/** Initialize the MainMenu */
initializeMenu(mainMenu, "/view/MainMenuStructure.fxml");
this.setScene(new Scene(mainMenu));
stage.setScene(scene);
stage.setFullScreen(true);
stage.setFullScreenExitHint("");
stage.show();
}
#FXML
private void MainMenu(ActionEvent event) {
setRoot(mainMenu);
}
/** Initialize the menus and the in game screen */
private void initializeMenu(Parent parent, String path) {
FXMLLoader loader = new FXMLLoader(getClass().getResource(path));
loader.setController(this);
if (parent == mainMenu) {
try {
this.setMainMenu(loader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setRoot(Parent parent) {
this.getStage().getScene().setRoot(parent);
}
}
I'm very confortable with my solution. But since I'm relatively new the Java and Javafx I hope this is helps a little and is not quick and dirty.
Thanks for the comments whicht actually helped a lot!

Customize ListView in JavaFX with FXML

I want to make a customize list view in javafx. Here I need to bind multiple component in list cell as follow, like
one label, one textfield, one button under one HBox and
two button, one hyperlink, one label in another HBox and
these HBox comes under one VBox and
this VBox comes under the single list cell and
it will repeat and make a list View.
The code is
<ListView fx:id="ListView" layoutX="0" layoutY="30" prefWidth="600" prefHeight="300">
<HBox fx:id="listBox" alignment="CENTER_LEFT">
<padding><Insets top="5" bottom="5" left="5"></Insets> </padding>
<HBox alignment="CENTER_LEFT" prefWidth="170" minWidth="88">
<Label fx:id="surveyName" text="Field A" styleClass="Name"></Label>
</HBox>
<VBox styleClass="Description" prefWidth="155" minWidth="86">
<HBox>
<HBox styleClass="surveyDesIcon" prefWidth="20" prefHeight="16"></HBox>
<Label fx:id="surveyCode" text="PRW3456HJ"></Label>
</HBox>
<HBox>
<HBox styleClass="DateIcon" prefWidth="20" prefHeight="16"></HBox>
<Label fx:id="Date" text="PRW3456HJ"></Label>
</HBox>
</VBox>
<HBox fx:id="Status" prefWidth="160" minWidth="80">
<Label fx:id="StatusLabel" text="Checking Files.."/>
</HBox>
<HBox fx:id="StatusIcon1" prefWidth="50" prefHeight="50" alignment="CENTER">
<Label styleClass="StatusIcon1" prefWidth="24" prefHeight="24" alignment="CENTER"/>
</HBox>
<HBox fx:id="StatusIcon2" prefWidth="50" prefHeight="50" styleClass="StatusIconBox" alignment="CENTER">
<Hyperlink styleClass="StatusIcon2" prefWidth="24" maxHeight="24" alignment="CENTER"/>
</HBox>
</HBox>
</ListView>
I understand your question. There are mainly two ways to set items in a Listview:
1. Create the ObservableList and set the items of the ListView with the ObservableList (listView.setItems(observableList)).
2. Use the setCellFactory() method of the ListView class.
You would prefer to use the setCellFactory() method, because this approach simplies the process as well as it helps to separate out the business logic and the UI (FXML).
Here is a more detailed explaination:
1. Create a new FXML file with the name listview.fxml to contain the ListView, and set the ListViewController class as its controller:
File: listview.fxml:
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.ListView?>
<?import demo.ListViewController?>
<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
<ListView fx:id="listView"/>
</GridPane>
2. Create the controller and name it ListViewController.
The controller can load the listview.fxml file and access the listview.
File: ListViewController.java:
package demo;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.io.IOException;
import java.util.Set;
public class ListViewController
{
#FXML
private ListView listView;
private Set<String> stringSet;
ObservableList observableList = FXCollections.observableArrayList();
public ListViewController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listview.fxml"));
fxmlLoader.setController(this);
try
{
Parent parent = (Parent)fxmlLoader.load();
Scene scene = new Scene(parent, 400.0 ,500.0);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public void setListView()
{
stringSet.add("String 1");
stringSet.add("String 2");
stringSet.add("String 3");
stringSet.add("String 4");
observableList.setAll(stringSet);
listView.setItems(observableList);
listView.setCellFactory(new Callback<ListView<String>, javafx.scene.control.ListCell<String>>()
{
#Override
public ListCell<String> call(ListView<String> listView)
{
return new ListViewCell();
}
});
}
}
3. First you need to set the value of the ObservableList. This is very important.
Then, set the items of list using the ObservableList and call the setCellFactory() method on the ListView. In the given example I just take the String values an add them to the String set (the Set<String> stringSet).
4. When the setCellFactory() method is called on the ListView, it will return the ListCell. So for sake of simplicity, I added a class which extends the ListCell, and the setGraphic() method is present for the ListCell() and will set the items of the ListCell.
File: ListViewCell.java:
package demo;
import javafx.scene.control.ListCell;
public class ListViewCell extends ListCell<String>
{
#Override
public void updateItem(String string, boolean empty)
{
super.updateItem(string,empty);
if(string != null)
{
Data data = new Data();
data.setInfo(string);
setGraphic(data.getBox());
}
}
}
5. I just added a class which will load the listCellItem.fxml and return the HBox, which will contain the other components as children.
The HBox is then set to the ListCell.
File: listCellItem.fxml:
<?import demo.Data?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Label?>
<HBox xmlns:fx="http://javafx.com/fxml" fx:id="hBox">
<children>
<Label fx:id="label1"/>
<Label fx:id="label2"/>
</children>
</HBox>
File: Data.java:
package demo;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import java.io.IOException;
public class Data
{
#FXML
private HBox hBox;
#FXML
private Label label1;
#FXML
private Label label2;
public Data()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listCellItem.fxml"));
fxmlLoader.setController(this);
try
{
fxmlLoader.load();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public void setInfo(String string)
{
label1.setText(string);
label2.setText(string);
}
public HBox getBox()
{
return hBox;
}
}
Using this way, you can use the setCellFactory() method to separate the things that are business logic and FXML.
Hope this is helpful.
The example above by #Anvay needs a couple of tweaks to work. These are simple things to set on-track.
The ListViewController needs to be running on the JavaFX application thread.
You can only call the injected #FXML elements from the JavaFX initialize() method
Need to call setListView()
The stringSet in the example needs to be allocated with a new before calling setListView().
The ListViewController below works with these changes. I changed "stringSet" to a list, "stringList". The controller is pretty much the sample controller provided by Scene Builder 2
public class ListViewController
{
#FXML private ResourceBundle resources;
#FXML private URL location;
#FXML private ListView listView;
private List<String> stringList = new ArrayList<>(5);
private ObservableList observableList = FXCollections.observableArrayList();
public void setListView(){
stringList.add("String 1");
stringList.add("String 2");
stringList.add("String 3");
stringList.add("String 4");
observableList.setAll(stringList);
listView.setItems(observableList);
listView.setCellFactory(
new Callback<ListView<String>, javafx.scene.control.ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> listView) {
return new ListViewCell();
}
});
}
#FXML
void initialize() {
assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file 'CustomList.fxml'.";
setListView();
}
}//ListViewController
The JavaFX platform needs to be started in the main() method from a JavaFX Application. Netbeans conviently provides most of this structure from the Maven JavaFX application template.
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/CustomList.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add("/styles/Styles.css");
stage.setTitle("CustomList");
stage.setScene(scene);
stage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
*
* #param args the command line arguments
**/
public static void main(String[] args) {
launch(args);
}
}
The answer by Anvay for some reason didnt work for me, what i had to do to fix it was just some very small tweaks:
remove import data statement from listCellItem.fxml
as the comment below the post states in Data.java put hBox = fmxlLoader.load()
I also had a main class (intellij auto generated).
public class MainMain extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("MainController.fxml"));
try
{
Parent root = fxmlLoader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Title");
primaryStage.show();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
launch(args);
}
I know this was probably obvious for most of the experts here, but these issues perplexed me for hours while i was debugging.

Resources