cannot convert from String to ObservableValue<String> - javafx

I'm making a program to manage and show data about airports, their flights and so on.
The fact is that I have a tableView (in javafx) with several tableColumns, and I want to show some information (destiny, origin, company, etc) on each column so I typed this:
#FXML
private TableColumn<Flight, String> destinoCol;
#FXML
private TableColumn<Flight, String> numCol;
#FXML
private MenuButton aeropuerto;
#FXML
private MenuButton tipo;
#FXML
private Button filtrar;
#FXML
private TableColumn<Flight, LocalTime> horaCol;
#FXML
private Button este;
#FXML
private DatePicker fecha;
#FXML
private TableColumn<Flight, String> origenCol;
#FXML
private Label retrasoLabel;
#FXML
private ImageView companiaImg;
#FXML
private VBox detalles;
#FXML
private Button todos;
#FXML
private ImageView avionImg;
#FXML
private Label tipoLabel;
private mainVuelos m;
private List<Airport> aeropuertos;
private Data data;
#FXML
void initialize() {
data = Data.getInstance();
aeropuertos = data.getAirportList();
List<MenuItem> ItemAeropuertos = new LinkedList<MenuItem>();
for (int i = 0; i < aeropuertos.size(); i++) {
MenuItem item = new MenuItem(aeropuertos.get(i).getName());
item.setOnAction((event) -> cambiarAer(event));
ItemAeropuertos.add(item);
}
aeropuerto.getItems().setAll(ItemAeropuertos);
destinoCol.setCellValueFactory(cellData -> cellData.getValue().getDestiny());
}
The method getDestiny(), as it says returns the destiny of a specific flight as a String so obviously I cannot use the last instruction, it says
cannot convert from String to ObservableValue<String>
but I don't really know how to solve it in order to be able to show the destinies on that column.

According to the Javadocs, setCellValueFactory(...) expects a Callback<CellDataFeatures<Flight, String>, ObservableValue<String>>, i.e a function that takes a CellDataFeatures<Flight, String> as its parameter, and results in an ObservableValue<String>.
As the error message says, your function evaluates to a String (cellData.getValue().getDestiny()), which is not the correct type.
You have two choices, depending on your actual requirements.
Either you can create something on the fly that is of the correct type: the easiest thing to use is a ReadOnlyStringWrapper:
destinoCol.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getDestiny()));
This will display the correct value, but won't be nicely "wired" to the property of the flight object. If your table is editable, edits won't automatically propagate back to the underlying object, and changes to the underlying object from elsewhere won't automatically update in the table.
If you need this functionality (and this is probably a better approach anyway), you should implement your model class Flight to use JavaFX properties:
public class Flight {
private final StringProperty destiny = new SimpleStringProperty();
public StringProperty destinyProperty() {
return destiny ;
}
public final String getDestiny() {
return destinyProperty().get();
}
public final void setDestiny(String destiny) {
destinyProperty().set(destiny);
}
// similarly for other properties...
}
and then you can do
destinoCol.setCellValueFactory(cellData -> cellData.getValue().destinyProperty());

I am a bit late I think, but this might help others.
You can have cade as below
destinoCol.setCellValueFactory(cellData -> cellData.getValue().destinyProperty().asObject());
This code will work for property other than string, as I had problem with "LongProperty".

Related

Javafx tableview checkbox to hide unwanted rows?

I'm trying to do some reports by hiding some rows from a tableview (I made in scenebuilder) by using a checkbox. If the object from tableview doesn't have in the column a specific String, it becomes invisible. example: By clicking the checkbox, hide all rows that dont have the exact value "Barcelona" in table_adress. I tried to do this, but I keep complicating myself. Any ideas?
the function
#FXML
void CakeRequestsFromBarcelona(ActionEvent event) throws IOException {
for(Object cake:table.getItems()) {
for (TableColumn column : table.getColumns()) {
//not sure how to write it
}
}
}
initialize
#FXML
private TableColumn<CakeRequest, Integer> table_ID;
#FXML
private TableColumn<CakeRequest, String> table_adress;
#FXML
private TableColumn<CakeRequest, String> table_design;
#FXML
private TableColumn<CakeRequest, String> table_flavour;
#FXML
private TableColumn<CakeRequest, String> table_model;
#FXML
private TableColumn<CakeRequest, String> table_name;
#FXML
private TableColumn<CakeRequest, String> table_phonenumber;
#FXML
private TableView<CakeRequest> table;
public void initialize(){
//cakeRequest
table_ID.setCellValueFactory(
p-> new SimpleIntegerProperty(p.getValue().getID()).asObject()
);
table_name.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().getOwnerName())
);
table_adress.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().getOwnerAddress())
);
table_phonenumber.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().getPhoneNumber())
);
table_flavour.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().getFlavour())
);
table_design.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().getDesign())
);
table_model.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().getModel())
);
}
CakeRequest
public class CakeRequest implements Identifiable<Integer>, Serializable{
private int ID;
private String OwnerName;
private String OwnerAddress;
private String PhoneNumber;
private String Model;
private String Flavour;
private String Design;
Use a FilteredList and change the predicate when the check box is checked/unchecked:
private ObservableList<CakeRequest> data = FXCollections.observableArrayList();
private FilteredList<CakeRequest> filteredData = new FilteredList<>(data, request -> true);
public void initialize(){
// existing code...
table.setItems(filteredData);
}
#FXML
void cakeRequestsFromBarcelona(ActionEvent event) {
if (checkBox.isChecked()) {
filteredData.setPredicate(request -> request.getOwnerAddress().contains("Barcelona"));
} else {
filteredData.setPredicate(request -> true);
}
}

Can TableColumns contain different classes in a TableView?

I am trying to find a way to populate my tablePrice column with prices of specific menu items. The code shown below works fine but I had to create a price variable in the MenuItem class which was not previously there. MenuItem and PricedMenuItem, as well as other classes are generated from a UML domain model for a restaurant management system. This current method is discouraged as I am modifying the domain model.
The commented section shows how far I got with an error on the setCellValueFactory(). Is there a way for a TableView to contain columns of different classes? If so, can someone please assist me in filling in the column directly from the PricedMenuItem class?
MenuItem has a private name and itemCategory enum, as well as a getCurrentPricedMenuItem() method.
PricedMenuItem has a private price as well as a getPrice() method.
#FXML private TableView<MenuItem> tableView;
#FXML private TableColumn<MenuItem, String> tableName;
#FXML private TableColumn<MenuItem, Double> tablePrice;
#FXML private TableColumn<MenuItem, ItemCategory> tableCategory;
#Override
public void initialize(URL location, ResourceBundle resources) {
tableName.setCellValueFactory(new PropertyValueFactory<MenuItem, String>("Name"));
tableCategory.setCellValueFactory(new PropertyValueFactory<MenuItem, ItemCategory>("itemCategory"));
tablePrice.setCellValueFactory(new PropertyValueFactory<MenuItem, Double>("price"));
/*way to retrieve price directly from PMI
tablePrice.setCellValueFactory(new Callback<CellDataFeatures<MenuItem, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call( CellDataFeatures<MenuItem, String> c) {
return new SimpleStringProperty(c.getValue().getValue().getCurrentPricedMenuItem().getPrice()+"");
}
});
*/
categoryDropDown1.getItems().setAll(ItemCategory.values());
categoryDropDown2.getItems().setAll(ItemCategory.values());
tableView.setItems(loadCurrentMenuItems());
updateBox("Select a menu item to edit.", Color.BLACK);
}
Declare tablePrice as
#FXML private TableColumn<MenuItem, Number> tablePrice;
(see JavaFX Properties in TableView for why).
Then the cell value factory should return a Property<Number> (yours is returning a Property<String>), so you need
tablePrice.setCellValueFactory(new Callback<CellDataFeatures<MenuItem, Number>, ObservableValue<Number>>() {
#Override
public ObservableValue<Number> call( CellDataFeatures<MenuItem, Number> c) {
return new SimpleDoubleProperty(c.getValue().getCurrentPricedMenuItem().getPrice());
}
});
or, using lambda expressions to get rid of most of the boilerplate code
tablePrice.setCellValueFactory(cellData ->
new SimpleDoubleProperty(cellData.getValue().getCurrentPricedMenuItem().getPrice()));
(I'm pretty sure you should not have two getValue() calls in there... but you didn't post your model classes.)

JavaFx: Static fields

Could you explain me why eclipse want getMissionFromMissionController() to be static if i haven't annotated listView with the static word ?
Whenever i want to create this function i've got an error:
"Cannot make a static reference to the non-static method getMissionFromMissionController() from the type MainController"
LogPanelController:
public void printLog()
{
textLog.appendText(MainController.getMissionFromMissionController());
}
MainController:
public String getMissionFromMissionController() {
return MissionController.listView.getSelectionModel().getSelectedItem();
}
And the Missionontroller fields:
#FXML private MainController mainController;
#FXML private Label missionsLabel;
#FXML public ListView<String> listView;
#FXML private TextArea textArea;
Here is one of the problems:
return MissionController.listView.getSelectionModel().getSelectedItem();
You access the listView field as though it's static, and it's not - mind the upper case letter in the word MissionController, in this case you access a class, not the missionController field, which I guess you implied.
And the second one: you call non static method as though it's static:
textLog.appendText(MainController.getMissionFromMissionController());
Mind the upper case letter of the MainController in this line. It should be a small one, if you probably try to access the field.

How to call several controls when creating tabs dinamically

I am creating tabs dinamically and i am using the same fxml file for all of them. So, all type of controls that I've included has de same "fx:id". I use this: "#FXML TextField textField". How could I use the TextField of the second tab, the TextField of the first tab, etc?
#Controller
public class MascotaTabControllerImpl implements MascotaTabController
{
private AnchorPane anchorPane;
private Tab tab;
private Mascota mascota;
#FXML
private ComboBox<String> comboMascota;
#FXML
private ComboBox<String> comboTamano;
#FXML
private TextField fieldNombreMascota;
#FXML
private RadioButton radioAlergiaSi;
#FXML
private RadioButton radioAlergiaNo;
#FXML
private TextField fieldRaza;
#FXML
private TextField fieldPeso;
#FXML
private ComboBox<String> comboSexo;
#FXML
private ComboBox<String> comboAgresividad;
#FXML
private TextArea areaObservaciones;
#FXML
private Button buttonEditar;
#FXML
private Button buttonCancelar;
#Override
public void inicializacionFxmlFile(TabPane tabPane, Collection<Mascota> mascotas)
{
try
{
for(Mascota mascota : mascotas)
{
anchorPane = new AnchorPane();
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/mascotaTab.fxml"));
loader.setController(this);
anchorPane.getChildren().setAll(loader.load());
tab = new Tab();
tab.setContent(anchorPane);
tabPane.getTabs().add(tab);
tab.setText(mascota.getNombre());
fieldNombreMascota.setText(mascota.getNombre());
fieldRaza.setText(mascota.getRaza());
comboSexo.setValue(mascota.getSexo());
fieldPeso.setText(String.valueOf(mascota.getPeso()));
comboTamano.setValue(mascota.getTamano());
comboAgresividad.setValue(mascota.getAgresividad());
areaObservaciones.setText(mascota.getObservaciones());
mascota.setNombre(fieldNombreMascota.getText());
}
tabSelected(tabPane, mascotas);
buttonEditar.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
mascota.setNombre(fieldNombreMascota.getText());
mascota.setRaza(fieldRaza.getText());
mascota.setSexo(comboSexo.getValue());
mascota.setPeso(Float.parseFloat(fieldPeso.getText()));
mascota.setTamano(comboTamano.getValue());
mascota.setAgresividad(comboAgresividad.getValue());
mascota.setObservaciones(areaObservaciones.getText());
clienteService.actualizarMascota(mascota);;
}
});
buttonCancelar.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
return;
}
});
}
catch(IOException ioe)
{
System.out.println(ioe.getMessage());
}
}
Edit: imagine "for" statement runs twice. How could call controls (TextFields, ComboBoxes, etc.) of the first tab?
For example, when "for" statement runs the first time "fieldNombreMascota" reference is "id=110" but when "for" statement runs the second time, the same TextField reference is "id=113". So how can I call them with annotation #FXML?
Because you call
loader.setController(this);
inside your loop, every time you call loader.load() you will reuse the same object as the controller for the FXML you are loading. This means each time you go through that for loop, the #FXML-injected fields will get redefined. So by doing this, you essentially make it impossible to access any FXML fields except the ones from the last iteration of the loop.
You must not call loader.setController(this). The "standard" way to combine FXML files and controllers is to use an fx:controller attribute in the root element of the FXML file, specifying a class name. This will cause the FXMLLoader to create a new instance of that class every time you call loader.load(). If there is some other problem you are trying to solve by attempting the non-standard way you are doing things, then the solution you are attempting is not the right approach.
If you organize your code well, the FXML creates a controller instance, and there should be no need whatsoever to access that controller externally. You may need some of your controllers to access a shared data model of some kind, but this is certainly not the right approach to achieve that.

JavaFX TableColumn Show HashMapValue (Non Property)

I've a simple question but I'm stuck with it since hours.
I have a Class UnitMap which extends HashMap
And a Class AttackCommand which contains an UnitMap units = new UnitMap();
I'm attempting to display the value of a MapEntry in my TableView:
#FXML
private TableColumn<AttackCommand, Integer> columnlkav;
#FXML
private TableColumn<AttackCommand, Integer> columnstart;
#FXML
private TableView<AttackCommand> tableview;
#FXML
private TableColumn<AttackCommand, String> columnname;
#FXML
private TableColumn<AttackCommand, Point> columntarget;
private ObservableList<AttackCommand> tableData = FXCollections.observableArrayList();
#Override
public void initialize(URL location, ResourceBundle resources) {
AttackCommand ac = new AttackCommand();
ac.getUnits().put(ConstantsUnit.UNIT_ID_LKAV, 5);
ac.setName("Testname");
tableData.add(ac);
columnname.setCellValueFactory(new PropertyValueFactory<AttackCommand, String>("name"));
columnlkav.setCellValueFactory(new PropertyValueFactory<AttackCommand, Integer>("units"));
tableview.setEditable(true);
columnname.setEditable(true);
tableview.setItems(tableData);
}
So the shown entry in the TableColumn is logically {2=5}, because ConstantsUnit.UNIT_ID_LKAV = 2
How can i display the value proper? I tried a few examples from the net but don't get it
By the way: I don't use SimpleStringProperty for name and don't use other Property Classes in AttackCommand because I want to serialize the Data via EclipseLink to my DerbyDB.

Resources