JavaFx: Static fields - javafx

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.

Related

How to control visiblity of (fx:)included from controller

I have 3 fxml files:
Main.fxml
Menu.fxml
Manager.fxml
and controller for each.
I need to controll BorderPane center of Main.fxmlfrom both remaining, so I fx:include'd them, gave them fx:id and also set included element Manager.fxml visible = false (only menu button set Manager to center of BorderPane ) but now I'am lost and dont understand how to set included fxml visible when button in Menu.fxmlis pressed. Easy when its about simple element but dont work same with included fxml file.
At the beginning I had only menu inlcuded in Main.fxml and method setCenter in its controller that i used to switch center element using MenuController.java but then i couldn't find way to set main center using manager (so manager could be swithched with other one) becouse FXMLloader only loaded Main and Menu so couldnt set proper MainController instance in ManagerController.
My code before change:
public class MainAdminController {
#FXML
private AdminMenuController adminMenuController;
#FXML
private BorderPane adminBorderPane;
#FXML
public void initialize(){
adminMenuController.setMainController(this);
}
public void setView(String fxmlPatch){
adminBorderPane.setCenter(Fxml.loadFXML(fxmlPatch));
}
}
AdminMenuController
public class AdminMenuController {
public static final String MANAGE_ACCOUNTS = "/fxml/admin/ManageAccounts.fxml";
public static final String MANAGE_ADMINS = "/fxml/admin/ManageAdmins.fxml";
public static final String CHANGE_PASSWORD = "/fxml/ChangePassword.fxml";
#FXML
private MainAdminController mainAdminController;
public void setMainController(MainAdminController mainAdminController) {
this.mainAdminController = mainAdminController;
}
#FXML
private ToggleGroup adminButtons;
#FXML
public void viewManageAccounts() {
mainAdminController.setView(MANAGE_ACCOUNTS);
}
#FXML
public void viewManageAdmins() {
mainAdminController.setView(MANAGE_ADMINS);
}
#FXML
public void viewChangePassword() {
mainAdminController.setView(CHANGE_PASSWORD);
}
}

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.)

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.

cannot convert from String to ObservableValue<String>

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".

How to properly bind javafx label (from fxml) to SimpleStringProperty

I've got Controller that handles actions from UI created by a Scene Builder.
Now when i'm trying to bind a label from this UI to a SimpleTextProperty inside a Controllers constructor i'm getting nullpointer exception. Apparently productInfoLabel is not instantiated yet. Where should i bind these in instead a constructor?
Here's my code
public class Controller {
#FXML
public TableView receiptTable;
#FXML
public TextField productCodeTextField;
#FXML
public Label productInfoLabel;
private StringProperty stringProperty = new SimpleStringProperty();
public Controller() {
productInfoLabel.textProperty().bind(stringProperty);
}
}
So my question is how to do it properly?
All Fields that are annotated by #FXML will be injected in the controller instance that is created by the FXMLLoader. In the constructor you can't use the Fields because the values are not injected at this moment. Use the initalize methods as shown here: http://docs.oracle.com/javafx/2/api/javafx/fxml/Initializable.html

Resources