I am trying to use a choicebox to show specific data in a tableview.
Lets say that i have 4 columns, "name" "Price" "Category" "in Stock", in my tableview. My tableview is getting its data from an .txt document, and it is possible to add data through textfields in my GUI.
My choicebox is holding value "Category 1" "Category 2" "Category 3", and my tableview is holding data in 6 rows, where 3 rows has category 1, and 3 rows has category 2.
What i want to do is, if i choose category 1 in my choicebox my tableview should only show data which is of category 1, and so on.
I am totally lost.
My Code.
private TableView<DTOArt> table = new TableView<DTOArt>();
private ObservableList<DTOArt> data = FXCollections.observableArrayList();
private ObservableList filCat = FXCollections.observableArrayList("Category 1","Category 2","Category 3");
public VBox getTableView(){
VBox tabelView = new VBox();
tabelView.setSpacing(5);
tabelView.setPadding(new Insets(10,10,10,10));
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setEditable(true);
TableColumn nameCol = new TableColumn("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<DTOArt, String>("name"));
TableColumn priceCol = new TableColumn("Price");
priceCol.setCellValueFactory(new PropertyValueFactory<DTOArt, String>("price"));
TableColumn categoryCol = new TableColumn("Category");
categoryCol.setCellValueFactory(new PropertyValueFactory<DTOArt, String>("category"));
TableColumn inStockCol = new TableColumn("in Stock");
inStockCol.setCellValueFactory(new PropertyValueFactory<DTOArt, String>("inStock"));
table.setItems(data);
table.getColumns().addAll(nameCol,priceCol,categoryCol,inStockCol);
tabelView.getChildren().add(table);
return tabelView;
}
My choiceBox.
ChoiceBox categoryCB = new ChoiceBox();
categoryCB.setItems(filCat);
categoryCB.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>(){
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue){
}
});
Wrap the list of all items in a FilteredList. This allows you to specify a Predicate to filter the items by.
private final ObservableList<DTOArt> data = FXCollections.observableArrayList();
private final FilteredList<DTOArt> filteredData = new FilteredList<>(data);
ChoiceBox<String> categoryCB = new ChoiceBox<>();
categoryCB.setItems(filCat);
categoryCB.valueProperty().addListener(new ChangeListener<String>(){
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue){
filteredData.setPredicate(newValue == null ? null : (DTOArt e) -> newValue.equals(e.getCategory()));
}
});
table.setItems(filteredData);
Furthermore if you want to allow dynamically changing the category, you should use an extractor for the ObservableList
private final ObservableList<DTOArt> data = FXCollections.observableArrayList((DTOArt e) -> new Observable[] { e.categoryProperty() });
Related
I have an observable List saving Author objects. The gui is able to add an author to my database. The observable list contains all the objects of the database. I want my table to update automatically if I add an Author in the databse.
I have already tried to refresh the list with table.refresh(). I am also thinking about using a change listener for the observable list.
Here the Code for creating the table. authorList is an observable list. I think I don't quite understand how to use an observable list. My suggestion was that by using "table.setItem(authorList)", my table automatically updates its entries if something is changed in the list. Obviously this is not the case.
private void createAuthorsTablePane() {
// TODO: Layout ändern
GridPane authorGridPane = new GridPane();
// create table
TableView<Author> table = new TableView<>();
// Create columns with title
TableColumn<Author, String> idColumn = new TableColumn<>("ID");
TableColumn<Author, String> nameColumn = new TableColumn<>("Name");
TableColumn<Author, String> emailColumn = new TableColumn<>("Email");
TableColumn<Author, String> publicationsColumn = new TableColumn<>("Publications");
// Add columns to table node
table.getColumns().add(idColumn);
table.getColumns().add(nameColumn);
table.getColumns().add(emailColumn);
table.getColumns().add(publicationsColumn);
// Bindings
PropertyValueFactory<Author, String> idColumnFactory = new PropertyValueFactory<>("id");
PropertyValueFactory<Author, String> nameColumnFactory = new PropertyValueFactory<>("name");
PropertyValueFactory<Author, String> emailColumnFactory = new PropertyValueFactory<>("email");
PropertyValueFactory<Author, String> publicationsColumnFactory = new PropertyValueFactory<>("publications");
idColumn.setCellValueFactory(idColumnFactory);
nameColumn.setCellValueFactory(nameColumnFactory);
emailColumn.setCellValueFactory(emailColumnFactory);
publicationsColumn.setCellValueFactory(publicationsColumnFactory);
table.setItems(authorList);
// Create Buttons
createAuthorButton = new Button("Create author");
createAuthorButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
mainController.createAuthorController();
}
});
deleteAuthorButton = new Button("Delete selected author");
// Add Nodes to Pane
authorGridPane.add(new Label("Authors"), 0, 0);
authorGridPane.add(table, 0, 1);
authorGridPane.add(deleteAuthorButton, 0, 2);
authorGridPane.add(createAuthorButton, 1, 2);
authorPane = authorGridPane;
}
Here is the class, where I create my authorList. I am registering the list in the class where I create the table by using a controller.
public class ObservableModel {
private ObservableList<Publication> publicationList;
private ObservableList<Author> authorList;
public ObservableModel(DatabaseService database) {
publicationList = FXCollections.observableList(database.getPublications());
authorList = FXCollections.observableList(database.getAuthors());
}
public ObservableList<Publication> getPublicationList() {
return publicationList;
}
public ObservableList<Author> getAuthorList() {
return authorList;
}
}
TableView<Author> table = new TableView<>();
private ObservableList<Author> authorList = FXCollections.observableArrayList();
private Property<ObservableList<Author>> authorListProperty = new SimpleObjectProperty<>(authorList);
table.itemsProperty().bind(authorListProperty); // The Binding
Every time you change authorList the tableview will be updated as well
idcolumnFactory.setCellValueFactory(cellData -> cellData.getValue().getIdProperty());
or if you declare simple bean
idcolumnFactory.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getId()));
don't forget
table.setItems(your_observale_list);
I have a restaurant menu with dishes and categories implemented as a treeTableView in javaFX.
I want to make the the category rows appear different with CSS but I just can't find a way to filter them out and apply a class. Moving the images a bit to the left would also be nice. I also had no luck using a rowFactory. I've seen this answer but I don't understand it.
This is how I fill the table. I've left out the column- and cellfactories.
private void fillDishes(List<Dish> dishes){
root.getChildren().clear();
Map<String,TreeItem<Dish>> categoryMap = new HashMap<>();
for (Category c: allCats) {
TreeItem<Dish> newCat = new TreeItem<>(new Dish(c.getName(),null,null,null));
//newCat.getGraphic().getStyleClass().add("category");
categoryMap.put(c.getName(),newCat);
root.getChildren().add(newCat);
}
for (Dish d: dishes) {
categoryMap.get(d.getCategory()).getChildren().add(new TreeItem<>(d));
}
}
TreeTableView uses the rowFactory to create the TreeTableRows. At some time later it assigns a TreeItem to a TreeTableRow. This may happen again with different TreeItems for the same row. For this reason you need to handle changes those changes which can be done by adding a ChangeHandler to the TreeTableRow.treeItem property. If a new TreeItem is assigned to the row, you can check for top-level nodes by checking the children of the (invisible) root item for the row item.
I prefer the approach that does not require searching the child list though. It's possible to compare the parent of the item with the root.
public static class Item {
private final String value1;
private final String value2;
public Item(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
}
#Override
public void start(Stage primaryStage) {
final TreeItem<Item> root = new TreeItem<>(null);
TreeTableView<Item> ttv = new TreeTableView<>(root);
ttv.setShowRoot(false);
TreeTableColumn<Item, String> column1 = new TreeTableColumn<>();
column1.setCellValueFactory(new TreeItemPropertyValueFactory<>("value1"));
TreeTableColumn<Item, String> column2 = new TreeTableColumn<>();
column2.setCellValueFactory(new TreeItemPropertyValueFactory<>("value2"));
ttv.getColumns().addAll(column1, column2);
final PseudoClass topNode = PseudoClass.getPseudoClass("top-node");
ttv.setRowFactory(t -> {
final TreeTableRow<Item> row = new TreeTableRow<>();
// every time the TreeItem changes, check, if the new item is a
// child of the root and set the pseudoclass accordingly
row.treeItemProperty().addListener((o, oldValue, newValue) -> {
boolean tn = false;
if (newValue != null) {
tn = newValue.getParent() == root;
}
row.pseudoClassStateChanged(topNode, tn);
});
return row;
});
// fill tree structure
TreeItem<Item> c1 = new TreeItem<>(new Item("category 1", null));
c1.getChildren().addAll(
new TreeItem<>(new Item("sub1.1", "foo")),
new TreeItem<>(new Item("sub1.2", "bar")));
TreeItem<Item> c2 = new TreeItem<>(new Item("category 2", null));
c2.getChildren().addAll(
new TreeItem<>(new Item("sub2.1", "answer")),
new TreeItem<>(new Item("sub2.2", "42")));
root.getChildren().addAll(c1, c2);
Scene scene = new Scene(ttv);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
style.css
.tree-table-row-cell:top-node {
-fx-background: orange;
}
Moving the images a bit to the left would also be nice.
Usually you do this from a custom TreeTableCell returned by a TreeTableColumn.cellFactory. Depending on the behavior you want to implement setting fitWidth/fitHeight may be sufficient, but in other cases dynamically modifying those values based on the cell size may be required.
In my JavaFX TableView, I am trying to retrieve TableCells from a selected row to mark them
with custom colors.
Simply changing the colors of the entire row does not work in this case, as I use different color shadings in each cell depending
on the value of each cell
The example below shows two approaches I tried I to solve the problem
1) Use a listener to retrieve cells in the selected row. Printing the row index and content already works
However, I could not find how to retrieve a TableCell from table.getSelectionModel().
2) Try a dirty workaround to add the TableCells to a global data structure in the columnCellFactory.
However, the TableCells do not get added to the tableCells ArrayList for some reason.
To obtain a short example, the imports and the Classes defining the EditingCell (custom TableCell) and CellEditEvent were omitted.
package TableViewColExample;
public class TableViewExample extends Application {
private Callback<TableColumn, TableCell> columnCellFactory ;
final TableView<String[]> table = new TableView<String[]>();
ObservableSet<Integer> selectedRowIndexes = FXCollections.observableSet();
ObservableSet<String> selectedRows = FXCollections.observableSet();
ArrayList<ArrayList<EditingCell>> tableColumns = new ArrayList<ArrayList<EditingCell>>();
#Override
public void start(Stage stage) {
String[][] dat = new String[][]{
{"C1","C2","C3"},{"a","b","c"},{"d","e","f"},{"g","i","h"}};
ObservableList<String []> data = FXCollections.observableArrayList();
data.addAll(Arrays.asList(dat));
data.remove(0);
table.setItems(data);
for (int i = 0; i < dat[0].length; i++) {
TableColumn tc = new TableColumn(dat[0][i]);
final int colNo = i;
tc.setCellValueFactory(new Callback<CellDataFeatures<String[], String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<String[], String> p) {
return new SimpleStringProperty((p.getValue()[colNo]));
}
});
ArrayList<EditingCell> tableCells = new ArrayList<EditingCell>();
columnCellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
EditingCell tcell = new EditingCell();
//For some reason, the EditingCell is never added to the list
tableCells.add(tcell);
return tcell;
}
};
tc.setCellFactory(columnCellFactory);
tableColumns.add(tableCells);
//The printed value here is 0, which means that the Factory does not add the Editing Cell to the List
System.out.println(" Column rows "+tableCells.size());
table.getColumns().add(tc);
}
//Output: TableColumns 3, TableRows 0
System.out.println("TableColumns "+ tableColumns.size() + " Table rows "+tableColumns.get(0).size());
table.setItems(data);
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> change) -> {
selectedRows.clear();
table.getSelectionModel().getSelectedCells().stream().map(TablePosition::getRow).f orEach(row -> {
selectedRowIndexes.add(row);
System.out.println(selectedRowIndexes.toString());
});
table.getSelectionModel().getSelectedItems().forEach(row -> {
selectedRows.add(Arrays.toString(row));
System.out.println(selectedRows.toString());
});
});
stage.setScene(new Scene(table));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
UPDATE: This qustion moved to TableColumn should only show one specific value of a complex data type because it's more specific
I want to populate a table with a complex data type Person containing name, id and a List<Person>. At the moment I get a table with the correct name, id and a third column with the whole information of other Persons but it should only show the name of the Personss.
Is there any way that my third column shows only the Person.getName() values?
Keywords, solutions welcome! Thank you very much!
Edit: Code
Person.class
public class Person {
String id;
String name;
List<Person> friends;
public String getId() {...}
public String getName() {....}
public List<Person> getFriends {...}
}
TableView.class
public TableView<Person> createTable() {
TableColumn<Person, String> firstCol = new TableColumn<>("ID");
TableColumn<Person, String> secondCol = new TableColumn<>("Name");
TableColumn<Person, List<Person> thirdCol = new TableColumn<>("Friends");
PropertyValueFactory<Person, String> firstColFactory = new PropertyValueFactory<>(
"id");
PropertyValueFactory<Person, String> secondColFactory = new PropertyValueFactory<>(
"name");
PropertyValueFactory<Person, List<Person>> thirdColFactory = new PropertyValueFactory<>(
"friends");
firstCol.setCellValueFactory(firstColFactory);
secondCol.setCellValueFactory(secondColFactory);
thirdCol.setCellValueFactory(thirdColFactory);
myTableView.getColumns().addAll(firstCol, secondCol, thirdCol);
}
So if I fill the table id and name-colums contain the correct name and the third column (with List<Person>) shows [Person [id1, John, ...]...]].
And now I want to ask if theres a possibility that the third column only displays the id or name without manipulating the data?
As an alternative to Uluk's solution, consider setting the cell factory on the column, instead of the cell value factory. The choice between these depends on how you regard the relationship between the column and the model (Person). If you consider the list of names to be the data displayed by the column, then Uluk's solution is the way to go. If you consider the list of Person objects to be the data, which are displayed by their names, then this will be the more intuitive option:
TableColumn<Person, List<Person> thirdCol = new TableColumn<>("Friends");
PropertyValueFactory<Person, List<Person>> thirdColFactory = new PropertyValueFactory<>("friends");
thirdCol.setCellValueFactory(thirdColFactory);
thirdCol.setCellFactory(col -> new TableCell<Person, List<Person>>() {
#Override
public void updateItem(List<Person> friends, boolean empty) {
super.updateItem(friends, empty);
if (empty) {
setText(null);
} else {
setText(friends.stream().map(Person::getName)
.collect(Collectors.joining(", ")));
}
}
});
This makes it easy to change the way the list is displayed in that column, e.g.:
thirdCol.setCellFactory( col -> {
ListView<Person> listView = new ListView<>();
listView.setCellFactory(lv -> new ListCell<Person>() {
#Override
public void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty) {
setText(null);
} else {
setText(person.getName());
}
}
});
return new TableCell<Person, List<Person>>() {
#Override
public void updateItem(List<Person> friends, boolean empty) {
super.updateItem(friends, empty);
if (empty) {
setGraphic(null);
} else {
listView.getItems().setAll(friends);
setGraphic(listView);
}
}
};
});
You may define cell value factory with the long expanded version where you control which fields to show:
thirdColFactory.setCellValueFactory(
( TableColumn.CellDataFeatures<Person, String> p ) ->
{
List<Person> friends = p.getValue().getFriends();
String val = friends
.stream()
.map( item -> item.getName() )
.reduce( "", ( acc, item ) -> acc + ", " + item );
return new ReadOnlyStringWrapper( val );
});
Let me first explain what I did.
I am having list of tables in the drop down
when someone select any one it will populate all the data to tableview with respect to column name
I am able to get the columns from the table and put it in the tableview but I am not able to bind the data with associated columns.
My code here is to look
this.tableName = clsComboData.getValue();
System.out.println(tableName); System.out.println("Button Pressed");
List<StaticColumnConfig> allColumns = null;
for(StaticDataTable dataTable : dropdown) {
if(dataTable.getTableName() != null && dataTable.getTableName().equalsIgnoreCase(tableName)) {
System.out.println(dataTable.getColumnConfig());
allColumns = dataTable.getColumnConfig();
}
}
switch (tableName) {
case "IOSwapCounterparties": SimpleDateFormat date = new SimpleDateFormat("2015-04-15");
System.out.println("Into the Switch");
data = FXCollections.observableArrayList(
new IOSwapCounterparties(1,"a","b","c","d","e",date),
new IOSwapCounterparties(1,"aa","bb","cv","dd","es",date),
new IOSwapCounterparties(1,"ad","bd","cd","dc","eb",date),
new IOSwapCounterparties(1,"aw","bw","cr","dt","ey",date),
new IOSwapCounterparties(1,"ag","bt","cy","du","ep",date)
);
break;
}
//System.out.println("Its in ELSE");
//"Invited" column
TableColumn checkboxCol = new TableColumn<Person, Boolean>();
checkboxCol.setText("");
checkboxCol.setMinWidth(50);
checkboxCol.setCellValueFactory(new PropertyValueFactory("checkbox"));
// Create checkboxes
checkboxCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> p) {
CheckBoxTableCell<Person, Boolean> checkBox = new CheckBoxTableCell();
return checkBox;
}
});
//Set cell factory for cells that allow editing
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
for(StaticColumnConfig column : allColumns){
TableColumn oneColumn = new TableColumn(column.getColumnName());
oneColumn.setCellFactory(cellFactory);
}
// Clear the tableview for next table
tblViewer.getColumns().clear();
// Push the data to the tableview
tblViewer.setItems(data);
tblViewer.setEditable(true);
tblViewer.getColumns().addAll(checkboxCol);
for(StaticColumnConfig column : allColumns){
System.out.println(column.getColumnName());
TableColumn oneColumn = new TableColumn(column.getColumnName());
tblViewer.getColumns().addAll(oneColumn);
}
// Add the columns
tblViewer.getSelectionModel().setCellSelectionEnabled(true);
tblViewer.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
There is been uppercase - lowercase mistake with my getter and setter method.