How to implement correct doubleclick-to-edit in TableView - javafx

Problem
The TableView seems to go into edit mode as soon as you click on an already selected cell. This is unwanted and inconsistent behavior, because the selection changes if you click outside of the current selection. It doesn't change when you click inside the current selection.
If anything, a real double click should be required to go into edit mode, at least when you work on a desktop.
Reproduction:
Create a TableView with this selection mode:
// cell selection mode instead of row selection
table.getSelectionModel().setCellSelectionEnabled(true);
// allow selection of multiple cells
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Now select multiple cells (via shift keypress). Then click on a single cell of the currently selected cells.
The current behavior:
the table goes into edit mode
Question
The requested behavior:
only the selection should change, i. e. the single cell should be selected; edit mode should only happen on a double-click
How can you achieve this?
Code
Here's a full example. I took the code from the Oracle samples website, added the above lines and pre-selected all cells. When you click on a single cell after program start, the table goes directly into edit mode:
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
firstNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFirstName(t.getNewValue());
}
}
);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
lastNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue());
}
}
);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
emailCol.setCellFactory(TextFieldTableCell.forTableColumn());
emailCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setEmail(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
// cell selection mode instead of row selection
table.getSelectionModel().setCellSelectionEnabled(true);
// allow selection of multiple cells
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// select all for demo purposes
table.getSelectionModel().selectAll();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}

You can try to clear selection on mouse pressed event:
table.addEventFilter( MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>()
{
#Override
public void handle( MouseEvent event )
{
if( event.isControlDown()) {
return;
}
if ( table.getEditingCell() == null) {
table.getSelectionModel().clearSelection();
}
}
});

Related

TableView is data is not shown in JavaFX

I am using TableView in JavaFX but tableview does not show any data here is my code.
public static class person {
private final SimpleIntegerProperty SR;
private final SimpleStringProperty URL;private person(int sr1,String url1 ){
this.SR=new SimpleIntegerProperty(sr1);
this.URL=new SimpleStringProperty(url1);
}
public int getsr() {
return SR.get();
}
public void setsr(int sr1) {
this.SR.set(sr1);
}
public String geturl() {
return URL.get();
}
public void seturl(String url2) {
this.URL.set(url2);
}
}
public TableView<person> table; TableColumn firstNameCol = new TableColumn("First Name");
TableColumn lastNameCol = new TableColumn("Last Name");
private final ObservableList<person> data1 =
FXCollections.observableArrayList(
new person((int)1, "Smith"),
new person((int)2, "Johnson"),
new person((int)3, "Williams"),
new person((int)4, "Jones"),
new person((int)5, "Brown")
);
public void btnScrapping(ActionEvent event) {
table.setEditable(true);
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<person,Integer>("SR"));
lastNameCol.setMinWidth(500);
lastNameCol.setCellValueFactory(new PropertyValueFactory<person,String>("URL"));
table.setItems(data1);
table.getColumns().addAll(firstNameCol, lastNameCol);
}

JavaFX table- how to add components and relate them to other scene?

Is there any way --by clicking on the embedded button -- to open a new scene that is related to the tableview raw selected :
for example :
public class TestClass extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("A", "B", "email#example.com","info"),
new Person("X", "Y", "email2#example.com","info"),
new Person("Z", "W", "email2#example.com","info")
);
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Override
public void start(Stage stage) {
stage.setTitle("Table View Sample");
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
final Label actionTaken = new Label();
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
TableColumn<Person, Person> btnCol = new TableColumn<>("info");
btnCol.setMinWidth(150);
btnCol.setCellValueFactory(new Callback<CellDataFeatures<Person, Person>, ObservableValue<Person>>() {
#Override public ObservableValue<Person> call(CellDataFeatures<Person, Person> features) {
return new ReadOnlyObjectWrapper(features.getValue());
}
});
btnCol.setComparator(new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return p1.getLikes().compareTo(p2.getLikes());
}
});
btnCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
#Override public TableCell<Person, Person> call(TableColumn<Person, Person> btnCol) {
Stage stage = null;
return new AddCell(stage, table);
}
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, btnCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 10, 10, 10));
vbox.getChildren().addAll(label, table, actionTaken);
VBox.setVgrow(table, Priority.ALWAYS);
stage.setScene(new Scene(vbox));
stage.show();
}
}
the Person Class :
public class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleStringProperty infos;
private Person(String fName, String lName, String email, String infos) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.infos = new SimpleStringProperty(infos);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public String getLikes() {
return infos.get();
}
public void setLikes(String likes) {
this.infos.set(likes);
}
}
}
the AddCell class :
public class AddCell extends TableCell<Person, Boolean> {
final Button addButton = new Button("Elément");
final StackPane paddedButton = new StackPane();
final DoubleProperty buttonY = new SimpleDoubleProperty();
private MessageCmiService messageService=new MessageCmiServiceImpl();
public AddCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(addButton);
addButton.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
buttonY.set(mouseEvent.getScreenY());
}
});
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
//???
// get the info
}
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
}
}
}
In your handler, you can do
Person currentPerson = getTableView().getItems().get(getIndex());
which gives you access to all the data in the current row.

JAVAFX - The hidden column's mysterious solution

I have a TableView which shows all the data from a Derby table, except the "ID" of the items (I don't want the user to see it.)
The method is simple, I made an SQL statement to select all the attributes but I did not ask for the ID column. I got back a TableData and this variable is shown in the TableView with all of the records (It is a simple namelist).
I want to allow the user to delete from the table with a "delete button".
So first, there should be an "OnAction" method, (when the user clicks the delete button) when we gather the ACTUAL selected row's (if not null) ID, and we send a statement to the database to delete it, where the (HIDDEN) ID of the selected item can be found at the derby table.
Since it is a Namelist, and the user is allowed to make another record with the exactly same data, in the tableview the records can be clones, with no differences. (only the ID is uniqe, but the tableview does not contain the id-s, that makes it so hard).
So how could we delete the selected line without knowing it's ID? Or how can we know the ID when it is not shown in the table? (searching on the name is not working, since there can be several records with the same name)
What is the "hidden column's mysterious solution"? :)
Your question has a simple solution. If you don't want to show the ID on the tableview, you don't have to. TableView binds with your class, in your case user, and not with its fields. When you add the values to the tableview, you can control which fields you want to show on it. Whether you show it on the tableview or not, you still have your complete user object with you (which still has the ID).
Now, there are multiple ways to do this. One of the way is to bind the id with the delete button that you want to show in each row. Whenever the delete button is pressed, delete the item from the DB and delete it from the tableview.
I have create an working example which shows how this work. I have hardcoded my values and instead of deleting it from the database, I am printing the ID value on the console.
In this example, the Person class can be considered equivalent to your user, each of which has an id and other attributes.
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person(10, "Jacob", "Smith", "jacob.smith#example.com"),
new Person(20, "Isabella", "Johnson", "isabella.johnson#example.com"),
new Person(30, "Ethan", "Williams", "ethan.williams#example.com"),
new Person(40, "Emma", "Jones", "emma.jones#example.com"),
new Person(50, "Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(600);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
TableColumn deleteCol = new TableColumn("Delete");
deleteCol.setMinWidth(100);
deleteCol.setCellFactory(param -> new ButtonCell());
deleteCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("id"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, deleteCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleIntegerProperty id;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(Integer id, String fName, String lName, String email) {
this.id = new SimpleIntegerProperty(id);
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
private class ButtonCell extends TableCell<Person, Integer> {
Image buttonDeleteImage = new Image("https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/fileclose.png");
final Button cellDeleteButton = new Button("", new ImageView(buttonDeleteImage));
ButtonCell() {
cellDeleteButton.setOnAction(actionEvent -> {
System.out.println("Deleted Id : " + getItem());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());
});
}
#Override
protected void updateItem(Integer t, boolean empty) {
super.updateItem(t, empty);
if (!empty) {
setGraphic(cellDeleteButton);
} else {
setGraphic(null);
}
}
}
}
To do it without binding ID
You need :
deleteCol.setCellValueFactory(p -> {
return new ReadOnlyObjectWrapper<Person>((Person)p.getValue());
});
The custom ButtonCell should extend TableCell<Person, Person>
The logic to delete item becomes :
System.out.println("Deleted ID : " +
getItem().getId());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());
Complete Example:
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person(10, "Jacob", "Smith", "jacob.smith#example.com"),
new Person(20, "Isabella", "Johnson", "isabella.johnson#example.com"),
new Person(30, "Ethan", "Williams", "ethan.williams#example.com"),
new Person(40, "Emma", "Jones", "emma.jones#example.com"),
new Person(50, "Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(600);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
TableColumn deleteCol = new TableColumn("Delete");
deleteCol.setMinWidth(100);
deleteCol.setCellFactory(param -> new ButtonCell());
deleteCol.setCellValueFactory(p -> {
return new ReadOnlyObjectWrapper<Person>((Person)p.getValue());
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, deleteCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleIntegerProperty id;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(Integer id, String fName, String lName, String email) {
this.id = new SimpleIntegerProperty(id);
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
private class ButtonCell extends TableCell<Person, Person> {
Image buttonDeleteImage = new Image("https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/fileclose.png");
final Button cellDeleteButton = new Button("", new ImageView(buttonDeleteImage));
ButtonCell() {
cellDeleteButton.setOnAction(actionEvent -> {
System.out.println("Deleted ID : " +
getItem().getId());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());
});
}
#Override
protected void updateItem(Person t, boolean empty) {
super.updateItem(t, empty);
if (!empty) {
setGraphic(cellDeleteButton);
} else {
setGraphic(null);
}
}
}
}
I have found an easier way, where the user can list anything, and he decides what to show and what to hide. This solution also works if we don't know what data will come to us, but We are sure that will be "ID" column we do not wan't to show in attention:
if (*ColumNameWeWantToHide*.equals(String.valueOf(columnName))) {
col.prefWidthProperty().bind(*TableViewName*.widthProperty().divide(100));
}
It makes us also able to work with ID numbers simply, but also we are abla to watch the ID if we want, but on default we do not see them. That is a good mixture of useability and easy coding.

how to make a table visible with custom rows & column

i have the following code to create a custom Table. but in the output it shows many rows which doesn't contain any values. i would like to display just two rows and 1 columns. is there any solution for this, else Javafx produces this by default. Is there any alternate way to create a table. May be using a GridPaneBuilder
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob"),
new Person("Isabella")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
table.setItems(data);
table.getColumns().addAll(firstNameCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private Person(String fName) {
this.firstName = new SimpleStringProperty(fName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
}
You can set that the columns take as much space as possible by:
myTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
I don't know if there is an easy way to set height of the table according to the amount of rows, but you could set the maxHeight of the table accoring to the amount of rows multiplied by the rowHeight:
myTable.setMaxHeight(countOfRows * rowHeight + headerHeight);
And a more flexible way would be to use JavaFX binding, so when you add or delete a row the height of the table changes.

Can a JavaFX TableColumn's header text be made to bleed over to another column?

I have a TableView with two TableColumns. On the left is a wide column that needs to be able to show entries up to 30 characters. On the right is a narrow column that needs to be able to show just one character. But the title of the right column needs to be a long word (it is required). But I don't have the luxury in terms of space to have a column as fat as the required header text. If I make the right column as wide as its header text, the data in the left column gets truncated. I would like the header text of the column on the right to bleed over into the column on the left (there is no worry of it running into the left column's header text). Is this possible by setting some fx css of my TableView and/or one or both of the TableColumns?
Here is an example where the column header "Long Last Name" bleeds into the column header to the left of it.
bleed.css
.column-header.left-header > .label {
-fx-alignment: baseline-left;
}
.column-header.bleed-header > .label {
-fx-alignment: baseline-right;
-fx-padding: 0 3 0 0;
}
Code snippet which enables the "bleed"
TableColumn<Person, String> lastNameCol = new TableColumn<>();
lastNameCol.setMinWidth(80);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
lastNameCol.getStyleClass().add("bleed-header");
Label headerLabel = new Label("Long Last Name");
headerLabel.setMinWidth(Control.USE_PREF_SIZE);
lastNameCol.setGraphic(headerLabel);
The solution required a bit of trickery:
Use of css to set alignment properties.
Use a label graphic for the header.
Supplying the label graphic gives you direct control of the minimum width (to prevent the default behaviour of the header label being elided when it is too long).
Executable code sample
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewHeaderBleed extends Application {
private TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
table.setPrefHeight(200);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
firstNameCol.getStyleClass().add("left-header");
TableColumn<Person, String> lastNameCol = new TableColumn<>();
lastNameCol.setMinWidth(80);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
lastNameCol.getStyleClass().add("bleed-header");
Label headerLabel = new Label("Long Last Name");
headerLabel.setMinWidth(Control.USE_PREF_SIZE);
lastNameCol.setGraphic(headerLabel);
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(100);
emailCol.setCellValueFactory(
new PropertyValueFactory<>("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
scene.getStylesheets().add(getClass().getResource("bleed.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}

Resources