How do I create KeyEvents in Java FXML? - javafx

I created this small project to test key events. But when I press keys, it isn't behaving as I want. Actually I need key events for my Calculator project. I created a Calculator project and aside from mouse clicks, I want to add a feature where numbers or operators can be typed from a keyboard. Can anyone check this and help make it more functional?
public class FXMLDocumentController implements Initializable{
#FXML
private Label label;
#FXML
private Button backSpace;
#FXML
private Button spaceBar;
#FXML
private Button enter;
#FXML
void typedBS(KeyEvent event) {
if (event.getCode() == KeyCode.BACK_SPACE) {
label.setText(event.getText() + " typed.");
}
}
#FXML
void typedE(KeyEvent event) {
if (event.getCode()==KeyCode.ENTER) {
label.setText(event.getText() + " typed");
}
}
#FXML
void typedSB(KeyEvent event) {
if (event.getCode()==KeyCode.SPACE) {
label.setText(event.getText()+" typed");
}
}
#FXML
void PressBackSpace(KeyEvent event) {
if (event.getCode() == KeyCode.BACK_SPACE) {
label.setText("You pressed Back Space key!");
}
}
#FXML
void clickBackSpace(ActionEvent event) {
label.setText("You clicked Back Space key!");
}
#FXML
void clickEnter(ActionEvent event) {
label.setText("You clicked Enter key!");
}
#FXML
void clickSpaceBar(ActionEvent event) {
label.setText("You clicked SpaceBar key!");
}
#FXML
void pressEnter(KeyEvent event) {
if (event.getCode() == KeyCode.ENTER) {
label.setText("You pressed Enter key!");
}
}
#FXML
void pressSpaceBar(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE) {
label.setText("You pressed SpaceBar key!");
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}

If you look at this FXML file you can see my root node(The AnchorPane) has an onKeyPressed and onKeyReleased.
<AnchorPane id="AnchorPane" onKeyPressed="#handleOnKeyPressed" onKeyReleased="#handleOnKeyReleased" prefHeight="650.0" prefWidth="855.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atmfx.FXMLDocumentController">
<children>
<BorderPane layoutX="263.0" layoutY="94.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<left>
<AnchorPane fx:id="apLeftDisplay" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
In The Controller my method that handles these two KeyEvents are
#FXML
private void handleOnKeyReleased(KeyEvent event)
{
System.out.println();
Button tempButton = buttons.get(event.getText());
System.out.println("Released key text: " + event.getText());
System.out.println("Released key code: " + event.getCode());
if (tempButton != null) {
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.ENTER)) {
tempButton = buttons.get("enter");
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.BACK_SPACE)) {
tempButton = buttons.get("backspace");
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.SPACE)) {
tempButton = buttons.get("space");
tempButton.disarm();
tempButton.setStyle("");
}
}
#FXML
private void handleOnKeyPressed(KeyEvent event)
{
Button tempButton = buttons.get(event.getText());
System.out.println("Pressed key text: " + event.getText());
System.out.println("Pressed key code: " + event.getCode());
if (tempButton != null) {
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.ENTER)) {
tempButton = buttons.get("enter");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.BACK_SPACE)) {
tempButton = buttons.get("backspace");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.SPACE)) {
tempButton = buttons.get("space");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
}
In this code I added all of my buttons to a HasMap. That way in my keyevent handlers I was able to retrieve the button depending on the keyboard key I pressed.
Map<String, Button> buttons = new HashMap<>();
TheButtonsParentNode.getChildren().stream().filter((tempNode)
-> (tempNode instanceof Button)).map((
tempNode) -> (Button) tempNode).forEachOrdered((tempButton) -> {
buttons.put(tempButton.getText().toLowerCase(), tempButton);//adding button text and the button to hashmap
});

Define the event and the handling in the same thing.
try something like this:
if u are using a borderpane as an example
borderPane.setOnKeyPressed(this::handleKeyPress);
private void handleKeyPress(KeyEvent event) {
if (event.getCode() == KeyCode.BACK_SPACE) {
label.setText(event.getText() + " typed.");
}
if (event.getCode() == KeyCode.SPACE) {
label.setText("You pressed SpaceBar key!");
}
//TODO
}
Hope it helps

Related

Update structure JavaFX TreeTableView

I create two windows and pass the Instance controller to the child window as a parameter. My problem is updating on the fly. I tried calling the refresh() method on treeTblState but nothing came of it.
First window
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mo.specdoc.controllers.StateController">
<children>
<TreeTableView fx:id="treeTblState" showRoot="false">
<columns>
....
</columns>
</TreeTableView>
</children>
</AnchorPane>
Controller
public class StateController implements Initializable {
private static Map<Long, TreeItem> subdivisions = new HashMap<>();
private static StateController instance;
#FXML private TreeTableColumn<StateEntity, String> tblClmnTitle,...,tblClmnDelete;
#FXML private TreeTableView<StateEntity> treeTblState = new TreeTableView<>();
//Create root element (property setShow = false в FXML)
private StateEntity root = new StateEntity(0L,"State");
//Pattern Instance
public static StateController getInstance() {
if (instance == null) {
instance = new StateController();
}
return instance;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
try {
tblClmnTitle.setCellValueFactory(new TreeItemPropertyValueFactory<>("postName"));
...
tblClmnDelete.setCellValueFactory(new TreeItemPropertyValueFactory<>("delete"));
treeGreate();
} catch ....
}
public void addChildren(StateEntity state) {
TreeItem<StateEntity> item = null;
for (Long key : subdivisions.keySet()) {
if (key == state.getSubdivisionId()) {
TreeItem<StateEntity> subdiv = subdivisions.get(key);
item = new TreeItem<StateEntity>(state);
subdiv.getChildren().add(item);
}
}
}
private void treeGreate() {
TreeItem<StateEntity> itemRoot = new TreeItem<StateEntity>(root);
treeTblState.setRoot(itemRoot);
//create tree - level 2
//subdivisions
for (SubdivisionEntity subdivision : subdivisionDAO.findAll()) {
StateEntity state = new StateEntity(
subdivision.getId(),
subdivision.getTitle()
);
state.getAdd().setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
createScene("Add post", new StateEntity(), subdivision.getId());
}
});
TreeItem<StateEntity> subdiv = new TreeItem<StateEntity>(state);
itemRoot.getChildren().add(subdiv);
subdivisions.put(state.getSubdivisionId(), subdiv);
//posts
List<StateEntity> childrens = stateDAO.findByIdSubdiv(state.getSubdivisionId());
if (!childrens.isEmpty()) {
for (StateEntity child : childrens) {
TreeItem<StateEntity> item = new TreeItem<StateEntity>(child);
subdiv.getChildren().add(item);
}
}
}
}
private void createScene(String title, StateEntity state, Long subdivisionId) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/state-edit.fxml"));
StateEditController stateEditController = new StateEditController(state, subdivisionId);
loader.setController(stateEditController);
Stage stage = new Stage();
Scene scene = new Scene(loader.load());
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
} catch ....
}
}
Result
Second window
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<children>
...elements...
</children>
</AnchorPane>
Controller
public class StateEditController implements Initializable {
....
#FXML
void saveAction(ActionEvent event) {
currentEntry.setSubdivisionId(subdivisionId);
currentEntry.setMaxAmountPersonal(cmbBoxMaxAmount.getSelectionModel().getSelectedItem().intValue());
currentEntry.setMinAmountPersonal(cmbBoxMaxAmount.getSelectionModel().getSelectedItem().intValue());
currentEntry.setAmplification(tglSwitchAmpl.isSelected());
currentEntry.setPostId(cmbBoxPost.getSelectionModel().getSelectedItem().getId());
if (currentEntry.getId() == null) {
stateDAO.save(currentEntry);
} else {
stateDAO.update(currentEntry);
}
stateController.addChildren(currentEntry); //call instance metod
}
#Override
public void initialize(URL location, ResourceBundle resources) {
....
}
}
Result
Everything is entered into the database successfully, but it is not updated when the window is closed 2.
I redraw the child elements using the clear method when adding an item
public void addChildren(StateEntity state) {
subdivisions.get(state.getSubdivisionId()).getChildren().clear();
List<StateEntity> childrens = stateDAO.findByIdSubdiv(state.getSubdivisionId());
if (!childrens.isEmpty()) {
for (StateEntity child : childrens) {
TreeItem<StateEntity> item = new TreeItem<StateEntity>(child);
subdivisions.get(state.getSubdivisionId()).getChildren().add(item);
}
}
}

Javafx: How do you target objects in nested views?

I understand that for each time I load a nested fxml and controller that I can use that controller to manipulate the nested fxml. For example I can make every other row purple, but what I don't understand is how, after everything is loaded, can I target something specific to manipulate.
This is the most bare bones minimal replicate-able example I can make, anything reduced from this doesn't fit the criteria of a nested fxml and a child. What I want to do here is take the first score, with the text "Score0" and change it to "10" when the 10 button is pushed. I have a feeling I'm close, the score box should be score[0]. It should also be nested in row[0], but I can't seem to get the scoreEntry method to find the box to change it.
Main.java:
public class Main extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("view1.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 400, 300);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
view1.fxml:
<AnchorPane prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.nested.Ctrl1">
<children>
<VBox fx:id="rowHolder" prefHeight="260.0" prefWidth="400.0" />
<Button mnemonicParsing="false" onAction="#scoreEntry" prefHeight="40.0" prefWidth="40.0" text="10" AnchorPane.leftAnchor="70.0" AnchorPane.topAnchor="260.0" />
<Button mnemonicParsing="false" onAction="#scoreEntry" prefHeight="40.0" prefWidth="40.0" text="0" AnchorPane.topAnchor="260.0" />
</children>
</AnchorPane>
Ctrl1.java:
public class Ctrl1 {
#FXML
private VBox rowHolder;
#FXML
void initialize() {
addRows();
}
public void addRows() {
int rounds = 2;
HBox row[];
row = new HBox[rounds];
String ctrlArray[];
ctrlArray = new String[rounds];
for (int i = 0; i < row.length; i++) {
row[i] = new HBox();
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/nested/view2.fxml"));
row[i] = loader.load();
rowHolder.getChildren().add(row[i]);
Ctrl2 ctrl2 = loader.getController();
ctrlArray[i] = String.valueOf(ctrl2);
ctrl2.setRoundNum(String.valueOf(i));
System.out.println(row[i]);
} catch (IOException e) {
e.printStackTrace();
}
}
for (int i = 0; i < ctrlArray.length; i++) {
System.out.println("Controller: " + ctrlArray[i]);
}
}
private BigDecimal scoreInput;
private boolean numberInput;
#FXML
public void scoreEntry(ActionEvent event) {
Button button = (Button) event.getSource();
String buttonText = button.getText();
if (buttonText.matches("[0-10\\.]")) {
if (!numberInput) {
numberInput = true;
//TODO: find Label for input text
//I think it should be something like "row[0].getChildren().score[0].clear();"
}
//And then I would append it with something like "score[0].appendText(buttonText);"
return;
}
}
}
view2.fxml
<HBox prefHeight="30.0" prefWidth="400.0" style="-fx-background-color: #77e77e;" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.nested.Ctrl2">
<children>
<Label fx:id="rowNum" text="Row Number" />
<HBox fx:id="scoresHolder" prefHeight="100.0" prefWidth="200.0" />
<Label fx:id="subTotal" text="Sub Total" />
<Label fx:id="total" text="Total" />
</children>
</HBox>
Ctrl2.java
public class Ctrl2 {
#FXML
private Label rowNum;
#FXML
private Label subTotal;
#FXML
private HBox scoresHolder;
#FXML
private Label total;
public void setRoundNum(String strRnd) {
rowNum.setText(strRnd);
}
public void setSubTotal(String strRnd) {
subTotal.setText(strRnd);
}
public void setTotal(String strRnd) {
total.setText(strRnd);
}
#FXML
void initialize() {
int plays = 1;
Label score[];
score = new Label[plays];
for (int i = 0; i < score.length; i++) {
score[i] = new Label("Score" + i);
scoresHolder.getChildren().add(score[i]);
System.out.println(score[i]);
}
}
}
I took #James_D's advice and wrapped my controllers and cells in arrays.
//This is the array for the controllers. It's outside the method to be accessed by other methods.
public static Ctrl2[] ctrlArray = new Ctrl2[2];
public void addRows() {
int rounds = 2;
HBox row = null;
for (int i = 0; i < rounds; i++) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/nested/view2.fxml"));
row = loader.load();
rowHolder.getChildren().add(row);
ctrlArray[i] = loader.getController();
ctrlArray[i].setRoundNum(String.valueOf(i));
} catch (IOException e) {
e.printStackTrace();
}
}
for (int i = 0; i < ctrlArray.length; i++) {
System.out.println("Controller: " + ctrlArray[i]);
}
}
The problem I was having was with scope. If I make the array holder outside the method I can access it from other methods.
private Label[] score;
public void addScores() {
int plays = 2;
//Moved array holder from here to outside
score = new Label[plays];
for (int i = 0; i < score.length; i++) {
score[i] = new Label("Score" + i);
scoresHolder.getChildren().add(score[i]);
System.out.println(score[i]);
}
}

How to add a javafx shortcut key combinations for buttons

My UI has a adding button and I want to assign a keyboard shortcut combination for that. I have failed to use the setAcceleartor for this purpose.
What is the easiest way to set up keyboard shortcuts in javafx applications?
button declaration in the UI:
<Button fx:id="addButton" alignment="CENTER" minWidth="-Infinity" mnemonicParsing="false" onAction="#addAction" prefHeight="31.0" prefWidth="130.0" text="Add" HBox.hgrow="ALWAYS" />
Controller button binding:
#FXML
private Button addButton;
The method that wants to setOnAction for the shortcut for the button:
public void addAction(ActionEvent event) throws SQLException, ClassNotFoundException {
if (validateInput()) {
String productName = productField.getText();
double unitPrice = Double.parseDouble(priceField.getText());
int quantity = Integer.parseInt(quantityField.getText());
double total = unitPrice * quantity;
ITEMLIST.add(new Item(productName, unitPrice, quantity, total));
calculation();
resetAdd();
productTableView.getSelectionModel().clearSelection();
ObservableList<Product> productsData = ProductDAO.searchProducts();
populateProducts(productsData);
searchField.setText("");
}
}
initialize() method:
#FXML
private void initialize() throws SQLException, ClassNotFoundException, IOException {
setSaveAccelerator(addButton);
}
The code I tried with setAccelerator:
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
In your setSaveAccelerator method, instead of directly calling addAction(ActionEvent event), just instruct the button to fire its event to its listeners such as: button.fire(). For example:
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button is null! "); // check that the button was injected properly through your fxml
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
EDIT
To also avoid the IllegalArgumentException you must attach the accelerator after the button is attached to a scene. I managed to achieve this by creating a public method in the controller to attach the accelerator after the scene has been set. Then, in the class where the scene is loaded the controller's method can be called which sets up this functionality. See the example below:
In the controller class (in my case MainController):
public void setup() {
setSaveAccelerator(button);
}
In your main class when loading the fxml file:
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
controller.setup(); // calls the setup method attaching the accelerators
FULL EXAMPLE
Main class:
public class Main extends Application{
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage=primaryStage;
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
primaryStage.setTitle("Shortcut example");
primaryStage.setScene(scene);
controller.setup();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maincontroller:
public class MainController {
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private Button button;
#FXML
private AnchorPane rootPane;
#FXML
private TextArea textarea;
#FXML
void action(ActionEvent event) {
textarea.setText("Action fired!!");
}
#FXML
void initialize() {
assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert textarea != null : "fx:id=\"textarea\" was not injected: check your FXML file 'MainFXML.fxml'.";
}
public void setup() {
setSaveAccelerator(button);
}
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button null!!");
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
}
MainFXML.fxml
<AnchorPane fx:id="rootPane" prefHeight="408.0" prefWidth="330.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<Button fx:id="button" layoutX="139.0" layoutY="350.0" mnemonicParsing="false" onAction="#action" text="Button" />
<TextArea fx:id="textarea" layoutX="73.0" layoutY="38.0" prefHeight="200.0" prefWidth="200.0" />
</children>
</AnchorPane>

Java FX- Moving the drop and drop event on Label

I'm having issue with dragging and dropping the label on each other. I could not able to drag the label and drop it onto another label. Event setOnDragDetected is detected and setOnDragDropped is not detected. These are the files.
FXML:
<HBox fx:id="hboxTwo" alignment="CENTER" spacing="100" style="-fx-background-color: orange;">
<padding>
<javafx.geometry.Insets bottom="25" left="25" right="25" top="25" />
</padding>
<children>
<Label fx:id="labelTwo" prefHeight="50.0" text="labelTWO" />
</children>
</HBox>
</children>
</HBox>
</center>
Controller:
public class HomeController implements Initializable {
#FXML
Label labelOne;
#FXML
Label labelTwo;
#FXML
HBox hboxOne;
#FXML
HBox hboxTwo;
#Override
public void initialize(URL url, ResourceBundle rb) {
labelOne.setOnDragDetected((MouseEvent event) -> {
System.out.println("source::" + event.getSource());
});
hboxTwo.setOnDragDropped((DragEvent event) -> {
event.acceptTransferModes(TransferMode.ANY);
System.out.println("source::" + event.getSource());
});
hboxTwo.setOnDragEntered((DragEvent event) -> {
event.acceptTransferModes(TransferMode.ANY);
System.out.println("source::" + event.getSource());
});
}
}
To make setOnDragDropped work, you have to set the TransferMode previously, and remove the according method call in setOnDragDropped:
boxTwo.setOnDragOver((DragEvent event) -> {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
});
boxTwo.setOnDragDropped((DragEvent event) -> {
Label source = (Label) event.getGestureSource();
boxTwo.getChildren().add(source);
event.setDropCompleted()
event.consume();
});
Just copy and pasting below, the relevant code for drag detection and dropping from the JavaFX 8 Drag and Drop tutorial.
When you compare the sample code to your code, you can see that you are missing stuff:
You aren't constructing a Dragboard containing drag and drop data.
You aren't extracting drag and drop data from a Dragboard on a drop.
You aren't notifying that the drag and drop event was completed.
You aren't appropriately consuming events.
source.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* drag was detected, start a drag-and-drop gesture*/
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
/* Put a string on a dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
}
});
target.setOnDragOver(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data is dragged over the target */
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target &&
event.getDragboard().hasString()) {
/* allow for moving */
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
}
});
target.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data dropped */
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
target.setText(db.getString());
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
}
});

JavaFX : data isn't displayed in tableview's column when trying to use cellfactory

I want to display data in tableview's column with custom rendering. I tried to adapt this tuto to my need.
Problem :
In the code example below, when I use the setCellFactory() method, the data in the corresponding column isn't displayed.
You can comment or uncomment the delimited section to see what happen in controller class.
Main class
public class CellFactory extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
AnchorPane root = FXMLLoader.load(CellFactory.class.getResource("CellFactory_Layout.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("CellFactory EXAMPLE");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Model class
public class Fruit {
private final SimpleStringProperty name;
private final SimpleIntegerProperty weight;
public Fruit(String name, int weight){
this.name = new SimpleStringProperty(name);
this.weight = new SimpleIntegerProperty(weight);
}
public String getName() {return this.name.get();}
public void setName(String v) {this.name.set(v);}
public SimpleStringProperty nameProperty() {return this.name;}
public int getWeight() {return this.weight.get();}
public void setWeight(int v) {this.weight.set(v);}
public SimpleIntegerProperty weightProperty() {return this.weight;}
}
Controller class
public class CellFactory_Controller implements Initializable {
#FXML private TableView<Fruit> fruit_tbl;
#FXML private TableColumn<Fruit, String> name_cln;
#FXML private TableColumn<Fruit, Integer> weight_cln;
// array with table data
final ObservableList<Fruit> data = FXCollections.observableArrayList();
public CellFactory_Controller() {
// some data
this.data.add(new Fruit("banana", 120));
this.data.add(new Fruit("apple", 150));
this.data.add(new Fruit("coconut", 500));
this.data.add(new Fruit("orange", 200));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
this.name_cln.setCellValueFactory(new PropertyValueFactory<>("name"));
this.weight_cln.setCellValueFactory(new PropertyValueFactory<>("weight"));
this.weight_cln.setCellValueFactory(cellData -> cellData.getValue().weightProperty().asObject());
// comment or uncomment to see what happen
///////////////////////////////////////////////////////////////////////
this.weight_cln.setCellFactory(column -> new TableCell<Fruit, Integer>() {
#Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setStyle("");
} else {
if (item < 10) {
setTextFill(Color.CHOCOLATE);
} else {
setTextFill(Color.BLACK);
setStyle("");
}
}
}
});
///////////////////////////////////////////////////////////////////////
this.fruit_tbl.setItems(this.data);
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="CellFactory.CellFactory_Controller">
<children>
<TableView fx:id="fruit_tbl" layoutX="189.0" layoutY="93.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="name_cln" prefWidth="471.0" text="FRUIT" />
<TableColumn fx:id="weight_cln" prefWidth="75.0" text="WEIGHT" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</AnchorPane>
Question :
From my code example, how can I use custom cell renderer properly (with int data type) to display my data ?
you forgot to add the following statement:
setText(String.valueOf(item));
So, your setCellFactory() method should look like the following:
this.weight_cln.setCellFactory(column -> new TableCell<Fruit, Integer>() {
#Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setStyle("");
} else {
setText(String.valueOf(item));
if (item < 200) {
setTextFill(Color.CHOCOLATE);
} else {
setTextFill(Color.BLACK);
}
}
}
});

Resources