I'm attempting to make TableView behave like Excel. I am having trouble selected the correct cells.
In Excel, if you select a column(say 5 adjacent cells in the column) and then you click SHIFT + LEFT, excel will automatically select all corresponding left most cell.
e.g.
Say in Excel I have [A1][A2][A3] selects and then I click SHIFT + RIGHT, know the selected cells would be:
[A1][B1][A2][B2][A3][B3]
How can I achieve similar behavior with a TableView.
I've tried doing something like the following with no luck.
// Table view
TableView tv = new TableView();
// Some more code goes here
// Select right most element
tv.getSelectionModel().selectRightCell();
The problem with selectRightCell() or selectLeftCell() function is that it selects just a single cell (to the left or right of the selected cell) not the all left or right cell of the selected cells.
Here is sample code below, you can select by dragging by mouse or by pressing control+arrow keys.
import com.sun.javafx.scene.control.behavior.TableCellBehavior;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
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.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.Arrays;
public class DragSelectionTable 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")
);
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));
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
final Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = new DragSelectionCellFactory();
firstNameCol.setCellFactory(cellFactory);
lastNameCol.setCellFactory(cellFactory);
emailCol.setCellFactory(cellFactory);
table.setEditable(true);
table.setItems(data);
table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol));
table.getSelectionModel().setCellSelectionEnabled(true);
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.addEventHandler(KeyEvent.KEY_RELEASED, new ControlDownSelectionEventHandler());
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 class DragSelectionCellFactory implements Callback<TableColumn<Person, String>, TableCell<Person, String>> {
#Override
public TableCell<Person, String> call(final TableColumn<Person, String> col) {
return new DragSelectionCell();
}
}
public class DragSelectionCell extends TableCell<Person, String> {
public DragSelectionCell() {
setOnDragDetected(new DragDetectedEventHandler(this));
setOnMouseDragEntered(new DragEnteredEventHandler(this));
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item);
}
}
}
public class DragDetectedEventHandler implements EventHandler<MouseEvent> {
TableCell<Person, String> tableCell;
public DragDetectedEventHandler(TableCell<Person, String> tableCell) {
this.tableCell = tableCell;
}
#Override
public void handle(final MouseEvent event) {
tableCell.startFullDrag();
}
}
public class DragEnteredEventHandler implements EventHandler<MouseDragEvent> {
TableCell<Person, String> tableCell;
public DragEnteredEventHandler(TableCell<Person, String> tableCell) {
this.tableCell = tableCell;
}
#Override
public void handle(final MouseDragEvent event) {
performSelection(tableCell.getTableView(), tableCell.getTableColumn(), tableCell.getIndex());
}
}
public class ControlDownSelectionEventHandler implements EventHandler<KeyEvent> {
#Override
public void handle(final KeyEvent event) {
KeyCode code = event.getCode();
if (event.isShiftDown() && (KeyCode.UP.equals(code) || KeyCode.DOWN.equals(code) || KeyCode.LEFT.equals(code) || KeyCode.RIGHT.equals(code))) {
int index = table.getFocusModel().getFocusedCell().getRow();
TableColumn column = table.getFocusModel().getFocusedCell().getTableColumn();
performSelection(table, column, index);
}
}
}
protected void performSelection(TableView<Person> table, TableColumn<Person, String> column, int index) {
final TablePositionBase anchor = TableCellBehavior.getAnchor(table, table.getFocusModel().getFocusedCell());
int columnIndex = table.getVisibleLeafIndex(column);
int minRowIndex = Math.min(anchor.getRow(), index);
int maxRowIndex = Math.max(anchor.getRow(), index);
TableColumnBase minColumn = anchor.getColumn() < columnIndex ? anchor.getTableColumn() : column;
TableColumnBase maxColumn = anchor.getColumn() >= columnIndex ? anchor.getTableColumn() : column;
table.getSelectionModel().clearSelection();
final int minColumnIndex = table.getVisibleLeafIndex((TableColumn) minColumn);
final int maxColumnIndex = table.getVisibleLeafIndex((TableColumn) maxColumn);
for (int _row = minRowIndex; _row <= maxRowIndex; _row++) {
for (int _col = minColumnIndex; _col <= maxColumnIndex; _col++) {
table.getSelectionModel().select(_row, table.getVisibleLeafColumn(_col));
}
}
table.getFocusModel().focus(index, column);
}
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);
}
}
}
Related
I am working on a TableView (FXML) where I want to have all the rows accompanied with a delete button at the last column.
Here's a video that shows what I mean: YouTube Delete Button in TableView
Here's what I have in my main controller class:
public Button del() {
Button del = new Button();
del.setText("X");
del.setPrefWidth(30);
del.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
int i = index.get();
if(i > -1) {
goals.remove(i);
list.getSelectionModel().clearSelection();
}
}
});
return del;
}
private SimpleIntegerProperty index = new SimpleIntegerProperty();
#Override
public void initialize(URL location, ResourceBundle resources){
//DateFormat df = new SimpleDateFormat("dd MMM yyyy");
sdate.setValue(LocalDate.now());
edate.setValue(LocalDate.now());
seq.setCellValueFactory(new PropertyValueFactory<Goals, Integer>("id"));
gol.setCellValueFactory(new PropertyValueFactory<Goals, String>("goal"));
sdt.setCellValueFactory(new PropertyValueFactory<Goals, Date>("sdte"));
edt.setCellValueFactory(new PropertyValueFactory<Goals, Date>("edte"));
prog.setCellValueFactory(new PropertyValueFactory<Goals, Integer>("pb"));
del.setCellValueFactory(new PropertyValueFactory<Goals, Button>("x"));
list.setItems(goals);
list.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observable,
Object oldValue, Object newValue) {
index.set(goals.indexOf(newValue));
System.out.println("Index is: "+goals.indexOf(newValue));
}
});
}
Each time I launch the application, I will try to click the delete button from random rows but it always delete the first row. I guess the addListener method I use for list is not properly implemented and indexOf(newValue) is always 0 at every initialisation.
However, it will work if I click a row first and then click the delete button. But this is not what I want. I want users to be able to delete any row if they press the delete button without selecting the row.
Appreciate your help guys!
You need a custom cell factory defined for the column containing the delete button.
TableColumn<Person, Person> unfriendCol = new TableColumn<>("Anti-social");
unfriendCol.setCellValueFactory(
param -> new ReadOnlyObjectWrapper<>(param.getValue())
);
unfriendCol.setCellFactory(param -> new TableCell<Person, Person>() {
private final Button deleteButton = new Button("Unfriend");
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person == null) {
setGraphic(null);
return;
}
setGraphic(deleteButton);
deleteButton.setOnAction(
event -> getTableView().getItems().remove(person)
);
}
});
Here is a sample app. It doesn't use FXML, but you could adapt it to work with FXML very easily. Just click on an "Unfriend" button in the "Anti-social" column to delete a friend. Do it a lot and you will soon run out of friends.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class GestureEvents extends Application {
private TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final Label label = new Label("Friends");
label.setFont(new Font("Arial", 20));
final Label actionTaken = new Label();
TableColumn<Person, Person> unfriendCol = new TableColumn<>("Anti-social");
unfriendCol.setMinWidth(40);
unfriendCol.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()));
unfriendCol.setCellFactory(param -> new TableCell<Person, Person>() {
private final Button deleteButton = new Button("Unfriend");
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person == null) {
setGraphic(null);
return;
}
setGraphic(deleteButton);
deleteButton.setOnAction(event -> data.remove(person));
}
});
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
table.setItems(data);
table.getColumns().addAll(unfriendCol, firstNameCol, lastNameCol);
table.setPrefHeight(250);
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();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private Person(String fName, String lName) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
}
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);
}
}
}
I am going through the Oracle tutorial with tables, and have implemented my own tabPane into this code. What I want is for the table to be a global table, so that when I go onto tab 2, it is the exact same as it is on Tab 1, just only that i can edit certain columns in tab 2 compared to editing it all in tab 1.
Is there anyway this can be done instead of basically duplicating code and creating a second table, which i believe is tedious as I don't want two tables, I want one global table.
public class TableSample 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"));
public static void main(String[] args) {
launch(args);
}
private TableView table = new TableView();
public void start(Stage stage) {
Scene scene = new Scene(new Group());
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab("Tab 2");
tabPane.getTabs().add(tab2);
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
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.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.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 VBox vbox = new VBox();
vbox.getChildren().addAll(tabPane, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
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);
}
}
}
Your code structure is already setup correctly. You just need to add Nodes to the different Tabs
Key Code: You need to add a root node to each Tab. Each root node will hold the other nodes that give each Tab it looks.
Tab tab1 = new Tab("Tab 1");
Label tab1Label = new Label("Use tab1Root to add nodes for tab1!");
StackPane tab1Root = new StackPane(tab1Label);
tab1Root.setPrefSize(500, 500);
tab1.setContent(tab1Root);
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab("Tab 2");
Button tab2Button = new Button("Press Me!");
tab2Button.setOnAction((event) -> {
System.out.println("Use tab2Root to add nodes for tab2!");
});
StackPane tab2Root = new StackPane(tab2Button);
tab2Root.setPrefSize(500, 500);
tab2.setContent(tab2Root);
tabPane.getTabs().add(tab2);
Full Code
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TreeTableColumn.CellEditEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestingGround 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"));
public static void main(String[] args)
{
launch(args);
}
public void start(Stage stage)
{
Scene scene = new Scene(new Group());
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
Label tab1Label = new Label("Use tab1Root to add nodes for tab1!");
StackPane tab1Root = new StackPane(tab1Label);
tab1Root.setPrefSize(500, 500);
tab1.setContent(tab1Root);
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab("Tab 2");
Button tab2Button = new Button("Press Me!");
tab2Button.setOnAction((event) -> {
System.out.println("Use tab2Root to add nodes for tab2!");
});
StackPane tab2Root = new StackPane(tab2Button);
tab2Root.setPrefSize(500, 500);
tab2.setContent(tab2Root);
tabPane.getTabs().add(tab2);
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
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.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.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 VBox vbox = new VBox();
vbox.getChildren().addAll(tabPane, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
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);
}
}
}
I have the following tableColumn
TableColumn<TradePurchaseOrderManifest, Double> netweightCol = createColumn("netWeight", "Net Wgt",
Double.class);
and the createColumn method
public static <T> TableColumn<TradePurchaseOrderManifest, T> createColumn(String name, String columHeading,
Class<T> type) {
TableColumn<TradePurchaseOrderManifest, T> column = new TableColumn<>(columHeading);
column.setCellValueFactory(new PropertyValueFactory<>(name));
column.setResizable(true);
return column;
}
This table have other columns as well which are of types ComboBoxTableCell etc. I wish to have a double click handler on this TextFieldTableCell AND only on this column. What I am able to achieve by now is to have a doubleClick handler on the tableview(row).
When I click on this cell, it converts to a TextFieldTableCell and then does not respond to double-clicks even if I am checking if it is an instance of TextFieldTableCell
tableView.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
if (event.getTarget() instanceof TableCell<?,?>) {
System.out.println("dblCLick tableCell");
} else if (event.getTarget() instanceof TextFieldTableCell<?,?>) {
System.out.println("dblCLick textfield");
}
}
}
});
Any suggestion on how to apply double click handler ONLY on this column and when it is a TextFieldTableCell.
This is a workaround I use. I use ContextMenus to handle similar situations.
import java.util.Arrays;
import java.util.Optional;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.TextInputDialog;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
/**
* Sedrick (SedJ601)
* Uses code from https://gist.github.com/james-d/7758918, https://code.makery.ch/blog/javafx-dialogs-official/ and https://stackoverflow.com/questions/21009377/context-menu-on-a-row-of-tableview
*/
public class App 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")
);
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<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<>("email"));
table.setItems(data);
table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol));
table.setRowFactory((TableView<Person> tableView) -> {
final TableRow<Person> row = new TableRow<>();
final ContextMenu contextMenu = new ContextMenu();
final MenuItem removeMenuItem = new MenuItem("Change last name");
removeMenuItem.setOnAction((ActionEvent event) -> {
Person tempPerson = table.getSelectionModel().getSelectedItem();
int rowIndex = table.getSelectionModel().getSelectedIndex();
TextInputDialog dialog = new TextInputDialog(tempPerson.getLastName());
dialog.setTitle("Text Input Dialog");
dialog.setHeaderText("Look, a Text Input Dialog");
dialog.setContentText("Please enter a last name:");
// Traditional way to get the response value.
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
tempPerson.setLastName(result.get());
tableView.getItems().set(rowIndex, tempPerson);
}
});
contextMenu.getItems().add(removeMenuItem);
// Set context menu on row, but use a binding to make it only show for non-empty rows:
row.contextMenuProperty().bind(
Bindings.when(row.emptyProperty())
.then((ContextMenu)null)
.otherwise(contextMenu)
);
return row ;
});
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 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);
}
}
}
I am trying to wrap the text in the ComboBox but I cannot really manage it.
I need the wrapped text in the editor part, not in the dropdown part of the comboBox. I see that the editor is a TextField, and its text cannot really be wrapped or?
Is there any trick or solution to have a nicely wrapped text so if I have a really long text I can see it every time?
So instead of displaying the ... I want the whole text to be displayed.
Code part I don't know if I should add since it is really simple table cell, and a ComboBox set as Graphics, but if it helps I will edit the question.
Note: Setting the colum wider so the text can fit, is not a solution!
Here is a screenshot:
I am bit confused about the question. You want to show the wrapped text while editing or while in non edit mode.? Because if you are in editing mode you cannot see ... in text box.
So I assume you are asking for showing the wrapped text after selecting from popup list and the combo is not in edit mode.We can fix that using buttonCell.
If this is what you are not asking for, then a Minimal, Complete, and Verifiable example can help me to look into the actual issue.
Please refer to the below code of how to show the wrapped text.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewComboBoxCell 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"));
public static void main(String[] args) {
launch(args);
}
private final ObservableList<String> comboList = FXCollections.observableArrayList("First big sentence with very long text to check the text wrap",
"Second big sentence with very long text to check the text wrap",
"Third big sentence with very long text to check the text wrap",
"Fourth big sentence with very long text to check the text wrap");
#Override
public void start(Stage stage) {
for (int i = 0; i < 50; i++) {
if(i%5==0){
data.add(new Person("Name " + i, "Last " + i, "Mail " + i, comboList.get(0)));
}else {
data.add(new Person("Name " + i, "Last " + i, "Mail " + i));
}
}
Scene scene = new Scene(new StackPane());
stage.setTitle("Table View Sample");
stage.setWidth(650);
stage.setHeight(500);
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<Person, String>(
"firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol
.setCellValueFactory(new PropertyValueFactory<Person, String>(
"lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
"email"));
TableColumn<Person, String> comboCol = new TableColumn<>("Combo");
comboCol.setMinWidth(200);
comboCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
"combo"));
comboCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> param) {
return new TableCell<Person, String>() {
private ComboBox<String> combo;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
getCombo().getSelectionModel().clearSelection();
if (!empty) {
getCombo().setValue(item);
setGraphic(getCombo());
} else {
setGraphic(null);
}
}
private ComboBox<String> getCombo() {
if (combo == null) {
combo = new ComboBox<>();
combo.setItems(comboList);
combo.getSelectionModel().selectedItemProperty().addListener((obs, old, newVal) -> {
((Person) getTableRow().getItem()).setCombo(newVal);
});
combo.setButtonCell(new ListCell<String>() {
private Text textLbl;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(null);
if (!empty) {
getTextLbl().setText(item);
setGraphic(getTextLbl());
}
}
private Text getTextLbl(){
if(textLbl ==null){
textLbl = new Text();
textLbl.wrappingWidthProperty().bind(this.widthProperty().subtract(10));
}
return textLbl;
}
});
}
return combo;
}
};
}
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, comboCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((StackPane) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleStringProperty combo;
private Person(String fName, String lName, String email) {
this(fName,lName,email,null);
}
private Person(String fName, String lName, String email, String comboStr) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.combo = new SimpleStringProperty(comboStr);
}
public String getFirstName() {
return firstName.get();
}
public SimpleStringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public SimpleStringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getEmail() {
return email.get();
}
public SimpleStringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
public String getCombo() {
return combo.get();
}
public SimpleStringProperty comboProperty() {
return combo;
}
public void setCombo(String combo) {
this.combo.set(combo);
}
}
}
And the output is as below:
I have this simple example of a table.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
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.PropertyValueFactory;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application
{
private TableView<Person> table = new TableView<>();
private final ObservableList<Person> data
= FXCollections.observableArrayList(
new Person("Processor", "72"),
new Person("RAM", "78"),
new Person("HDD Free Space", "890"),
new Person("Lan Adapter NIC 1", "36"),
new Person("Lan Adapter NIC 2", "67"));
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(850);
//stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(false);
Callback<TableColumn, TableCell> cellFactory
= new Callback<TableColumn, TableCell>()
{
#Override
public TableCell call(TableColumn p)
{
return new EditingCell();
}
};
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn firstNameCol = new TableColumn("Credentials");
//firstNameCol.setPrefWidth(300);
firstNameCol.setMinWidth(50);
//firstNameCol.prefWidthProperty().bind(table.widthProperty().divide(2)); // w * 1/2
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(cellFactory);
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("Value");
//lastNameCol.setPrefWidth(300);
lastNameCol.setMinWidth(50);
//lastNameCol.prefWidthProperty().bind(table.widthProperty().divide(2)); // w * 1/2
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(cellFactory);
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());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol);
final VBox vbox = new VBox();
VBox.setVgrow(table, Priority.ALWAYS);
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 final SimpleStringProperty lastName;
private Person(String fName, String lName)
{
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
}
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);
}
}
class EditingCell extends TableCell<Person, String>
{
private TextField textField;
public EditingCell()
{
}
#Override
public void startEdit()
{
if (!isEmpty())
{
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit()
{
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty)
{
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.focusedProperty().addListener(new ChangeListener<Boolean>()
{
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2)
{
if (!arg2)
{
commitEdit(textField.getText());
}
}
});
}
private String getString()
{
return getItem() == null ? "" : getItem().toString();
}
}
}
I want resize the table and fit the main stage when I resize the window. How I can do this?
P.S I tested to add table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); but nothing is changed.
use vbox as root for scene and set vgrow constraint for table to always.
Sample Code :
Scene scene = new Scene(vbox);
vbox.setVgrow(table, Priority.ALWAYS);
If you are using Scenebuilder, then you can easily anchor the anchorpane (where your tableview is residing) to the sides. Or you can set it programatically in the controller.
Select the sides whichever direction you want the table to grow.
AnchorPane.setTopAnchor(<anchorpane>,0.0);
AnchorPane.setBottomAnchor(<anchorpane>,0.0);
AnchorPane.setLeftAnchor(<anchorpane>,0.0);
AnchorPane.setRightAnchor(<anchorpane>,0.0);
ColumnResizePolicy will only resize the tablecolumn according to your tableview size.