How to style the GridPane using CSS in JavaFx - javafx

I'm new in JavaFx. I have a GridPane created in FXML. I want to style that GridPane as the following image. I have tried with the following answers and tried with more CSS elements. But none of these helped me to do this.
JavaFX CSS class for GridPane, VBox, VBox
Center children in GridPane using CSS
Adding borders to GridPane JavaFX
And also am I using a wrong approach? Can I achieve this using GridPane or will the TableView is easy to this?

I think you can use a TableView control. You can use the following code to start with:
Controller Class:
package sample;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.net.URL;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private TableView<Project>
projectTableView;
#FXML
private TableColumn<Project, String>
projeNameColumn;
#FXML
private TableColumn<Project, LocalDate>
dateColumn;
#FXML
private TableColumn<Project, LocalTime>
startTimeColumn,
stopTimeColumn;
#FXML
private TableColumn<Project, Duration>
durationColumn;
private ObservableList<Project> projectList = FXCollections.observableArrayList();
#Override
public void initialize(URL location, ResourceBundle resources) {
projectList.addAll(
new Project("Landing page Design",
LocalDate.of(2019, 5, 21),
LocalTime.of(13, 10),
LocalTime.of(21, 20, 37)),
new Project("Mobile App",
LocalDate.of(2019, 5, 21),
LocalTime.of(12, 0),
LocalTime.of(20, 0)),
new Project("UI/UX",
LocalDate.of(2019, 5, 21),
LocalTime.of(13, 10),
LocalTime.of(13, 20, 37)),
new Project("Website/apps",
LocalDate.of(2019, 5, 21),
LocalTime.of(13, 11),
LocalTime.of(21, 0, 37)),
new Project("Branding",
LocalDate.of(2019, 5, 21),
LocalTime.of(13, 10),
LocalTime.of(13, 20, 37))
);
projectTableView.setItems(projectList);
projeNameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
projeNameColumn.setCellFactory(tc -> {
final Image image = new Image(getClass().getResource("image.png").toString());
return new TableCell<Project, String>() {
private ImageView imageView = new ImageView();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty)
setGraphic(null);
else {
imageView.setImage(image);
imageView.setPreserveRatio(true);
imageView.setFitHeight(35);
setGraphic(imageView);
setText(item);
}
}
};
});
dateColumn.setCellValueFactory(new PropertyValueFactory("date"));
dateColumn.setCellFactory((TableColumn<Project, LocalDate> column) -> new TableCell<Project, LocalDate>() {
protected void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty)
setText(null);
else
setText(DateTimeFormatter.ofPattern("MMM dd, yyyy").format(item));
}
});
startTimeColumn.setCellValueFactory(new PropertyValueFactory("startTime"));
startTimeColumn.setCellFactory((TableColumn<Project, LocalTime> column) -> new TableCell<Project, LocalTime>() {
protected void updateItem(LocalTime item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty)
setText(null);
else
setText(DateTimeFormatter.ofPattern("hh:mm a").format(item));
}
});
stopTimeColumn.setCellValueFactory(new PropertyValueFactory("stopTime"));
stopTimeColumn.setCellFactory((TableColumn<Project, LocalTime> column) -> new TableCell<Project, LocalTime>() {
protected void updateItem(LocalTime item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty)
setText(null);
else
setText(DateTimeFormatter.ofPattern("hh:mm a").format(item));
}
});
durationColumn.setCellValueFactory(new PropertyValueFactory("duration"));
durationColumn.setCellFactory((TableColumn<Project, Duration> column) -> new TableCell<Project, Duration>() {
protected void updateItem(Duration item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
int s = (int) item.getSeconds();
int hours = s / 3600;
int minutes = (s % 3600) / 60;
int seconds = (s % 60);
setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
}
});
}
}
Project Class:
package sample;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
public class Project {
private final SimpleStringProperty name;
private final ObjectProperty<LocalDate> date;
private final ObjectProperty<LocalTime> startTime, stopTime;
private final ObjectProperty<Duration> duration;
public Project(String name, LocalDate date, LocalTime startTime, LocalTime stopTime) {
this.name = new SimpleStringProperty(name);
this.date = new SimpleObjectProperty<>(date);
this.startTime = new SimpleObjectProperty<>(startTime);
this.stopTime = new SimpleObjectProperty<>(stopTime);
this.duration = new SimpleObjectProperty<>(Duration.between(startTime, stopTime));
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public LocalDate getDate() {
return date.get();
}
public ObjectProperty<LocalDate> dateProperty() {
return date;
}
public void setDate(LocalDate date) {
this.date.set(date);
}
public LocalTime getStartTime() {
return startTime.get();
}
public ObjectProperty<LocalTime> startTimeProperty() {
return startTime;
}
public void setStartTime(LocalTime startTime) {
this.startTime.set(startTime);
}
public LocalTime getStopTime() {
return stopTime.get();
}
public ObjectProperty<LocalTime> stopTimeProperty() {
return stopTime;
}
public void setStopTime(LocalTime stopTime) {
this.stopTime.set(stopTime);
}
public Duration getDuration() {
return duration.get();
}
public ObjectProperty<Duration> durationProperty() {
return duration;
}
public void setDuration(Duration duration) {
this.duration.set(duration);
}
}
FXML File:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<TableView fx:id="projectTableView" stylesheets="#styling.css" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="sample.Controller">
<columns>
<TableColumn id="first-column" fx:id="projeNameColumn" prefWidth="144.0" style="-fx-alignment: center-left;" text="Project" />
<TableColumn fx:id="dateColumn" prefWidth="96.0" style="-fx-alignment: center;" text="Date" />
<TableColumn fx:id="startTimeColumn" prefWidth="75.0" style="-fx-alignment: center;" text="Start Time" />
<TableColumn fx:id="stopTimeColumn" prefWidth="75.0" style="-fx-alignment: center;" text="Stop Time" />
<TableColumn fx:id="durationColumn" prefWidth="75.0" style="-fx-alignment: center;" text="Duration" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
CSS File:
.table-view .column-header,
.table-view .column-header-background .filler {
-fx-background-color: #F9F9F9;
}
.table-view .column-header {
-fx-cell-size: 35;
-fx-border-width: 0.25 0.25 1 0.25;
-fx-border-color: #EDEDED;
}
.table-view .column-header .label{
-fx-padding: 10 0 10 0;
}
.table-view .cell{
-fx-cell-size: 35;
}
Preview:

Related

JavaFx TableCellEditor

I have JavaFx TableView and I permit user to enter some data in the table and retrieve what a user entered in the table for this reason I create also ArrayList of TextFields and use the following code but the size of the ArrayList should be 3 in my case but I found the size 7, what's wrong?
Edit the full code here
import java.util.ArrayList;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
public class TestTableView extends Application
{
private final TableView <ProductOpeningBalance> tableProductOpeningBalance = new TableView();
private final Scene scene = new Scene(tableProductOpeningBalance, 400, 200);
private final TableColumn productQuantity = new TableColumn("Product Quantity");
private final ObservableList <ProductOpeningBalance> data = FXCollections.observableArrayList(
new ProductOpeningBalance("0"),
new ProductOpeningBalance("0"));
private final ArrayList <TextField> txtProductQuantity = new ArrayList <> ();
#Override
public void start(Stage stage)
{
productQuantity.impl_setReorderable(false);
productQuantity.setEditable(true);
productQuantity.setCellValueFactory(new PropertyValueFactory("ProductQuantity"));
productQuantity.setCellFactory(column -> new TableCell()
{
#Override
public void startEdit()
{
if(!isEmpty())
{
super.startEdit();
createTextField();
setText(null);
setGraphic(txtProductQuantity.get(txtProductQuantity.size() - 1));
txtProductQuantity.get(txtProductQuantity.size() - 1).selectAll();
}
}
#Override
public void cancelEdit()
{
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if(empty)
{
setText(null);
setGraphic(null);
}
else
{
if(isEditing())
{
if(txtProductQuantity.get(txtProductQuantity.size() - 1) != null)
{
txtProductQuantity.get(txtProductQuantity.size() - 1).setText(getString());
}
setText(null);
setGraphic(txtProductQuantity.get(txtProductQuantity.size() - 1));
}
else
{
setText(getString());
setGraphic(null);
}
}
}
private void createTextField()
{
txtProductQuantity.add(new TextField(getString()));
txtProductQuantity.get(txtProductQuantity.size() - 1).
setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
txtProductQuantity.get(txtProductQuantity.size() - 1).setAlignment(Pos.BASELINE_RIGHT);
txtProductQuantity.get(txtProductQuantity.size() - 1).focusedProperty().
addListener((ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) ->
{
if(!arg2)
{
commitEdit(txtProductQuantity.get(txtProductQuantity.size() - 1).getText());
}
});
}
private String getString()
{
return getItem() == null ? "" : getItem().toString();
}
});
tableProductOpeningBalance.setEditable(true);
tableProductOpeningBalance.getColumns().addAll(productQuantity);
tableProductOpeningBalance.setItems(data);
stage.setScene(scene);
stage.show();
}
public class ProductOpeningBalance
{
private final SimpleStringProperty ProductQuantity;
public ProductOpeningBalance(String productQuantity)
{
this.ProductQuantity = new SimpleStringProperty(productQuantity);
}
public void setProductQuantity(String productQuantity)
{
ProductQuantity.set(productQuantity);
}
public String getProductQuantity()
{
return ProductQuantity.get();
}
}
}
Solution finally:
this code help me to find what I need after spent a lot of time in searching and trying a lot of methods
purchaseItemPrice.setCellFactory(column -> new TableCell()
{
#Override
public void startEdit()
{
if(!isEmpty())
{
if(txtPurchaseItemPrice.size() < data.size() && getGraphic() == null)
{
super.startEdit();
txtPurchaseItemPrice.add(new TextField());
txtPurchaseItemPrice.get(txtPurchaseItemPrice.size() - 1).setAlignment(Pos.BASELINE_RIGHT);
setGraphic(txtPurchaseItemPrice.get(txtPurchaseItemPrice.size() - 1));
}
}
}
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if(!empty)
{
if(isEditing())
{
setGraphic(txtPurchaseItemPrice.get(txtPurchaseItemPrice.size() - 1));
}
}
}
});

Why the delay in ComboBox responding

Following code works, but when I change from Male to Female or vice versa, cbSpouse gets new Items loaded. Correct data gets loaded. But the issue is, if I click on the down arrow in cbSpouse, I need to wait for a few minutes before I see the drop down. There is no database interaction, all necessary data is already sitting in Observable list.
ObservableList<String> GenderOptions = FXCollections.observableArrayList("Male", "Female");
cbGender.getItems().clear();
cbGender.setItems(GenderOptions);
cbGender.setVisibleRowCount(GenderOptions.size());
cbGender.getSelectionModel().selectFirst();
cbGender.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue ov, String t, String t1) {
System.out.println("Change Listener for cbGender - oldValue "+t+" new value"+t1);
if ((t1.equals("Male")) || (t.equals(null))) {
cbSpouse.setItems(myMainApp.getFemaleSpouses());
if (thisPerson.getPhoto() == null) {
thePicture.setImage(defaultMale);
}
} else {
cbSpouse.setItems(myMainApp.getMaleSpouses());
if (thisPerson.getPhoto() == null) {
thePicture.setImage(defaultFemale);
}
}
cbSpouse.getSelectionModel().selectFirst();
}
});
//
There are about 300 items in myMainApp.getMaleSpouses() which actually returns the Observable list.
Can someone help me?
Thanks in Advance.
Hornigold
Here is the smaller version, which works nicely. Just keep changing the Gender and click on Spouses, new list appears.
package application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.Tooltip;
import javafx.scene.text.Text;
/**
*
* #author Hornigold Arthur
*/
public class APerson {
private final StringProperty firstName;
private final StringProperty lastName;
private final StringProperty gender;
private final IntegerProperty familyID;
private final IntegerProperty personID;
private final StringProperty toolTipText;
private final Tooltip toolTip;
public APerson() {
this(null, null, null, 0, 0 );
}
/**
* Constructor with some initial data.
*
* #param familyID
* #param personID
* #param firstName
* #param lastName
* #param gender
*/
public APerson( String firstName, String lastName, String gender, int familyID, int personID) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.gender = new SimpleStringProperty(gender);
this.familyID = new SimpleIntegerProperty(familyID);
this.personID = new SimpleIntegerProperty(personID);
this.toolTipText = new SimpleStringProperty(null);
this.toolTip = new Tooltip();
}
//
public int getFamilyID() {
return familyID.get();
}
public void setFamilyID(int FamilyID) {
this.familyID.set(FamilyID);
}
public IntegerProperty familyIDProperty() {
return familyID;
}
//
public int getPersonID() {
return personID.get();
}
public void setPersonID(int PersonID) {
this.personID.set(PersonID);
}
public IntegerProperty personIDProperty() {
return personID;
}
//
public String getFirstName() {
return firstName.get().trim();
}
public void setFirstName(String FirstName) {
this.firstName.set(FirstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
//
public String getLastName() {
return lastName.get();
}
public void setLastName(String LastName) {
this.lastName.set(LastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getGender() {
return gender.get();
}
public void setGender(String Gender) {
this.gender.set(Gender);
}
public StringProperty genderProperty() {
return gender;
}
public String LoadToolTipText() {
String tmp = "";
String headerLine = "<body style=\"background-color: LavenderBlush; border-style: none;\"> <u><b><font color=\"red\">Click Mouse's right button to see options</font></b></u><br><br>";
headerLine = headerLine+"<font color=\"red\">Use ARROW keys to scrol the Tooltip</font></b></u><br><br>";
Text t1 = new Text();
t1.setText(headerLine);
String ToolTipText = t1.getText() + this.toString() + "<br>";
ToolTipText = ToolTipText + "<br></body>";
this.toolTip.setStyle("-fx-background-color: pink; -fx-text-fill: black; -fx-font: normal normal 12pt \"Times New Roman\"");
this.toolTip.setText(ToolTipText);
// System.out.println("Tip: "+ToolTipText);
return ToolTipText;
}
public Tooltip getToolTip() {
// A browser.
return toolTip;
}
//
public String toString() {
String tmp = this.getFirstName() + " " + this.getLastName() + " [" + this.getPersonID()+"] ";
return tmp;
}
}
Main.java
package application;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
test000Controller myController;
static private javafx.collections.ObservableList<APerson> persons = javafx.collections.FXCollections.observableArrayList();
static private javafx.collections.ObservableList<APerson> fathers = javafx.collections.FXCollections.observableArrayList();
static private javafx.collections.ObservableList<APerson> mothers = javafx.collections.FXCollections.observableArrayList();
#Override
public void start(Stage stage) throws Exception {
loadFathers();
FXMLLoader loader = new FXMLLoader(getClass().getResource("test000.fxml"));
Parent root = (Parent) loader.load();
myController = loader.getController();
Scene scene = new Scene(root);
myController.persons = persons;
myController.fathers = fathers;
myController.mothers = mothers;
myController.setCBValues();
stage.setTitle("Check editable Combo - TEST000");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private void loadFathers() {
persons.clear();
persons.add(new APerson("*** Not ", "Known ***","F" ,2,0));
persons.add(new APerson("Nesammal", "Rivington","F" ,2,57));
persons.add(new APerson("Ratnammal","Andews","F",1,55));
persons.add(new APerson("Hornigold","Arthur","M", 1,63));
persons.add(new APerson("Andrews","Sundareson","M", 2,60));
persons.add(new APerson("Christopher","Easweradoss","M", 3,57));
persons.add(new APerson("Arthur","Kennedy","M", 4,55));
persons.add(new APerson("Victoria","Arthur","F" , 1,95));
persons.add(new APerson("Eliza", "Daniel","F", 1,60));
fathers.clear();
fathers.add(new APerson("*** Not ", "Known ***","F" ,2,0));
fathers.add(new APerson("Hornigold","Arthur","M", 1,63));
fathers.add(new APerson("Andrews","Sundareson","M", 2,60));
fathers.add(new APerson("Christopher","Easweradoss","M", 3,57));
fathers.add(new APerson("Arthur","Kennedy","M", 4,55));
mothers.clear();
mothers.add(new APerson("*** Not ", "Known ***","F" ,2,0));
mothers.add(new APerson("Nesammal", "Rivington","F" ,2,57));
mothers.add(new APerson("Ratnammal","Andews","F",1,55));
mothers.add(new APerson("Victoria","Arthur","F" , 1,95));
mothers.add(new APerson("Eliza", "Daniel","F", 1,60));
}
}
test000.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="502.0" prefWidth="325.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.test000Controller">
<children>
<VBox prefHeight="431.0" prefWidth="301.0" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="10.0">
<children>
<Label text="Fathers : ">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
</Label>
<ComboBox fx:id="cbFather" editable="true" onAction="#onCBFather" prefHeight="35.0" prefWidth="325.0" promptText="Enter search string" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin></ComboBox>
<TextField fx:id="txtValue" editable="false" prefHeight="38.0" prefWidth="301.0" promptText="Selected value" style="-fx-background-color: white; -fx-border-color: red;">
<VBox.margin>
<Insets top="15.0" />
</VBox.margin>
</TextField>
<Label text="Mothers : ">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="cbMother" editable="true" onAction="#onCBMother" prefHeight="35.0" prefWidth="325.0" promptText="Enter search string" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</ComboBox>
<Label text="Spouses : ">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="cbSpouse" editable="true" prefHeight="35.0" prefWidth="325.0" promptText="Enter search string" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</ComboBox>
<Label text="Gender:">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="cbGender" editable="true" onAction="#onCBGender" onMouseClicked="#handleGenderMoouseEvent" prefHeight="35.0" prefWidth="325.0" promptText="Select Gender" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</ComboBox>
</children>
</VBox>
<HBox layoutX="14.0" layoutY="450.0" prefHeight="38.0" prefWidth="294.0">
<children>
<Button fx:id="btnCB1" mnemonicParsing="false" onAction="#showCB1Value" prefHeight="41.0" prefWidth="123.0" text="CB1 value">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Button>
<Button fx:id="btnCB2" mnemonicParsing="false" onAction="#showCB2Value" prefHeight="41.0" prefWidth="123.0" text="CB2 value">
<font>
<Font name="System Bold" size="14.0" />
</font>
<HBox.margin>
<Insets left="25.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</children>
</AnchorPane>
test000Controller.java
package application;
import application.APerson;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class test000Controller {
boolean searchWordStartOnly = true;
private ObservableList<APerson> originalData;
private ObservableList<APerson> originalData2;
public javafx.collections.ObservableList<APerson> persons;
public javafx.collections.ObservableList<APerson> fathers;
public javafx.collections.ObservableList<APerson> mothers;
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private ComboBox<APerson> cbFather;
#FXML
private ComboBox<APerson> cbMother;
#FXML
private ComboBox<APerson> cbSpouse;
#FXML
private ComboBox<String> cbGender;
#FXML
private TextField txtValue;
#FXML
private Button btnCB1;
#FXML
private Button btnCB2;
#FXML
void onCBFather(ActionEvent event) {
if (event.getEventType().toString() != null) {
System.out.println("ActionEvent - CBFather - Fired = "+event.getEventType().toString());
}
}
#FXML
void onCBMother(ActionEvent event) {
if (event.getEventType().toString() != null) {
System.out.println("ActionEvent - CBMother - Fired = "+event.getEventType().toString());
}
}
#FXML
void showCB1Value(ActionEvent event) {
txtValue.setText("Button 1 was clicked - "+cbFather.getValue());
}
#FXML
void showCB2Value(ActionEvent event) {
txtValue.setText("Button 2 was clicked - "+cbMother.getValue());
}
#FXML
void onCBGender(ActionEvent event) {
// event.fireEvent(arg0, arg1);
cbGender.getSelectionModel().selectedItemProperty().addListener((options, oldValue, newValue) -> {
System.out.println("cbGender newValue -> " + newValue);
});
}
#FXML
void handleGenderMoouseEvent(MouseEvent event) {
System.out.println("This is MOUSE CLICKED on cbGENDER");
}
#FXML
void initialize() {
assert cbFather != null : "fx:id=\"cbFather\" was not injected: check your FXML file 'test000.fxml'.";
assert txtValue != null : "fx:id=\"txtValue\" was not injected: check your FXML file 'test000.fxml'.";
assert cbMother != null : "fx:id=\"cbMother\" was not injected: check your FXML file 'test000.fxml'.";
assert cbSpouse != null : "fx:id=\"cbSpouse\" was not injected: check your FXML file 'test000.fxml'.";
assert btnCB1 != null : "fx:id=\"btnCB1\" was not injected: check your FXML file 'test000.fxml'.";
assert btnCB2 != null : "fx:id=\"btnCB2\" was not injected: check your FXML file 'test000.fxml'.";
assert cbGender != null : "fx:id=\"cbGender\" was not injected: check your FXML file 'test000.fxml'.";
}
public void setCBValues() {
cbGender.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue ov, String t, String t1) {
System.out.println("Change Listener for cbGender - oldValue "+t+" new value"+t1);
if ((t1.equals("Male")) || (t.equals(null))) {
cbSpouse.setItems(mothers);
} else {
cbSpouse.setItems(fathers);
}
cbSpouse.getSelectionModel().selectFirst();
}
});
//
ObservableList<String> GenderOptions = FXCollections.observableArrayList("Male", "Female");
cbGender.setItems(GenderOptions);
cbGender.setValue("Male");
createCBFather(persons);
createCBMother(persons);
}
public void createCBFather(javafx.collections.ObservableList<APerson> persons) {
cbFather.getItems().clear();
cbFather.setItems(persons);
cbFather.setEditable(true);
cbFather.setMaxWidth(Double.MAX_VALUE);
cbFather.setVisibleRowCount(5);
originalData = cbFather.getItems();
cbFather.getEditor().setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (cbFather.getEditor().getText() == null) {
cbFather.getItems().clear();
cbFather.setItems(persons);
cbFather.setEditable(true);
cbFather.setMaxWidth(Double.MAX_VALUE);
cbFather.setVisibleRowCount(5);
}
System.out.println("MOUSE PRESSED!!! in EDITOR");
}
});
//
cbFather.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (cbFather.getEditor().getText() == null) {
cbFather.getItems().clear();
cbFather.setItems(persons);
cbFather.setEditable(true);
cbFather.setMaxWidth(Double.MAX_VALUE);
cbFather.setVisibleRowCount(5);
}
System.out.println("MOUSE PRESSED!!! in CB BUTTON");
}
});
//
cbFather.getSelectionModel().selectFirst();
//
cbFather.setConverter(new CBFatherConverter());
//
setCellFactory(cbFather);
//
handleKeyPressed(cbFather, originalData);
cbFather.valueProperty().addListener(new ChangeListener<APerson>() {
#Override
public void changed(ObservableValue ov, APerson t, APerson t1) {
System.out.println("Change Listener for cbFather - ov "+t+" new value"+t1);
if (t1 == null) {
cbFather.setItems(persons);
// cbFather.getSelectionModel().selectFirst();;
}
}
});
}
class CBFatherConverter extends StringConverter<APerson> {
#Override
public String toString(APerson object) {
if (object == null) {
cbFather.setPromptText("Use Arrow Keys or type to locate entry");
return null;
}
return object.toString();
}
#Override
public APerson fromString(String thisString) {
// TODO Auto-generated method stub
for (APerson pers : cbFather.getItems()) {
if (pers.toString().equals(thisString)) {
APerson person = pers;
return person;
}
}
return null;
}
}
//
public void createCBMother(javafx.collections.ObservableList<APerson> persons) {
cbMother.setItems(persons);
cbMother.setEditable(true);
cbMother.setMaxWidth(Double.MAX_VALUE);
cbMother.setVisibleRowCount(5);
originalData2 = cbMother.getItems();
cbMother.getSelectionModel().selectFirst();
// if (cbGender.getValue().equals("Male")) {
// cbSpouse.setItems(mothers);
// } else {
// cbSpouse.setItems(fathers);
// }
//
cbMother.setConverter(new CBMotherConverter());
//
setCellFactory(cbMother);
//
handleKeyPressed(cbMother, originalData2);
// cbMother.valueProperty().addListener(new ChangeListener<APerson>() {
// #Override
// public void changed(ObservableValue ov, APerson t, APerson t1) {
// System.out.println("Change Listener for cbMother - ov "+t+" new value"+t1);
// }
// });
//
// cbGender.setValue("Male");
//
}
//
class CBMotherConverter extends StringConverter<APerson> {
#Override
public String toString(APerson object) {
if (object == null) {
cbMother.setPromptText("Use Arrorw Keys or type to locate entry");
return null;
}
return object.toString();
}
#Override
public APerson fromString(String thisString) {
// TODO Auto-generated method stub
for (APerson pers : cbMother.getItems()) {
if (pers.toString().equals(thisString)) {
APerson person = pers;
return person;
}
}
return null;
}
}
//
public void setCellFactory(ComboBox<APerson> thisCombo) {
thisCombo.setCellFactory(new Callback<ListView<APerson>, ListCell<APerson>>() {
#Override
public ListCell<APerson> call(ListView<APerson> param) {
final ListCell<APerson> cell = new ListCell<APerson>() {
#Override
protected void updateItem(APerson item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
final ListCell<APerson> this$ = this;
item.LoadToolTipText();
String htmlString = item.getToolTip().getText();
Tooltip.install(this$, createToolTip(htmlString));
String str = item.toString();
setText(str);
if (item.getGender() == "M") {
String cellStyle = " -fx-font-size: 16;\n"
+ " -fx-font-family: \"Times New Roman\";\n" + "-fx-text-fill: blue;";
setStyle(cellStyle);
} else {
String cellStyle = " -fx-font-size: 16;\n"
+ " -fx-font-family: \"Times New Roman\";\n" + "-fx-text-fill: red;";
setStyle(cellStyle);
}
}
}
}; // ListCell
return cell;
}
}); // setCellFactory
}
public void handleKeyPressed(ComboBox<APerson> thisCombo, ObservableList<APerson> originalData) {
thisCombo.addEventHandler(KeyEvent.KEY_PRESSED, t -> thisCombo.hide());
thisCombo.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
private boolean moveCaretToPos = false;
private int caretPos;
#Override
public void handle(KeyEvent event) {
switch (event.getCode()) {
case UP:
if (thisCombo.getEditor().equals(null)) {
thisCombo.getSelectionModel().selectFirst();
thisCombo.show();
}
break;
//
case DOWN:
System.out.println("Processing DOWN arrow");
if (thisCombo.getEditor().equals(null)) {
System.out.println("Editor is NULL");
thisCombo.getSelectionModel().selectFirst();
thisCombo.show();
}
if (!thisCombo.isShowing()) {
if (thisCombo.getItems().size() == 0) {
System.out.println("Processing DOWN arrow - Combo Box is EMPTY");
thisCombo.setItems(originalData);
} else {
System.out.println("Processing DOWN arrow - Has items.");
}
thisCombo.getSelectionModel().selectFirst();
thisCombo.show();
}
break;
//
case BACK_SPACE:
moveCaretToPos = true;
caretPos = thisCombo.getEditor().getCaretPosition();
break;
//
case DELETE:
moveCaretToPos = true;
caretPos = thisCombo.getEditor().getCaretPosition();
break;
case ENTER:
break;
//
case RIGHT:
case LEFT:
case SHIFT:
case HOME:
case END:
case TAB:
case CONTROL:
return;
default:
if (event.getCode().ALPHANUMERIC != null) {
// System.out.println("in ALPHANUMERIC input");
locateItems();
}
break;
}
APerson waste = (APerson) getComboBoxValue(thisCombo);
if (waste != null) {
txtValue.setText(waste.getFirstName()+" ID = "+waste.getPersonID());
} else {
txtValue.setText("");
}
}
private void locateItems() {
txtValue.setText("Alpha numeric input");
ObservableList<APerson> list = FXCollections.observableArrayList();
int selectedIndex = 0;
for (APerson aData : originalData) {
String tmp = thisCombo.getEditor().getText().toLowerCase();
if (checkMatch(thisCombo, aData, tmp)) {
list.add(aData);
selectedIndex++;
}
}
String t = thisCombo.getEditor().getText();
thisCombo.setItems(list);
thisCombo.getEditor().setText(t);
if (!moveCaretToPos) {
caretPos = -1;
}
moveCaret(t.length());
if (!list.isEmpty()) {
thisCombo.hide();
if (list.size() > 5) {
thisCombo.setVisibleRowCount(5);
} else {
thisCombo.setVisibleRowCount(list.size());
}
thisCombo.getSelectionModel().select(selectedIndex);
thisCombo.show();
}
}
private void moveCaret(int textLength) {
if (caretPos == -1) {
thisCombo.getEditor().positionCaret(textLength);
} else {
thisCombo.getEditor().positionCaret(caretPos);
}
moveCaretToPos = false;
}
});
}
private boolean checkMatch(ComboBox thisCombo, APerson aData, String tmp) {
boolean retValue = false;
if (aData != null && thisCombo.getEditor().getText() != null) {
String[] words = aData.toString().split(" ");
for (String thisWord : words) {
if (searchWordStartOnly) {
if (thisWord.toLowerCase().startsWith(tmp)) {
return true;
}
} else {
if (thisWord.toLowerCase().contains(tmp)) {
return true;
}
}
}
}
return retValue;
}
public Object getComboBoxValue(ComboBox thisCombo) {
System.out.println("getComboBoxValue - name = "+thisCombo.getSelectionModel().getSelectedItem());
if (thisCombo.getSelectionModel().getSelectedIndex() < 0) {
return null;
} else {
System.out.println("Item Selected in cbFather is = "+thisCombo.getSelectionModel().getSelectedItem());
return thisCombo.getSelectionModel().getSelectedItem();
}
}
//
private Tooltip createToolTip(String htmlStr) {
Tooltip thisToolTip = new Tooltip();
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.loadContent(htmlStr);
thisToolTip.setStyle("\n" + " -fx-border-color: black;\n" + " -fx-border-width: 1px;\n"
+ " -fx-font: normal bold 12pt \"Times New Roman\" ;\n"
+ " -fx-background-color: LavenderBlush;\n" + " -fx-text-fill: black;\n"
+ " -fx-background-radius: 4;\n" + " -fx-border-radius: 4;\n" + " -fx-opacity: 1.0;");
thisToolTip.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
thisToolTip.setGraphic(browser);
thisToolTip.setAutoHide(false);
thisToolTip.setMaxWidth(300);
thisToolTip.setPrefHeight(400);
thisToolTip.setGraphicTextGap(0.0);
return thisToolTip;
}
}
In this example data is Hard coded and in Main.java, but in the other version, it is loaded from
static private javafx.collections.ObservableList Fathers = javafx.collections.FXCollections.observableArrayList();
static private javafx.collections.ObservableList Mothers = javafx.collections.FXCollections.observableArrayList();
loading the ObservableList from database occurs much earlier, before cbGender valueProperty Changed.
Thanks,

JavaFX TableView custom cell rendering split menu button

i've a problem with a custom cell render in a java fx table view component. I'm able to render the split menu button, but is only rendered from second row of table.
Below i put the code created to generate that image.
SplitMenuButtonApp.java
package com.example.splimenubtn;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SplitMenuButton;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class SplitMenuButtonApp extends Application {
private class Contact {
private StringProperty firstName;
private StringProperty lastName;
public Contact() {}
public Contact(String fName, String lName) {
firstName = new SimpleStringProperty(fName);
lastName = new SimpleStringProperty(lName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public StringProperty firstName() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lName) {
lastName.set(lName);
}
public StringProperty lastName() {
return lastName;
}
}
private ObservableList<Contact> data;
protected List<MenuItemFactory<Contact>> menuItemsList;
private TableView<Contact> table;
#Override
public void start(Stage primaryStage) throws Exception {
// Init data list
data = FXCollections.observableArrayList();
data.add(new Contact("Mickey", "Mouse"));
data.add(new Contact("Donald", "Duck"));
data.add(new Contact("Fantasy", "Name"));
initMenuButton();
SplitMenuButtonFactory<Contact> sMBtn = new SplitMenuButtonFactory<>();
sMBtn.setMenuItems(menuItemsList);
SplitMenuButton actions = sMBtn.buildButton();
// Build the list
table = new TableView<>();
TableColumn<Contact, String> col = new TableColumn<>("First Name");
col.setCellValueFactory(c -> c.getValue().firstName);
table.getColumns().add(col);
col = new TableColumn<>("Last Name");
col.setCellValueFactory(c -> c.getValue().lastName);
table.getColumns().add(col);
TableColumn<Contact, SplitMenuButton> aCol = new TableColumn<>("Action");
aCol.setCellValueFactory(new PropertyValueFactory<>(""));
aCol.setCellFactory(new ButtonCellFactory<>(actions));
table.getColumns().add(aCol);
table.setItems(data);
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(table, 5.0);
AnchorPane.setRightAnchor(table, 5.0);
AnchorPane.setBottomAnchor(table, 5.0);
AnchorPane.setLeftAnchor(table, 5.0);
root.getChildren().add(table);
Scene s = new Scene(root, 600d, 300d);
primaryStage.setScene(s);
primaryStage.setTitle("Split menu button on table row");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void initMenuButton() {
if (menuItemsList == null) {
menuItemsList = new ArrayList<>();
menuItemsList.add(new MenuItemFactory<Contact>(MenuItemActions.EDIT, "Edit", true).setDataList(table));
menuItemsList.add(new MenuItemFactory<Contact>(MenuItemActions.DELETE, "Delete", false).setDataList(table));
}
}
}
MenuItemActions.java
package com.example.splimenubtn;
public enum MenuItemActions {
/**
* Detail item
*/
DETAILS,
/**
* Edit/Update item
*/
EDIT,
/**
* Delete item
*/
DELETE;
}
MenuItemFactory.java
package com.example.splimenubtn;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableView;
public class MenuItemFactory<S> {
private MenuItemActions itemType;
private String itemLbl;
private TableView<S> table;
private boolean defaultAction;
public MenuItemFactory() {}
public MenuItemFactory(MenuItemActions itemType, String itemLabel, boolean dA) {
this.itemType = itemType;
itemLbl = itemLabel;
defaultAction = dA;
}
public MenuItemFactory<S> setDataList(TableView<S> t) {
table = t;
return this;
}
public boolean isDefault() {
return defaultAction;
}
public MenuItem buildMenuItem() {
MenuItem mI = new MenuItem();
switch (itemType) {
case DETAILS:
mI.setText(itemLbl);
mI.setOnAction(handleDetails());
break;
case EDIT:
mI.setText(itemLbl);
mI.setOnAction(handleEdit());
break;
case DELETE:
mI.setText(itemLbl);
mI.setOnAction(handleDelete());
break;
default:
break;
}
return mI;
}
private EventHandler<ActionEvent> handleDetails() {
return new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent aE) {
System.out.println("*** DETAIL REQUESTED ***");
}
};
}
private EventHandler<ActionEvent> handleEdit() {
return new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent aE) {
System.out.println("*** EDIT REQUESTED ***");
}
};
}
private EventHandler<ActionEvent> handleDelete() {
return new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent aE) {
System.out.println("*** DELETE REQUESTED ***");
}
};
}
}
ButtonCellFactory.java
package com.example.splimenubtn;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.SplitMenuButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
public class ButtonCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private SplitMenuButton btn;
public ButtonCellFactory() {}
public ButtonCellFactory(SplitMenuButton b) {
btn = b;
}
#Override
public TableCell<S, T> call(TableColumn<S, T> param) {
return new TableCell<S, T>() {
#Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(btn);
}
}
};
}
}
SpliMenuButtonFactory.java
package com.example.splimenubtn;
import java.util.List;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SplitMenuButton;
public class SplitMenuButtonFactory<T> {
private List<MenuItemFactory<T>> menuItems;
public SplitMenuButtonFactory() {}
public SplitMenuButtonFactory<T> setMenuItems(List<MenuItemFactory<T>> items) {
menuItems = items;
return this;
}
public SplitMenuButton buildButton() {
SplitMenuButton menuBtn = new SplitMenuButton();
for (MenuItemFactory<?> mIF : menuItems) {
MenuItem btn = mIF.buildMenuItem();
if (mIF.isDefault()) {
menuBtn.setText(btn.getText());
menuBtn.setOnAction(btn.getOnAction());
}
menuBtn.getItems().add(btn);
}
return menuBtn;
}
}
As you see in the image, with this code i'm able to create the spli menu button and add it to ta table, but is only rendered on last row.
I need suggestion to render the split menu button in the other row, any help is appreciated.
Cause you have use the same button in every cell, So it's set a button only last of the cell Value.
Remove this line in SplitMenuButtonApp class
SplitMenuButton actions = sMBtn.buildButton();
And replace this line
aCol.setCellFactory(new ButtonCellFactory<>(actions));
To below code
Callback<TableColumn<Contact, SplitMenuButton>, TableCell<Contact, SplitMenuButton>> actionsCol = new Callback<TableColumn<Contact, SplitMenuButton>, TableCell<Contact, SplitMenuButton>>() {
#Override
public TableCell call(final TableColumn<Contact, SplitMenuButton> param) {
final TableCell<Contact, SplitMenuButton> cell = new TableCell<Contact, SplitMenuButton>() {
SplitMenuButton actions = sMBtn.buildButton();
#Override
public void updateItem(SplitMenuButton item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
setGraphic(actions);
setText(null);
}
}
};
return cell;
}
};
aCol.setCellFactory(actionsCol);
I hope this code is working for you:)

JavaFX : TableView ToogleButton Column

My TableView has a column with a ToggleButton. All the buttons belong to one group, you can only select one button (one row).
But my TableView has a lot of rows and the ToggleGroup seems to work.
That is until I scroll drown.
When I select one ToggleButton and scroll down no other button should be selected but there is always one that is selected per view.
Is this fixable?
Edit: Here is a SSCCE :
MainApp.java :
package p1;
import java.io.IOException;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainApp extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private ObservableList<Person> personData = FXCollections.observableArrayList();
public MainApp(){
for(int i=0;i<40;i++){
personData.add(new Person("person " +i));
}
}
public ObservableList<Person> getPersonData(){
return personData;
}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
try{
FXMLLoader loader =new FXMLLoader();
loader.setLocation(MainApp.class.getResource("People.fxml"));
rootLayout = (AnchorPane)loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
PeopleController controller = loader.getController();
controller.setMainApp(this);
} catch(IOException e){
e.printStackTrace();
}
}
public Stage getPrimaryStage(){
return primaryStage;
}
public static void main(String[] args){
launch(args);
}}
People.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="p1.PeopleController">
<children>
<TableView fx:id="personTable" layoutX="160.0" layoutY="49.0" prefHeight="351.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="49.0">
<columns>
<TableColumn fx:id="nameColumn" prefWidth="75.0" text="Name" />
<TableColumn fx:id="previewColumn" prefWidth="75.0" text="Preview"/>
</columns>
</TableView>
</children>
</AnchorPane>
PeopleController.java :
package p1;
import com.sun.prism.impl.Disposer;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.util.Callback;
public class PeopleController{
#FXML private TableView<Person> personTable;
#FXML private TableColumn<Person, String> nameColumn;
#FXML private TableColumn previewColumn;
private MainApp mainApp;
final ToggleGroup group = new ToggleGroup();
#FXML
public void initialize() {
nameColumn.setCellValueFactory(cellData -> cellData.getValue().NameProperty());
previewColumn.setCellFactory(
new Callback<TableColumn<Disposer.Record, Boolean>, TableCell<Disposer.Record, Boolean>>() {
#Override
public TableCell<Disposer.Record, Boolean> call(TableColumn<Disposer.Record, Boolean> p) {
ButtonCell cell = new ButtonCell(group);
cell.setAlignment(Pos.CENTER);
return cell;
}
});
}
public void setMainApp(MainApp mainApp){
this.mainApp = mainApp;
personTable.setItems(mainApp.getPersonData());
}
public class ButtonCell extends TableCell<Disposer.Record, Boolean> {
final ToggleButton cellButton = new ToggleButton("click");
public ButtonCell(ToggleGroup group){
cellButton.setToggleGroup(group);
}
#Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if(!empty){
setGraphic(cellButton);
}
}}}
Person.java :
package p1;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty name;
public Person(){
this(null);
}
public Person(String name){
this.name = new SimpleStringProperty(name);
}
public String getName(){
return name.get();
}
public void setName(String name){
this.name.set(name);
}
public StringProperty NameProperty(){
return name;
} }
The reason that the toggle seems to "jump" when scrolling is the re-use of the cells: the selected state sticks to the button, not the item. Consequently, you can't use a toggleGroup (except in the not-so-common and not recommended case that the items in your data implement Toggle) to keep the toggle state. You need to implement the toggle-logic yourself.
One option is a custom SingleSelectionModel and a custom ButtonCell that talks to the model (as do all other collaborators). Unfortunately, FX doesn't have a publicly accessible concrete implementation of the model. As often, the heavy lifting - which in this case is to update itself on modifications to the items - is left to client code (and not done in this example as well ;-)
Something like:
public class ToggleButtonTableExample extends Application {
public static class DataSelectionModel<S> extends SingleSelectionModel<S> {
private ListProperty<S> listProperty;
public DataSelectionModel(Property<ObservableList<S>> items) {
//listProperty = BugPropertyAdapters.listProperty(items);
listProperty = new SimpleListProperty<>();
listProperty.bindBidirectional(items);
ListChangeListener<S> itemsContentObserver = c -> {
itemsChanged(c);
};
listProperty.addListener(itemsContentObserver);
}
protected void itemsChanged(Change<? extends S> c) {
// TODO need to implement update on modificatins to the underlying list
}
#Override
protected S getModelItem(int index) {
if (index < 0 || index >= getItemCount()) return null;
return listProperty.get(index);
}
#Override
protected int getItemCount() {
return listProperty.getSize();
}
}
public static class ButtonCellX<S, T> extends TableCell<S, T> {
private ToggleButton cellButton;
private SingleSelectionModel<S> model;
public ButtonCellX(SingleSelectionModel<S> group) {
this.model = group;
cellButton = new ToggleButton("click");
cellButton.setOnAction(e -> updateToggle());
updateToggle();
setAlignment(Pos.CENTER);
}
protected void updateToggle() {
model.select(cellButton.isSelected()? getIndex() : -1);
}
#Override
protected void updateItem(T t, boolean empty) {
super.updateItem(t, empty);
if (empty) {
setGraphic(null);
} else {
cellButton.setSelected(model.isSelected(getIndex()));
setGraphic(cellButton);
}
}
}
private Parent getContent() {
TableView<Person> table = new TableView<>();
table.setItems(Person.persons());
TableColumn<Person, String> name = new TableColumn<>("Name");
name.setCellValueFactory(new PropertyValueFactory<>("lastName"));
SingleSelectionModel<Person> model = new DataSelectionModel<>(table.itemsProperty());
TableColumn<Person, Boolean> toggle = new TableColumn<>("Preview");
toggle.setCellFactory(c -> new ButtonCellX<Person, Boolean>(model));
toggle.setCellValueFactory(f -> {
Object value = f.getValue();
return Bindings.equal(value, model.selectedItemProperty());
});
table.getColumns().addAll(name, toggle);
Button select = new Button("Select 0");
select.setOnAction(e -> {
model.select(0);
});
VBox content = new VBox(10, table, select);
return content;
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
//primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(ChoiceBoxTableCellDynamic.class.getName());
}

JavaFX editable table cell having display errors

Editing the question as per Brian's suggestion:
I have now trimmed it down to just a single integer column.
I am simply not able to find the problem in the code. THe values show up only when the columns are selected once for edit.
Please suggest of i am making some mistake in the EditingCell class.
The single classfile code that i am trying is as follows.
package javafxtest;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
//import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
* FXML Controller class
*
* #author balz
*/
public class UploadTemplateController extends Application {
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
MasterUploadParameter objMasterUploadParameter=new MasterUploadParameter();
MasterUploadParameter objMasterUploadParameter1=new MasterUploadParameter();
MasterUploadParameter objMasterUploadParameter2=new MasterUploadParameter();
final ObservableList<MasterUploadParameter> tableContent =
FXCollections.observableArrayList
(
objMasterUploadParameter,
objMasterUploadParameter1,
objMasterUploadParameter2
);
TableColumn ColumnID = new TableColumn();
ColumnID.setText("columnId");
ColumnID.setCellValueFactory(new PropertyValueFactory("columnId"));
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
ColumnID.setCellFactory(cellFactory);
TableView tableView = new TableView();
tableView.setItems(tableContent);
//Enabling editing
tableView.setEditable(true);
tableView.getColumns().addAll(ColumnID);
root.getChildren().add(tableView);
}
private void updateObservableListProperties(TableColumn<MasterUploadParameter, Integer> ColumnFieldNo, TableColumn<MasterUploadParameter, Boolean> ColumnLPad, TableColumn<MasterUploadParameter, Integer> ColumnStarting, TableColumn<MasterUploadParameter, Integer> ColumnEnding, TableColumn<MasterUploadParameter, String> ColumnSheetID, TableColumn<MasterUploadParameter, Integer> ColumnID, TableColumn<MasterUploadParameter, Integer> ColumnStartingRow, TableColumn<MasterUploadParameter, Integer> ColumnEnding0) {
ColumnFieldNo.setOnEditCommit(new EventHandler<CellEditEvent<MasterUploadParameter, Integer>>() {
#Override public void handle(CellEditEvent<MasterUploadParameter, Integer> t) {
((MasterUploadParameter) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setcolumnId(new SimpleIntegerProperty(t.getNewValue()));
}
});
}
public static class MasterUploadParameter {
private SimpleIntegerProperty columnId;
public SimpleIntegerProperty columnIdProperty() {return columnId;}
public void setcolumnId(SimpleIntegerProperty columnId) {this.columnId = columnId;}
public MasterUploadParameter() {
this.columnId=new SimpleIntegerProperty(0);
}
}
// EditingCell - for editing capability in a TableCell
public static class EditingCell extends TableCell<MasterUploadParameter, Integer> {
private TextField textField;
public EditingCell() {
System.out.println("find value of textField: "+textField);
}
#Override public void startEdit() {
super.startEdit();
System.out.println("find value of textField: "+textField);
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setGraphic(null);
}
public void updateItem(Integer item, boolean empty) {
System.out.println("find value of update: "+empty+item);
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(Integer.parseInt(textField.getText()));
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
You don't need separate editing cell types. You can use the col.setOnEditCommit method
Just use public static class EditingCell extends TableCell{ with no type information. (I guess that's why you left out #override before.)
Change the updateItem to #Override public void updateItem(Object item, boolean empty) {
Then whenever you call commitEdit use a string (all textfields have strings anyway). commitEdit(textField.getText())
Then for each column depending, on the type you can commit the edit however you want.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
//import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
* FXML Controller class
*
* #author balz
*/
public class UploadTemplateController extends Application {
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
MasterUploadParameter objMasterUploadParameter=new MasterUploadParameter();
MasterUploadParameter objMasterUploadParameter1=new MasterUploadParameter();
MasterUploadParameter objMasterUploadParameter2=new MasterUploadParameter();
final ObservableList<MasterUploadParameter> tableContent =
FXCollections.observableArrayList
(
objMasterUploadParameter,
objMasterUploadParameter1,
objMasterUploadParameter2
);
Callback<TableColumn, TableCell> cellFactory = new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
TableColumn ColumnID = new TableColumn();
ColumnID.setText("columnId");
ColumnID.setCellValueFactory(new PropertyValueFactory("columnId"));
ColumnID.setCellFactory(cellFactory);
ColumnID.setOnEditCommit(new EventHandler<CellEditEvent<MasterUploadParameter, String>>() {
#Override
public void handle(CellEditEvent<MasterUploadParameter, String> evt) {
try {
evt.getTableView().getItems().get(evt.getTablePosition().getRow())
.columnId.set(Integer.parseInt(evt.getNewValue()));
} catch (NumberFormatException numberFormatException) {
//do whatever if a non int is entered
}
}
});
TableColumn columnDesc = new TableColumn("Desc");
columnDesc.setCellValueFactory(new PropertyValueFactory("desc"));
columnDesc.setCellFactory(cellFactory);
columnDesc.setOnEditCommit(new EventHandler<CellEditEvent<MasterUploadParameter, String>>() {
#Override
public void handle(CellEditEvent<MasterUploadParameter, String> evt) {
evt.getTableView().getItems().get(evt.getTablePosition().getRow())
.desc.set(evt.getNewValue());
}
});
TableView tableView = new TableView();
tableView.setItems(tableContent);
//Enabling editing
tableView.setEditable(true);
tableView.getColumns().addAll(ColumnID, columnDesc);
root.getChildren().add(tableView);
}
public static class MasterUploadParameter {
private SimpleIntegerProperty columnId;
public SimpleIntegerProperty columnIdProperty() {return columnId;}
private StringProperty desc;
public StringProperty descProperty() {return desc;}
public MasterUploadParameter() {
this.columnId=new SimpleIntegerProperty(0);
this.desc = new SimpleStringProperty("hi");
}
}
// EditingCell - for editing capability in a TableCell
public static class EditingCell extends TableCell{
private TextField textField;
#Override public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
Platform.runLater(new Runnable() {
#Override
public void run() {
textField.requestFocus();
}
});
}
}
#Override public void cancelEdit() {
super.cancelEdit();
try {
setText(getItem().toString());
} catch (Exception e) {
}
setGraphic(null);
}
#Override public void updateItem(Object item, boolean empty) {
System.out.println("find value of update: "+empty+item);
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}

Resources