I have problem with adding of column to TableView dynamically. The problem is, that it adds data for one cell into entire row. I think the problem could be in my callback, or in the part where I add data in tableView.
This is my callback method:
Callback<CellDataFeatures<String,String> ,ObservableValue<String>> cb;
cb = new Callback<CellDataFeatures<String,String> ,ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<String, String> param) {
return new SimpleStringProperty(param.getValue());
}
};
and this is where I add data, listOfNewValues is ArrayList which contains new String values:
ObservableList<String> addData = FXCollections.observableArrayList();
addData = aktualTable.getItems();
for(String data : listOfNewValues) {
addData.add(data);
}
aktualTable.setItems(addData);
Related
I'm attempting to bind between a ListView where more than one item can be selected and a ObjectProperty. When an item is selected, I want to filter a TableView column accordingly:
With the two lower filters (Components, Details), I do the binding like this:
ObjectProperty<Predicate<Log>> detailsSearchFilter = new SimpleObjectProperty<>();
TextField detailsSearchField = new TextField();
detailsSearchField.setPromptText("e.g. finished initializing");
detailsSearchFilter.bind(Bindings.createObjectBinding(() ->
log -> log.getDetails().toLowerCase().contains(detailsSearchField.getText().toLowerCase()),
detailsSearchField.textProperty()
));
Then later adding the logical operator method and() in order to be able to combine all filters:
logFilteredList.predicateProperty().bind(Bindings.createObjectBinding(() ->
detailsSearchFilter.get().and(componentSearchFilter.get()).and(sourceFilter.get()),
detailsSearchFilter, componentSearchFilter, sourceFilter
));
For the other two ListView filters, I was thinking to do something like this:
private final static String[] sources = new String[]{"ECS","IISNode","PrismWebServer"};
ListView<String> sourceList = new ListView<>();
ObservableList sourceItems = FXCollections.observableArrayList(sources);
sourceList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
sourceList.getItems().addAll(sources);
ListView<String> selected = new ListView<>();
sourceList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
selected.setItems(sourceList.getSelectionModel().getSelectedItems());
System.out.println(Arrays.toString(selected.getItems().toArray()));
});
So the selected list now includes all values that are currently selected.
How do I bind the sourceFilter with all values of the selected list?
I was thinking of iterating through each value in the list and binding this way:
sourceFilter.bind(Bindings.createObjectBinding(() ->
log -> log.getSource().toLowerCase().contains(selected.getItems().get(i).toLowerCase()),
sourceList.selectionModelProperty()
));
But it seems not very elegant and I'm not sure I'm using the selectionModelProperty in the right way.
The selectionModelProperty only triggers changes when the selectionModel is replaced which usually doesn't happen. The dependency should be to sourceList.getSelectionModel().getSelectedItems().
Furthermore the way I understand it you should query, if the Log's source is among the selected items, not if the source string contains some part of the string at some position in the selected items list.
Also note that using a ListView just for storing the data should be avoided and for large lists it's much more efficient to do the contains checks on a Set instead of a List.
You could use code like this:
ObjectBinding<Predicate<Log>> binding = new ObjectBinding<Predicate<String>>() {
private final Set<String> strings = new HashSet<>();
{
sourceList.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(ListChangeListener.Change<? extends String> c) {
boolean changed = false;
// modify set on selection change
while (c.next()) {
if (c.wasRemoved()) {
changed = true;
c.getRemoved().stream().map(String::toLowerCase).forEach(strings::remove);
}
if (c.wasAdded()) {
changed = true;
c.getAddedSubList().stream().map(String::toLowerCase).forEach(strings::add);
}
}
if (changed) {
invalidate();
}
}
});
}
#Override
protected Predicate<Log> computeValue() {
return log -> strings.contains(log.getSource().toLowerCase());
}
};
sourceFilter.bind(binding);
i have a data model "Rule"
A Rule consists of 1-x String parts saved as a List and an boolean value weather the rule is active or not.
To show this in my UI i want to add a TableView with 2 Columns.
Column 1 should display the Rule Text as a whole, but heavily customized. In the cell i add a textfield for each rule part which then get binded to the StrinProperty (Thats why i need a List of String Properties.
The 2. column should display a checkbox to activate or deactivate the rule (this is no problem an works fine)
Before my rule Model had the boolean isActive flag i used a Listview which had the whole Rule model class as Object. I made my own ListCell implementation and overrode updateItem(Object item, boolean isEmpty) to customize the cell to look like this:
I want the tablecell in column 1 to look exactly how the listcell in my listview looked.
Because ListCell and Tablecell both inherit from IndexedCell i saw no problem in my way of changing the visual of the cell.
My problem is to bind the new datamodel to the table:
private TableView<Rule> tvRules;
this.tvRules = new TableView<Rule>();
this.tvRules.setPrefSize(GuiCore.prefWidth * 0.32, GuiCore.prefHeight * 0.32);
this.tvRules.setEditable(true);
headerBoxLbl = new Label("Active");
headerBox = new CheckBox();
headerBoxLbl.setGraphic(headerBox);
headerBoxLbl.setContentDisplay(ContentDisplay.RIGHT);
headerBox.setOnAction(e -> this.changeAllActiveBoxes());
rulePartsColumn = new TableColumn<Rule, List<SimpleStringProperty>>("Rule");
rulePartsColumn.setCellFactory((callback) -> new RuleTableCell());
rulePartsColumn.setCellValueFactory(cellData -> cellData.getValue().getRulePartsProperty());
rulePartsColumn.setResizable(false);
rulePartsColumn.prefWidthProperty().bind(this.widthProperty().multiply(0.8));
isActiveColumn = new TableColumn<Rule, Boolean>();
isActiveColumn.setCellValueFactory(cellData -> cellData.getValue().getIsActiveProperty());
isActiveColumn.setCellFactory(cellData -> new CheckBoxTableCell<>());
isActiveColumn.setResizable(false);
isActiveColumn.prefWidthProperty().bind(this.widthProperty().multiply(0.2));
isActiveColumn.setStyle( "-fx-alignment: CENTER;");
isActiveColumn.setGraphic(headerBoxLbl);
this.tvRules.getColumns().addAll(rulePartsColumn, isActiveColumn);
As you see i create 2 Columns with the TableDataType Rule, one with Boolean type and one with the List as Data type.
The problem ist that i dont get the binding of the rulePartsColumn to the rule Model to work:
I really dont know how to bind this so in the cell i can work with a List of StringProperties (or SimpleStringProperties).
For reference my Model class Rule:
public class Rule {
private SimpleListProperty<SimpleStringProperty> ruleParts;
private SimpleBooleanProperty isActive;
public Rule() {
this(true, Arrays.asList("", "=", ""));
}
public Rule(final boolean isActive, final List<String> ruleParts) {
this.isActive = new SimpleBooleanProperty(isActive);
this.ruleParts = new SimpleListProperty<SimpleStringProperty>(FXCollections.observableArrayList());
for(int i = 0; i < ruleParts.size(); i++) {
this.ruleParts.add(new SimpleStringProperty(ruleParts.get(i)));
}
}
public SimpleListProperty<SimpleStringProperty> getRulePartsProperty() {
return this.ruleParts;
}
public List<SimpleStringProperty> getRulePartsProperties() {
return (List<SimpleStringProperty>)this.ruleParts;
}
public List<String> getRuleParts() {
List<String> parts = new ArrayList<String>();
for(int i = 0; i < this.ruleParts.size(); i++) {
parts.add(this.ruleParts.get(i).get());
}
return parts;
}
public SimpleBooleanProperty getIsActiveProperty() {
return this.isActive;
}
public boolean isActive() {
return isActive.get();
}
public void setActive(boolean isActive) {
this.isActive.set(isActive);
}
}
Thanks in advance
I have a problem trying to sort specific columns from a cell table, whcih is populated from the DB, using RPC. Basically I'm trying to sort the family name column alphabetically, and it's just not working. Table gets fully populated, but sorting does not work.
Any ideas why ?
Thanks in advance
// Create the family name column.
final TextColumn<ContactInfo> familyNameColumn = new TextColumn<ContactInfo>() {
#Override
public String getValue(ContactInfo object) {
return object.getFamilyName();
}
};
table.setColumnWidth(familyNameColumn, 20, Unit.PCT);
// Make the family name sortable
familyNameColumn.setSortable(true);
// Add the columns
table.addColumn(familyNameColumn, UserMenuConstants.FAMILY_NAME_COLUMN);
table.addColumn(familyAdministratorColumn, UserMenuConstants.FAMILY_ADMINISTRATOR_COLUMN);
table.addColumn(apartmentNuberColumn, UserMenuConstants.FAMILY_APARTMENT_NUMBER_COLUMN);
table.addColumn(emailColumn, UserMenuConstants.EMAIL_ADDRESS_COLUMN);
table.addColumn(phoneNumberColumn, UserMenuConstants.PHONE_NUMBER_COLUMN);
DBGetContactInfoAsync rpcService = (DBGetContactInfoAsync) GWT.create(DBGetContactInfo.class);
ServiceDefTarget target = (ServiceDefTarget) rpcService;
String moduleRelativeURL = GWT.getModuleBaseURL() + "DBGetContactInfoImpl";
target.setServiceEntryPoint(moduleRelativeURL);
rpcService.getContacts(new AsyncCallback<List<ContactInfo>>() {
#Override
public void onSuccess(List<ContactInfo> result) {
table.setRowCount(result.size());
ListDataProvider<ContactInfo> dataProvider = new ListDataProvider<ContactInfo>();
dataProvider.addDataDisplay(table);
List<ContactInfo> list = dataProvider.getList();
for (ContactInfo contactInfo : result) {
list.add(contactInfo);
}
ListHandler<ContactInfo> listHandler = new ListHandler<ContactInfo>(result);
listHandler.setComparator(familyNameColumn, new Comparator<ContactInfo>() {
#Override
public int compare(ContactInfo o1, ContactInfo o2) {
return o1.getFamilyName().compareTo(o2.getFamilyName());
}
});
table.addColumnSortHandler(listHandler);
}
#Override
public void onFailure(Throwable caught) {
...
}
});
You are making two copies of your data: result and list. The list is connected with dataProvider:
List<ContactInfo> list = dataProvider.getList();
and the listListener is connected with result:
ListHandler<ContactInfo> listHandler = new ListHandler<ContactInfo>(result);
so you are displaying list but sorting the result.
Just replace
new ListHandler<ContactInfo>(result);
with
new ListHandler<ContactInfo>(list);
and it works.
EDIT:
You can make it even easier and pass the result to the ListDataProvider constructor:
new ListDataProvider<ContactInfo>(result);
Then, you don't need to copy values to the list and just do
new ListHandler<ContactInfo>(dataProvider.getList());
Move most of the code in your onSuccess method out of there - there is no reason to call it each time a data is loaded. For example, you can/should set a Comparator only once, etc.
Tell your table which column to use for sorting:
table.getColumnSortList().push(familyNameColumn);
When you finish loading new data, tell your table to sort it:
ColumnSortEvent.fire(table, table.getColumnSortList());
I have the following code for creating a JTable
public void tableTeam()
{
rl.readRunners();
String[] sampleHeaders = {"Athlete ID", "Team"};
JTable myTable = new JTable(rl.teamTableData,sampleHeaders);
myTable.setAutoCreateRowSorter(true);
myTable.setRowHeight(20);
///////////////
sorter = new TableRowSorter(myTable.getModel());
List sortKeys = new ArrayList();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.DESCENDING));
sorter.setSortKeys(sortKeys);
sorter.setRowFilter
(
new RowFilter<TableModel, Integer>()
{
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
{
boolean included = true;
Object cellValue = entry.getModel().getValueAt(entry.getIdentifier(), 0);
if (cellValue == null || cellValue.toString().trim().isEmpty())
{
included = false;
}
return included;
}
}
);
myTable.setRowSorter(sorter);
///////////////
teamScrollTable = new JScrollPane(myTable);
teamScrollTable.setSize(500,300);
teamScrollTable.setLocation(50,100);
System.out.println("Creating team table");
teamPanel.add(teamScrollTable);
}
And then I have this code to update the table.
public void RefreshTeam()
{
teamPanel.remove(teamScrollTable);
rl.readRunners();
String[] sampleHeaders = {"Athlete ID", "Team"};
JTable myTable = new JTable(rl.teamTableData,sampleHeaders);
myTable.setAutoCreateRowSorter(true);
myTable.setRowHeight(20);
///////////////
sorter = new TableRowSorter(myTable.getModel());
List sortKeys = new ArrayList();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.DESCENDING));
sorter.setSortKeys(sortKeys);
sorter.setRowFilter
(
new RowFilter<TableModel, Integer>()
{
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
{
boolean included = true;
Object cellValue = entry.getModel().getValueAt(entry.getIdentifier(), 0);
if (cellValue == null || cellValue.toString().trim().isEmpty())
{
included = false;
}
return included;
}
}
);
myTable.setRowSorter(sorter);
teamScrollTable = new JScrollPane(myTable);
teamScrollTable.setSize(500,300);
teamScrollTable.setLocation(50,100);
System.out.println("Changing team table");
teamPanel.add(teamScrollTable);
}
This code is activated when a button is clicked on the program. However unlike my expectations this does not update the table. I did some research and found this line of code fireTableCellUpdated(). Although I am unable to implement this into my code so that it updates the table since I do not know how. I would greatly appreciate someone helping me implement this code or showing me a better way to do what I want.
In order to update the table you can use the following:
myTable.setModel(new DefaultTableModel(rl.teamTableData,sampleHeaders));
The refreshTeam method (note java methods should begin with lowercase) is not updating the JTable, it is creating a new table entirely, and then trying to add these Components to (what I presume is) an already visible UI. The UI is unaware of this, so after adding/removing Components you should call...
revalidate();
repaint();
...on the components. There are alternatives to this approach - you can change the model rather than recreating the table. You can do so by implementing your own AbstractTableModel (you can then call the fireTableDataChanged that you refer to on the instance to update listeners that the data has changed).
I'm using GXT 3.0 and I want to develop a grid table in it. In table, a cell assigned to be have multiple jobs, like save, delete, update. So I need to develop a grid table which has multiple buttons in a cell. To visualize the problem I'm sharing this image :
I tried to add just a cell via
ColumnConfig.setCell()
method, and It's succeeded. But I must add multiple buttons, or cells to handle events. In short form I need multiple Cells inside a Cell.
I know there is a method called ColumnConfig.setWidget(), but it didn't helped. It just added toolbar(or any widget element) to top(header part).
Remember that I use GXT 3.0
Thanks for any help.
You must use a CompositeCell :
private CompositeCell<ObjectRow> createCompositeCell(){
HasCell<ObjectRow, String> button1 = new HasCell<ObjectRow, String>() {
public Cell<String> getCell() {
return new ButtonCell();
}
public FieldUpdater<ObjectRow, String> getFieldUpdater() {
return null;
}
public String getValue(ObjectRow object) {
return "Button 1";
}};
HasCell<ObjectRow, String> button2 = new HasCell<ObjectRow,String>(){
public Cell<String> getCell() {
return new ButtonCell();
}
public FieldUpdater<ObjectRow, String> getFieldUpdater() {
return null;
}
public String getValue(ObjectRow object) {
return "Button 2";
}
};
List<HasCell<ObjectRow, ?>> cells = new ArrayList<HasCell<ObjectRow, ?>>();
cells.add(buton1);
cells.add(button2);
CompositeCell<ObjectRow> compositeCell = new CompositeCell<ObjectRow>(cells);
return compositeCell;
}
You can set a different FieldUpdater for handle button click.