I'm attempting to do something related to stackoverflow-17693136. The "first name" table will serve as the frozen columns. I am using a single SortedList in both tables and I have bound the sorted data to the table on the right.
So the data in the first name table is sorted when either column in the right table is sorted. But, I would like to also be able to sort the columns in the right table by clicking on the header in the first name table. I'm currently at a loss to find a good way to bind the comparator for both tables to the sorted data. The commented out code just under 2. Bind the SortedList..., you can see in the TableSort.java code shows what I wish I could do. I currently have set the firstNameColumn to not be sortable (line 45).
This code is similar to an example from Marko Jakob.
TableSort2.java:
package tablesort2;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TableSort2 extends Application
{
private TableView<Person> personTable;
private TableView<Person> firstNameTable;
private TableColumn<Person, String> firstNameColumn; // In firstNameTable
private TableColumn<Person, String> lastNameColumn; // In personTable
private TableColumn<Person, Number> idValueColumn; // In personTable
private ObservableList<Person> masterData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage)
{
// Just add some sample data in the initialization
masterData.add(new Person("Hans", "Muster", 3210));
masterData.add(new Person("Ruth", "Mueller", 2625));
masterData.add(new Person("Heinz", "Kurz", 1234));
masterData.add(new Person("Cornelia", "Meier", 9999));
masterData.add(new Person("Werner", "Meyer", 5623));
masterData.add(new Person("Lydia", "Kunz", 7834));
masterData.add(new Person("Anna", "Best", 2942));
masterData.add(new Person("Stefan", "Meier", 9423));
masterData.add(new Person("Martin", "Mueller", 9122));
// 0. Initialize the columns
firstNameColumn = new TableColumn("First Name");
firstNameColumn.setSortable(false);
lastNameColumn = new TableColumn("Last Name");
idValueColumn = new TableColumn("Id Number");
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
idValueColumn.setCellValueFactory(cellData -> cellData.getValue().idValueProperty());
idValueColumn.setComparator((Number o1, Number o2) ->
{
return (o1.intValue() < o2.intValue() ? -1 : o1.intValue() == o2.intValue() ? 0 : 1);
});
firstNameTable = new TableView<>();
personTable = new TableView<>();
// 1. Wrap the ObservableList in a SortedList.
SortedList<Person> sortedData = new SortedList<>(masterData);
// 2. Bind the SortedList comparator to the TableView(s) comparator.
//sortedData.comparatorProperty().bind(firstNameTable.comparatorProperty());
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
// 3. Add sorted data to the tables.
firstNameTable.setItems(sortedData);
personTable.setItems(sortedData);
firstNameTable.getColumns().add(firstNameColumn);
personTable.getColumns().addAll(lastNameColumn, idValueColumn);
HBox tableBox = new HBox(5);
tableBox.getChildren().addAll(firstNameTable, personTable);
Scene scene = new Scene(tableBox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Person.java:
package tablesort2;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author dale
*/
public class Person
{
private final StringProperty firstName;
private final StringProperty lastName;
private final IntegerProperty idValue;
public Person(String firstName, String lastName, Integer id)
{
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.idValue = new SimpleIntegerProperty(id);
}
public String getFirstName()
{
return firstName.get();
}
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 Integer getIdValue()
{
return idValue.get();
}
public void setIdValue(Integer id)
{
this.idValue.set(id);
}
public IntegerProperty idValueProperty()
{
return idValue;
}
}
I believe that I have found in stackoverflow-25144215, (in the answer from James_D) something that will work. I can use a Label for each column and then use setGraphic with the Label. This allows me to add an event filter for each column. If a MOUSE_PRESSED event is detected, the sortedData.comparatorProperty() is bound to the table where that column exists. This apparently happens before the actual sort. This is shown in the updated TableSort2.java (below).
This won't work for multi-column sorts across tables - but it doesn't cause an exception for this case. (I also see that this requires me to manually adjust the column width to account for the Label size).
package tablesort2;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TableSort2 extends Application
{
private TableView<Person> personTable;
private TableView<Person> firstNameTable;
private TableColumn<Person, String> firstNameColumn; // In firstNameTable
private Label firstNameLabel;
private TableColumn<Person, String> lastNameColumn; // In personTable
private Label lastNameLabel;
private TableColumn<Person, Number> idValueColumn; // In personTable
private Label idValueLabel;
private ObservableList<Person> masterData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage)
{
// Just add some sample data in the initialization
masterData.add(new Person("Hans", "Muster", 3210));
masterData.add(new Person("Ruth", "Mueller", 2625));
masterData.add(new Person("Heinz", "Kurz", 1234));
masterData.add(new Person("Cornelia", "Meier", 9999));
masterData.add(new Person("Werner", "Meyer", 5623));
masterData.add(new Person("Lydia", "Kunz", 7834));
masterData.add(new Person("Anna", "Best", 2942));
masterData.add(new Person("Stefan", "Meier", 9423));
masterData.add(new Person("Martin", "Mueller", 9122));
// 0. Initialize the columns
firstNameColumn = new TableColumn();
firstNameLabel = new Label("First Name");
firstNameColumn.setGraphic(firstNameLabel);
//firstNameColumn.setSortable(false);
lastNameColumn = new TableColumn();
lastNameLabel = new Label("Last Name");
lastNameColumn.setGraphic(lastNameLabel);
idValueColumn = new TableColumn();
idValueLabel = new Label("Id Number");
idValueColumn.setGraphic(idValueLabel);
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
idValueColumn.setCellValueFactory(cellData -> cellData.getValue().idValueProperty());
idValueColumn.setComparator((Number o1, Number o2) ->
{
return (o1.intValue() < o2.intValue() ? -1 : o1.intValue() == o2.intValue() ? 0 : 1);
});
firstNameTable = new TableView<>();
personTable = new TableView<>();
// 1. Wrap the ObservableList in a SortedList.
SortedList<Person> sortedData = new SortedList<>(masterData);
// 2. Bind the SortedList comparator (initally) to the personTable comparator.
//sortedData.comparatorProperty().bind(firstNameTable.comparatorProperty());
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
// 2a. Create listenters for each column header label - to update the comparator property
firstNameLabel.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler()
{
#Override
public void handle(Event event)
{
sortedData.comparatorProperty().bind(firstNameTable.comparatorProperty());
}
});
lastNameLabel.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler()
{
#Override
public void handle(Event event)
{
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
}
});
idValueLabel.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler()
{
#Override
public void handle(Event event)
{
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
}
});
// 3. Add sorted data to the table.
firstNameTable.setItems(sortedData);
personTable.setItems(sortedData);
firstNameTable.getColumns().add(firstNameColumn);
personTable.getColumns().addAll(lastNameColumn, idValueColumn);
HBox tableBox = new HBox(5);
tableBox.getChildren().addAll(firstNameTable, personTable);
Scene scene = new Scene(tableBox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Related
How do I get the name of the column of a textfield inside a javaFX table?
I need this to check the value of the cells only in the "text2" column. I tried it with textfield.parent() but I didn't get a useful result.Edit: I just removed some unnessary log, which was not helpful for understanding.Now it is more convenient.
Here is my Code:
import java.util.ArrayList;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
/*interface inside_table
{
public String get_column_name
}*/
public class Supermain extends Application {
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory());
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory());
//Add data
final ObservableList<myTextRow> data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
public TextFieldCell() {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.setGraphic(textField);
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
//only if textfield is in the text2 column
if(isNowFocused){last_text=textField.getText(); System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
textField.selectAll();
System.out.println("blur");
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}//update
private boolean isValid(String s){
if(s.length()<7){return true;}
return false;
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
//setter
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
this.text2.set(text);
}
//getter
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public String getText2() {
return text2.get();
}
//properties
public StringProperty textProperty() {
return text;
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
return l.prefWidth(-1);
}
}
There are probably (way) better ways to organize this, but probably the cleanest fix is just to define a boolean validate parameter to the constructor of your cell implementation. (You really don't want the logic to be "if the title of the column is equal to some specific text, then validate". You would be utterly screwed when your boss came in to the office and asked you to internationalize the application, or even just change the title of the column, for example.)
Using an entire inner class just to implement the callback seems completely redundant, but keeping that you would have to pass the parameter through it:
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
private final boolean validate ;
public TextFieldCellFactory(boolean validate) {
this.validate = validate ;
}
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell(validate);
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
public TextFieldCell(boolean validate) {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.setGraphic(textField);
if (validate) {
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
//only if textfield is in the text2 column
if(isNowFocused){last_text=textField.getText(); System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
textField.selectAll();
System.out.println("blur");
}
});
}
}
// ...
}
Then of course you just do
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory(false));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory(true));
(To properly answer your question, you can get the text of the column from within the cell to which it is attached with getTableColumn().getText(), but as I pointed out, actually basing the logic on the value displayed in a column header will make your code completely unmaintainable.)
And I guess for completeness, I should also mention that your TextFieldCellFactory class looks like it is not really serving any purpose. I would remove it entirely and just have the TextFieldCell class, and do
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(c -> new TextFieldCell(false));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(c -> new TextFieldCell(true));
I have a requirement similar to this example.
In the EventHandler callback, how do I determine which row was clicked on?
#Override
public void handle(ActionEvent event) {
// how do I get the row details when reusing context menu and handler code?
}
I am sharing the context menu because I have to add a CheckMenuItem who's state is "global" to the table, i.e. if its selected on any row, I want to show it as checked when I click on any other row in the table.
Use a row factory and one context menu per row, as in the question you linked.
For the "global" CheckMenuItem, create a BooleanProperty and bidirectionally bind the CheckMenuItems' selected properties to it.
SSCCE:
import java.util.function.Function;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableWithContextMenu extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Value", Item::valueProperty));
BooleanProperty globalSelection = new SimpleBooleanProperty();
table.setRowFactory(t -> {
TableRow<Item> row = new TableRow<>();
ContextMenu contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("Do something");
item1.setOnAction(e -> System.out.println("Do something with "+row.getItem().getName()));
MenuItem item2 = new MenuItem("Do something else");
item2.setOnAction(e -> System.out.println("Do something else with "+row.getItem().getName()));
CheckMenuItem item3 = new CheckMenuItem("Global selection");
item3.selectedProperty().bindBidirectional(globalSelection);
contextMenu.getItems().addAll(item1, item2, new SeparatorMenuItem(), item3);
row.emptyProperty().addListener((obs, wasEmpty, isEmpty) -> {
if (isEmpty) {
row.setContextMenu(null);
} else {
row.setContextMenu(contextMenu);
}
});
return row ;
});
IntStream.rangeClosed(1, 25).mapToObj(i -> new Item("Item "+i, i)).forEach(table.getItems()::add);
primaryStage.setScene(new Scene(new BorderPane(table), 800, 600));
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty();
private final StringProperty name = new SimpleStringProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
I followed the tutorial about TableView in Oracle docs, and I want to do the same thing, but instead of showing a TextField to modified items, I want to show a RadioButton.
(I created the TableView with RadionButton on it)
I used the tutorial at https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm (see Editing Data in the Table) and extended it. As a basis for the values, which should be set by radio buttons I assume an Enumeration.
In my example I extended the Person class with the enum Participation (indicating whether or not the people of the list attending an fictive event) ...
public static enum Participation {
YES,
NO,
MAYBE;
public String toString() {
return super.toString().toLowerCase();
};
}
...
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleObjectProperty<Participation> participation;
...
I implemented a RadioButtonCell, which takes an arbitraty EnumSet<T>. So you can use it for every Enumeration and every TableColumn, which should contain RadioButtons.
public static class RadioButtonCell<S,T extends Enum<T>> extends TableCell<S,T>{
private EnumSet<T> enumeration;
public RadioButtonCell(EnumSet<T> enumeration) {
this.enumeration = enumeration;
}
#Override
protected void updateItem(T item, boolean empty)
{
super.updateItem(item, empty);
if (!empty)
{
// gui setup
HBox hb = new HBox(7);
hb.setAlignment(Pos.CENTER);
final ToggleGroup group = new ToggleGroup();
// create a radio button for each 'element' of the enumeration
for (Enum<T> enumElement : enumeration) {
RadioButton radioButton = new RadioButton(enumElement.toString());
radioButton.setUserData(enumElement);
radioButton.setToggleGroup(group);
hb.getChildren().add(radioButton);
if (enumElement.equals(item)) {
radioButton.setSelected(true);
}
}
// issue events on change of the selected radio button
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#SuppressWarnings("unchecked")
#Override
public void changed(ObservableValue<? extends Toggle> observable,
Toggle oldValue, Toggle newValue) {
getTableView().edit(getIndex(), getTableColumn());
RadioButtonCell.this.commitEdit((T) newValue.getUserData());
}
});
setGraphic(hb);
}
}
}
You now have to adjust the CellFactory of the particular TableColumn
participationColumn.setCellFactory((param) -> new RadioButtonCell<Person, Participation>(EnumSet.allOf(Participation.class)));
Finally update the actual value of your data on a commit as usual:
participationColumn.setCellValueFactory(new PropertyValueFactory<Person, Participation>("participation"));
participationColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Person, Participation>>() {
#Override
public void handle(CellEditEvent<Person, Participation> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setParticipation(t.getNewValue());
}
}
);
gkri, thank you for the nice code! This is very useful. I have one additional remark: Of course the new SimpleObjectProperty needs its get & set methods. Without the getter the table will not properly update, especially when sorting or, with TreeTableView, when expanding or collapsing nodes:
public void setParticipation(Participation p){
participation.set(p);
}
public Participation getParticipation(){
return participation.get();
}
So the full code sample:
import java.util.EnumSet;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
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.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
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.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private final TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com",Participation.MAYBE),
new Person("Isabella", "Johnson", "isabella.johnson#example.com",Participation.MAYBE),
new Person("Ethan", "Williams", "ethan.williams#example.com",Participation.MAYBE),
new Person("Emma", "Jones", "emma.jones#example.com",Participation.MAYBE),
new Person("Michael", "Brown", "michael.brown#example.com",Participation.MAYBE));
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(650);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
table.setMinWidth(640);
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"));
TableColumn<Person,Participation> participationColumn = new TableColumn("Participation");
participationColumn.setCellFactory((param) -> new RadioButtonCell<Person, Participation>(EnumSet.allOf(Participation.class)));
participationColumn.setCellValueFactory(new PropertyValueFactory<Person, Participation>("participation"));
participationColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Person, Participation>>() {
#Override
public void handle(CellEditEvent<Person, Participation> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setParticipation(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, participationColumn );
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((ActionEvent e) -> {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText(),
Participation.NO
));
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();
}
public static enum Participation {
YES,
NO,
MAYBE;
public String toString() {
return super.toString().toLowerCase();
};
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleObjectProperty<Participation> participation = new SimpleObjectProperty<Participation>();
private Person(String fName, String lName, String email, Participation p ) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.participation.setValue(p);
}
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 void setParticipation(Participation p){
participation.set(p);
}
public Participation getParticipation(){
return participation.get();
}
}
public static class RadioButtonCell<S,T extends Enum<T>> extends TableCell<S,T>{
private EnumSet<T> enumeration;
public RadioButtonCell(EnumSet<T> enumeration) {
this.enumeration = enumeration;
}
#Override
protected void updateItem(T item, boolean empty)
{
super.updateItem(item, empty);
if (!empty)
{
// gui setup
HBox hb = new HBox(7);
hb.setAlignment(Pos.CENTER);
final ToggleGroup group = new ToggleGroup();
// create a radio button for each 'element' of the enumeration
for (Enum<T> enumElement : enumeration) {
RadioButton radioButton = new RadioButton(enumElement.toString());
radioButton.setUserData(enumElement);
radioButton.setToggleGroup(group);
hb.getChildren().add(radioButton);
if (enumElement.equals(item)) {
radioButton.setSelected(true);
}
}
// issue events on change of the selected radio button
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#SuppressWarnings("unchecked")
#Override
public void changed(ObservableValue<? extends Toggle> observable,
Toggle oldValue, Toggle newValue) {
getTableView().edit(getIndex(), getTableColumn());
RadioButtonCell.this.commitEdit((T) newValue.getUserData());
}
});
setGraphic(hb);
}
}
}
}
Simly do it like in the example 12-8 Editing a Table Cell.
Define a EditingCell like there(at the bottom) but simply replace the TextField with a RadioButton.
If you need further assistance, write a comment under this answer.
Happy Coding,
Kalasch
In nutshell I want to add the row silently at the end of the table without affecting other rows.
This is my controller class code excerpt to add entries to the table. The problem is when I add an item to the sourceTree, instead of adding a row at the end silently it disturbs the complete table. Suppose there were already some entries in the table and the user expands one of the titled pane in the table, now when a new row will be added to the table, complete table will blink and this titled pane will automatically shrink. This blinking led me to conclude that instead of adding an entry whole table is refreshing probably. Please help...
#FXML
public static TableView<NodeInfo> tableView;
#FXML
public static TableColumn<NodeInfo, TitledPane> nodeTree;
#FXML
private TableColumn<NodeInfo, String> name;
#FXML
private TableColumn<NodeInfo, CheckBox> favourite;
#FXML
private TableColumn<NodeInfo, Button> updates;
#FXML
public static ObservableList<NodeInfo> sourceTree = FXCollections.observableArrayList();
tableView.setPlaceholder(new Label("You have no Nodes in the network at the moment!!!"));
nodeTree.setCellValueFactory(new PropertyValueFactory<NodeInfo, TitledPane>("TitledPaneNode"));
nodeTree.prefWidthProperty().bind(tableView.widthProperty().divide(3));
name.setCellValueFactory(new PropertyValueFactory<NodeInfo, String>("name"));
name.prefWidthProperty().bind(tableView.widthProperty().divide(3));
favourite.setCellValueFactory(new PropertyValueFactory<NodeInfo, CheckBox>("favourite"));
favourite.prefWidthProperty().bind(tableView.widthProperty().divide(6));
updates.setCellValueFactory(new PropertyValueFactory<NodeInfo, Button>("NoOfUpdates"));
updates.prefWidthProperty().bind(tableView.widthProperty().divide(6));
tableView.setItems(sourceTree);
The following is a SSCCE code I have simulated your use case. At the result there was no table disturbing, or pane shrinking. Test it yourself and compare it with yours:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TitledPane;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class PaneDemo extends Application {
private TableView<NodeInfo> table = new TableView<NodeInfo>();
private final ObservableList<NodeInfo> data = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setWidth(450);
stage.setHeight(500);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<NodeInfo, String>("firstName"));
TableColumn paneCol = new TableColumn("Pane");
paneCol.setMinWidth(100);
paneCol.setCellValueFactory(new PropertyValueFactory<NodeInfo, TitledPane>("titledPane"));
for (int i = 0; i < 5; i++) {
TitledPane pane = new TitledPane("title " + i, new Text("text " + i));
data.add(new NodeInfo("name " + i, pane));
}
table.setItems(data);
table.getColumns().addAll(firstNameCol, paneCol);
Button btn = new Button("add new item");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
TitledPane pane = new TitledPane("title new", new Text("text new"));
data.add(new NodeInfo("name new", pane));
}
});
final VBox vbox = new VBox(20);
vbox.getChildren().addAll(table, btn);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class NodeInfo {
private final SimpleStringProperty firstName;
private TitledPane titledPane;
private NodeInfo(String fName, TitledPane titledPane) {
this.firstName = new SimpleStringProperty(fName);
this.titledPane = titledPane;
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public TitledPane getTitledPane() {
return titledPane;
}
public void setTitledPane(TitledPane fName) {
titledPane = fName;
}
}
}
How to
Table column select check box will select
I have select check box in table view and unfortunately,I will select table column at time all check box is un-select,click table column check box only select
how to rearrange in my code help me.
package tableviewwithaddcheckbox;
import com.sun.javafx.runtime.VersionInfo;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
*
* #author reegan
*/
public class TableViewWithAddCheckBox extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) {
stage.setTitle("People");
// create a table.
final TableView<Person> table = new TableView<>(
FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")));
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// define the table columns.
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
TableColumn<Person, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
// define a simple boolean cell value for the action column so that the column will only be shown for non-empty rows.
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Person, Boolean> features) {
return new SimpleBooleanProperty(features.getValue() != null);
}
});
// create a cell value factory with an add button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> personBooleanTableColumn) {
return new AddPersonCell(stage, table);
}
});
table.getColumns().setAll(firstNameCol, lastNameCol, actionCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.getSelectionModel().getSelectedIndices().addListener(new ListChangeListener() {
#Override
public void onChanged(ListChangeListener.Change change) {
System.out.println("-------------------------");
System.out.println(change.getList());
System.out.println("------------------------");
}
});
table.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<Person>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Person> change) {
System.out.println(change.getList());
VersionInfo info = new VersionInfo();
System.out.println(info.getRuntimeVersion());
String version = System.getProperty("java.version");
System.out.println(version);
String vendor = System.getProperty("java.vendor.url");
System.out.println(vendor);
System.out.println(version);
}
});
System.out.println(table.getItems());
stage.setScene(new Scene(table));
stage.show();
}
private class AddPersonCell extends TableCell<Person, Boolean> {
final CheckBox addButton = new CheckBox();
final StackPane paddedButton = new StackPane();
AddPersonCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(addButton);
addButton.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (t1) {
table.getSelectionModel().select(getTableRow().getIndex());
System.out.println(getTableColumn());
} else {
table.getSelectionModel().clearSelection(getTableRow().getIndex());
}
}
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
}
}
}
}
PersonClaa.java
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package tableviewwithaddcheckbox;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author reegan
*/
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
public final void setFirstName(String value) { firstNameProperty().set(value); }
public final void setLastName(String value) { lastNameProperty().set(value); }
public String getFirstName() { return firstNameProperty().get(); }
public String getLastName() { return lastNameProperty().get(); }
public StringProperty firstNameProperty() {
if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
return firstName;
}
public StringProperty lastNameProperty() {
if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
return lastName;
}
}