I want the title of my notes-program to change whenever my list (notes) changes. To achieve this I wanted to bind an IntegerProperty to the size of my list, but it says:
The method bind(ObservableValue<? extends Number>) in the type Property is not applicable for the arguments (int)
Does this mean I should cast the size from int to a Number (tried it but there was another problem) or is there an even easier solution?
public class Notes extends Stage {
ObservableList<String> notes = FXCollections.observableArrayList();
public Notes() {
this.setup();
}
private void setup() {
IntegerProperty size = new SimpleIntegerProperty();
size.bind(this.notes.size());
this.setTitle(String.format("Notes (%d)", size.getValue()));
final Scene scene = new Scene(this.createRootPane());
this.setScene(scene);
}
}
Just bind your title property to the list size’s asString binding:
titleProperty().bind(Bindings.size(notes).asString("Notes (%d)"));
Related
I need to get two specific fields of every object inside an ObservableList, multiply them and get the sum of all their products. I also need to keep the sum updated
For now I'm trying to get the sum of only one of those fields first (just because its easier and might help me to understand what is happening).
I have discovered:
How to bind an ObjectBinding<BigDecimal> to a Label with a Formatter?
which is almost what I'm trying to do. However, applying the same code will throw a NullPointerException. Which I assume it's because there was more behind the scene.
Also, according to:
Javafx How to make a Binding to a list of Properties
the observableList needs to have and extractor so it can track the updates of the required field on each element.
Here is what I tried, only ONE field first!!:
// Graphical stuff
void createExempleMenu(){
ObservableList<Itens> orc = FXCollections.observableArrayList(o -> new Observable[] {o.valueProperty()});
Label valueFinalLabel;
valueFinalLabel.textProperty().bind( // this gets NullPointerException
Bindings.createObjectBinding(() ->{
if(orc==null || orc.isEmpty())
return BigDecimal.ZERO; // case orc is empty
else
return orc.stream().map(i->i.getValue()).reduce(BigDecimal.ZERO,BigDecimal::add);},
orc).asString()
);
}
// item
class Itens {
private ObjectProperty<BigDecimal> value;
public BigDecimal getValue() {
return value.get();
}
public void setValue(BigDecimal b){
value.set(b);
}
public void valueProperty(BigDecimal b){
return value;
}
}
You have a few issues with the Itens class:
You never initialize value.
The method valueProperty(BigDecimal b) must return
ObjectProperty<BigDecimal> instead of void.
You have a typo in method getValue(), instead of returning value.get() you are returning valor.get().
class Itens {
private final ObjectProperty<BigDecimal> value =
new SimpleObjectProperty<>(this, "value");
Itens(BigDecimal value) {
this.value.set(value);
}
public BigDecimal getValue() {
return valueProperty().get();
}
public void setValue(BigDecimal value){
valueProperty().set(value);
}
public ObjectProperty<BigDecimal> valueProperty(){
return value;
}
}
Also, you never initialize the Label.
Finally, you can improve your Bindings:
You can create a StringBinding directly instead of creating an
ObjectBinding and then creating StringBinding.
orc is never null, so you don't need to
check for null.
You don't need to check for orc.isEmpty() because when you reduce
the stream you are supplying an identity `BigDecimal.ZERO
Label valueFinalLabel = new Label();
ObservableList<Itens> orc = FXCollections.observableArrayList(
obs -> new Observable[] {obs.valueProperty()});
valueFinalLabel.textProperty().bind(
Bindings.createStringBinding(() -> orc.stream()
.filter(Objects::nonNull)
.map(Itens::getValue)
.reduce(BigDecimal.ZERO, BigDecimal::add).toString(), orc));
Test:
valueFinalLabel.textProperty().addListener((obs, oldVal, newVal) ->
System.out.println(String.format("Label change from %s to %s",
oldVal, newVal)));
Itens i1 = new Itens(BigDecimal.valueOf(1));
Itens i2 = new Itens(BigDecimal.valueOf(2));
Itens i3 = new Itens(BigDecimal.valueOf(3));
orc.add(i1);
orc.add(i2);
orc.add(i3);
i1.setValue(BigDecimal.valueOf(4));
Output:
Label change from 0 to 1
Label change from 1 to 3
Label change from 3 to 6
Label change from 6 to 9
Always remember to initialize your variable folks:
Label valueFinalLabel = new Label("some text");
I may write another question to ask about the product of two fields, which was my initial objective
Where is a complete sample for anyone who stumble here:
public class Main extends Application{
#Override
public void start(Stage primaryStage) {
ObservableList<Itens> orc = FXCollections.observableArrayList();
Label label = new Label("0.00");
label.textProperty().bind( // this gets NullPointerException
Bindings.createObjectBinding(() ->{
if(orc==null || orc.isEmpty())
return BigDecimal.ZERO; // case orc is empty
else
return orc.stream().map(i->i.getValue()).reduce(BigDecimal.ZERO,BigDecimal::add);},
orc).asString()
);
Button btn = new Button("add stuff");
btn.setOnAction(e->{
orc.addAll(new Itens("1"),new Itens("2"),new Itens("3"),new Itens("4"),new Itens("5"));
});
VBox vbox = new VBox();
vbox.getChildren().addAll(label,btn);
Scene scene = new Scene(vbox);
primaryStage = new Stage();
primaryStage.setScene(scene);
primaryStage.setWidth(200);
primaryStage.setHeight(200);
primaryStage.show();
}
}
public static void main(String[] args) {
launch(args);
}
}
public class Itens {
private SimpleObjectProperty<BigDecimal> value;
Itens(String v){
value = new SimpleObjectProperty<BigDecimal>(new BigDecimal(v));
}
public BigDecimal getValue() {
return value.get();
}
public void setValue(BigDecimal b){
value.set(b);
}
public SimpleObjectProperty<BigDecimal> valueProperty(){
return value;
}
}
Hi StackOverflow people,
First question here, I'm stuck on this code and cannot move forward, tried different approaches but cannot figure out why this is happening.
The code is intended to be a few lists each one represents a day of the week, and each of the list has all the possible time. Now, everytime I ran the code each list, even when not update, is using the last date available. For the sake of the example, remove almost all the lists and leave only 2.
The update on the date is being done on this line, t1.setFecha(lunesDate.plusDays(i));, but if for instance, I remove this line on one of the lists, the list is getting the date updated, even if this is happening on another list, with another variable!! It is like the JVM is considering all the lists to be the same... Makes no sense for me...
Can anyone point where is the issue on the code?
Class Turno.class
import java.time.LocalDate;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
public class Turno {
private static final int LIBRE = 1;
private static final int RESERVADO = 2;
private static final int CUMPLIDO = 3;
private static final int CERRADO = 4;
private SimpleIntegerProperty id = new SimpleIntegerProperty();
private SimpleObjectProperty<LocalDate> fecha = new SimpleObjectProperty<LocalDate>();
private SimpleIntegerProperty idhorario = new SimpleIntegerProperty();
private SimpleStringProperty horario = new SimpleStringProperty();
private SimpleIntegerProperty estado = new SimpleIntegerProperty();
private SimpleIntegerProperty idProfesional = new SimpleIntegerProperty();
private SimpleStringProperty profesional = new SimpleStringProperty();;
private SimpleIntegerProperty idPaciente = new SimpleIntegerProperty();
private SimpleStringProperty paciente = new SimpleStringProperty();;
private SimpleStringProperty observaciones = new SimpleStringProperty();;
public Turno(int id, LocalDate d, int idh, String h, int e, int idpro, String pro, int idpac, String pac,
String o) {
this.setId(id);
this.setFecha(d);
this.setIdHorario(idh);
this.setHorario(h);
this.setEstado(e);
this.setIdProfesional(idpro);
this.setProfesional(pro);
this.setIdPaciente(idpac);
this.setPaciente(pac);
this.setObservaciones(o);
}
public Turno() {
}
// ID
public final SimpleIntegerProperty idProperty() {
return this.id;
}
public final int getId() {
return this.idProperty().get();
}
public final void setId(final int i) {
this.idProperty().set(i);
}
/* Bunch of getter and setters for properties, just like the one above */
Class TestTurno.class
public class TestTurnos extends Application {
private static Turno turnoSeleccionado = null;
ScrollPane scrollPane = new ScrollPane();
HBox listas = new HBox();
VBox vBoxL = new VBox();
VBox vBoxM = new VBox();
ListView<Turno> listViewTurnosL = new ListView<>();
ListView<Turno> listViewTurnosM = new ListView<>();
List<Turno> listaHorarios = new ArrayList<>();
List<Turno> listaTurnos = new ArrayList<>();
public static void Main(String[] args) {
launch(args);
}
#Override
public void start(Stage escenario) throws Exception {
// Here we get the current Monday date, in order to prepare for the current week
LocalDate lunesDate = null;
LocalDate diaSemana = null;
LocalDate diaHoy = LocalDate.now();
int d = diaHoy.getDayOfWeek().getValue();
lunesDate = diaHoy.minusDays(d - 1);
// Give the schedules to each day list
listViewTurnosL.setItems(FXCollections.observableList(listaHorarios));
listViewTurnosM.setItems(FXCollections.observableList(listaHorarios));
// Then we look for more data on the DB,
for (int i = 0; i < 2; i++) {
// Database magic happens here, we filled the listaTurnos, not relevant
// We make the lists
//
//!!! Here is where the glitch appears, debugging shows that it only gets into the switch on the right conditions,
// but it keeps on updating the date on any of the lists, even when it is updating another list
switch (i) {
case 0: {
for (Turno t1 : listViewTurnosL.getItems()) {
t1.setFecha(lunesDate.plusDays(i));
}
// Value of t1.getFecha() is 1
break;
}
case 1: {
for (Turno t2 : listViewTurnosM.getItems()) {
t2.setFecha(lunesDate.plusDays(i));
}
// Value of t1.getFecha() is 2 !!!!!
// Value of t2.getFecha() is 2
break;
}
}
}
vBoxL.getChildren().addAll(listViewTurnosL);
vBoxM.getChildren().addAll(listViewTurnosM);
listas.getChildren().addAll(vBoxL, vBoxM);
scrollPane.setContent(listas);
Scene escena = new Scene(scrollPane, 800, 800);
escenario.setScene(escena);
escenario.show();
}
}
Consider how you are creating your lists:
listViewTurnosL.setItems(FXCollections.observableList(listaHorarios));
listViewTurnosM.setItems(FXCollections.observableList(listaHorarios));
The documentation for the factory method you are using reads:
Constructs an ObservableList that is backed by the specified list.
That is - the base list provided is kept as the backing (storing) list. Since both ObservableList instances share the same original ArrayList, it is no wonder they share the content.
You may want to use the factory method FXCollections.ObservableArrayList which creates a new ObservableList (the backing list is created internally, or is the list itself).
If you really need the non-observable list instances, you should probably use two different ones if the lists are not to be equal.
If I look at your code I see the following things:
listViewTurnosL.setItems(FXCollections.observableList(listaHorarios));
listViewTurnosM.setItems(FXCollections.observableList(listaHorarios));
This means to that both listViewTurnosX contain exactly the same element references.
case 0: {
for (Turno t1 : listViewTurnosL.getItems()) {
t1.setFecha(lunesDate.plusDays(i));
}
// Value of t1.getFecha() is 1
break;
}
case 1: {
for (Turno t2 : listViewTurnosM.getItems()) {
t2.setFecha(lunesDate.plusDays(i));
}
It doesn't matter which list is iterated, both contain the same elements, so in both cases the same properties get updated.
Say I have a JavaFX app with an observable class SomeObservableClass with some Properties:
public class SomeObservableClass{
private StringProperty prop1 = new SimpleStringProperty();
private DoubleProperty prop2 = new SimpleDoubleProperty();
... constructors, getters and setters
}
and another class which has a property:
public class ParentClass{
private ObservableList<SomeObservableClass> sOC = FXCollections.observableArrayList();`
}
In this parent class I add a listener for the observable list: `
public class ParentClass{
private ObservableList<SomeObservableClass> observableList = FXCollections.observableArrayList();
public`ParentClass(List<SomeObservableClass> list){
this.observableList.addAll(list);
this.observableList.addListener((InvalidationListener) observable -> System.out.println("listener detected a change!"));`.
}
}
Now say that in a controller class I change the property of one of the SomeObservableClass objects:
public class Controller(){
private ParentClass parentClass;
public void changeSomeProps(){
SomeObservableClass anObservableObject = parentClass.getObservableList().get(0);
anObservableObject.setProp1("newVal");
}
}
This doesn't fire the listener. Why ?
I suspect I am lacking some code to make the listener aware that it should fire when any property of the list objects get modified, but I have no idea how to do that.
By default, ObservableList doesn't handle changes of item's contents. But instanciation of ObservableList with an extractor enables handling them.
ObservableList<SomeObservableClass> observableList = FXCollections.observableArrayList(
e -> new Observable[]{e.prop1Property(), e.prop2Property()});
// add items and set listeners here
observableList.get(1).setProp1("newVal");
// It fires InvalidationListener and ListChangeListener.
EDIT:
It seems only ListChangeListener can identify updated items. Try it out please.
observableList.addListener((ListChangeListener) change -> {
while (change.next()) {
if (change.wasUpdated()) {
SomeObservableClass changedItem = observableList.get(change.getFrom());
System.out.println("ListChangeListener item: " + changedItem);
}
}
});
The following creates a simple window displaying the following fields:
proxyType (Combobox:enum)
proxyHost (Textfield:String)
proxyPort (Textfield:int
proxyExclusions (Textfield:String)
The following Java code displays the content:
public class ProxyDemo extends Application {
private FXForm<Proxy> fxForm;
private StackPane mainPane = new StackPane();
static enum ProxyType {
DIRECT, HTTP, HTTPS, FTP, SOCKS;
}
static class Proxy {
private final ObjectProperty<ProxyType> proxyType = new SimpleObjectProperty<ProxyType>();
private final StringProperty proxyHost = new SimpleStringProperty();
private final IntegerProperty proxyPort = new SimpleIntegerProperty();
private final StringProperty proxyExclusions = new SimpleStringProperty();
public Proxy(ProxyType proxyType, String proxyHost, int proxyPort, String proxyExclusions) {
this.proxyType.set(proxyType);
this.proxyHost.set(proxyHost);
this.proxyPort.set(proxyPort);
this.proxyExclusions.set(proxyExclusions);
}
public ProxyType getProxyType() {
return proxyType.get();
}
public String getProxyHost() {
return proxyHost.get();
}
public int getProxyPort() {
return proxyPort.get();
}
public String getProxyExclusions() {
return proxyExclusions.get();
}
}
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) throws Exception {
Proxy proxy = new Proxy(ProxyType.DIRECT, "", 0, "");
fxForm = new FXFormBuilder<>().source(proxy).build();
mainPane.getChildren().addAll(fxForm);
Scene root = new Scene(mainPane);
primaryStage.setTitle("Demo");
primaryStage.setScene(root);
primaryStage.show();
}
public static void main(String... args) {
ProxyDemo.launch(args);
}
}
The following is my attempt to lookup the Proxy Type.
fxForm.getScene().lookup("#proxyType");
It is my intention to Disable the fields when the Proxy Type is DIRECT, otherwise enable them.
Is the "proxyType" a Combobox type?
What is the ID of this field that FXForm2 assigns?
How does FXForm2 assign the IDs?
My questions are solved.
1. Is the proxyType a Combobox type?
No, the proxyType is a ChoiceBox.
2. What is the ID of this field that FXForm2 assigns?
#proxyType-form-editor
3. How does FXForm2 assign the IDs?
The interactive control is always suffixed with "-form-editor".
e.g. proxyHost will have an ID of #proxyHost-form-editor.
The associated label will have the ID #proxyHost-label.
References
FXForm2 GitHub - Wiki
https://github.com/dooApp/FXForm2/wiki/Style-your-form-with-css
Why is my TableColumn.setSortable() showing the sort graphic on the table header when I double-click on it, but it is not actually doing any sort at all? I would imagine it naturally knows how to sort Numbers? Do I have to set an explicit comparator even for types that have a natural sort behavior?
public class PenaltyDashboardManager {
private final TableView<Penalty> penaltyTable = new TableView<Penalty>();
/* ... */
private void initializeTable() {
penaltyTable.setItems(Penalty.getPenaltyManager().getPenalties());
penaltyTable.setEditable(true);
TableColumn<Penalty,Number> penaltyId = new TableColumn<>("ID");
penaltyId.setCellValueFactory(c -> c.getValue().getPenaltyIdProperty());
penaltyId.setEditable(true);
penaltyId.setSortable(true);
/* ... */
penaltyTable.getColumns.add(penaltyId);
}
}
UPDATE
Very odd. I tried to create a simple example to demonstate the sorting not working. But this simple column of integers is sorting just fine :/
public final class TableSortTest extends Application {
private static final ObservableList<NumericCombo> values = FXCollections.observableList(
IntStream.range(1, 100).mapToObj(i -> new NumericCombo()).collect(Collectors.toList()));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Collections.shuffle(values);
TableView<NumericCombo> tableView = new TableView<>();
tableView.setItems(values);
TableColumn<NumericCombo,Number> combo1 = new TableColumn<>("COMBO 1");
combo1.setCellValueFactory(c -> new ReadOnlyObjectWrapper<>(c.getValue().combo1));
TableColumn<NumericCombo,Number> combo2 = new TableColumn<>("COMBO 2");
combo2.setCellValueFactory(c -> c.getValue().combo2);
TableColumn<NumericCombo,Number> combo3 = new TableColumn<>("COMBO 3");
combo3.setCellValueFactory(c -> c.getValue().combo3);
tableView.getColumns().addAll(combo1,combo2,combo3);
Group root = new Group(tableView);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private static final class NumericCombo {
private static final Random rand = new Random();
private final int combo1;
private final IntegerProperty combo2;
private final IntegerProperty combo3;
private NumericCombo() {
combo1 = rand.nextInt((10000 - 0) + 1);
combo2 = new SimpleIntegerProperty(rand.nextInt((10000 - 0) + 1));
combo3 = new SimpleIntegerProperty(rand.nextInt((10000 - 0) + 1));
}
}
}
I found the issue! I was using my own implementation of ObservableList, called ObservableImmutableList. It wraps the ObservableList interface around a Guava ImmutableList. Since the ImmutableList is not modifiable, it cannot be sorted... even in a TableView.
This transitions to another issue I'm struggling to figure out. How do I sort my ObservableImmutableList? So I posted another question.