Creating a simple slider in javaFX - javafx

What i am trying to achieve is to create a method next to the initialize method, that can handle the current value of a slidebar. I want that method to pass back the value into a Timeline. The problem i am facing is that the method that handles the slider and when its changing is a void method, but i need that value in double type.
I am having hard time understanding the concept of addlistener and what the difference is between Observable, ObservableValue, InvalidationListener, and ChangeListener.
/*DATA FIELD*/
private double cycle;
private Timeline loop;
#FXML
private Slider slider1;
/*METHODS*/
#FXML
public double frequency(){
slider1.valueProperty().addListener(new InvalidationListener(){
#Override
public void invalidated(Observable observable){
cycle = slider1.setValue(slider1.getValue());
}});
return cycle;
public void initialize(URL url, ResourceBundle rb) {
slider1 = new Slider(100, 3000, 500);
loop = new Timeline(new KeyFrame(Duration.millis(cycle), (ActionEvent event) -> {
gb.nextGen(gb.getBoard());
graphic.draw(gb.getBoard());
}

A good way to do this is using property bindings instead. You could bind the value of your slider, to update any other field. An example to bind your slider to a label:
#FXML
private Label label;
#FXML
private Slider slider;
#Override
public void initialize(URL url, ResourceBundle rb) {
label.textProperty().bindBidirectional(slider.valueProperty(), new NumberStringConverter());
}
And in your concrete case, you could bind it to a double property. You could do something as the following:
private final DoubleProperty doubleProperty = new SimpleDoubleProperty();
#FXML
private Slider slider;
#Override
public void initialize(URL url, ResourceBundle rb) {
doubleProperty.bind(slider.valueProperty());
//When you're in need to pass the primitive forward, get it from the property
doubleProperty.get();
}
The binding basically does the following: change the slider, change whatever it's bound to. When using bidirectional binding, it goes both ways. E.g. bind a slider to a textfield, both update each other.

Related

Javafx MVC how to signal new threaded result from one controller to another

I have a Javafx MVC Structure where Controller 1, 2, 3... are in charge of updating the UI elementes, and Super Controller executes whatever job is passed to it from them, and returns the result.
I have a Button Btn1 in Controller1 which when pressed will be disabled and show a processing symbol while the main job is done in other thread whose function is present in Super Controller. Once that thread completes its job, it'll notify the calling function that job is done, so that Btn1 will be enabled again, and processing symbol removed.
CONTROLLER1
#FXML
Button Btn1;
private ProgressIndicator loading;
private SUPERCONTROLLER con;
#Override
public void initialize(URL url, ResourceBundle rb){
loading=new javafx.scene.control.ProgressIndicator();
loading.setProgress(javafx.scene.control.ProgressIndicator.INDETERMINATE_PROGRESS);
}
#FXML
private void press(MouseEvent evt){
Btn1.setDisable(true);
Btn1.setGraphic(loading);
Task<Integer>t=this.con.Action();
new Thread(t).start();
t.setOnSuceeded(e->{
if(t.getValue()==0){
System.out.println("Success");
}else{
System.out.println("falure");
}
Btn1.setDisable(false);
Btn1.setGraphic(null);
});
}
private void inject(SUPERCONTROLLER c){
this.con=c;
}
SUPERCONTROLLER
#FXML
private VBox sidePane;
#Override
public void initialize(URL url,ResourceBundle rb){
var fxml=new FXMLLoader(getClass().getResource("CONTROLLER1.fxml");
Pane childPanel=fxml.load();
sidePane.getChildren().add(childPanel);
CONTROLLER1 childPanel.getController();
CONTROLLER1.inject(this);
}
protected Task<Integer> Action(){
Task<Integer>task=new Task<>(){
#Override protected Integer call(){
Thread.currentThread.wait(3000); //Imitate long task
return 0;
}
}
return task;
}
However, I want the thread concept to stay in Super Controller itself. i.e., Instead of setting suceeded operation in Controller1, I want to move it to Super Controller and Controller1 should just re-enable the button when Super Controller tells it.

JavaFX TableView commit change to object field without setOnEditCommit()

Can TableView commit changes to generic fields like String or Integer without extra step with setOnEditCommit?
Currently I use such structure:
#FXML
TableColumn<User, String> _tableStateUsername;
#Override
public void initialize(URL location, ResourceBundle resources) {
_tableStateUsername.setCellFactory(TextFieldTableCell.<User, String>forTableColumn(new DefaultStringConverter()));
_tableStateUsername.setCellValueFactory(new PropertyValueFactory<User, String>("username"));
_tableStateUsername.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<User, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<User, String> event) {
event.getRowValue().setUsername(event.getNewValue());
}
});
and it works fine, but I feel that it can be simplified (without using SimpleString)
I tried binding Value factory to field but no luck:
_tableStateUsername.setCellValueFactory(cellData -> Bindings.select(cellData.getValue(), "username"));
Is there any way to make cell set NewValue to bound field automatically?

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 Change label text from another class with controller

I want to change the text of a Label with the controller from another class. I have made a method in the FXMLDocumentController, which sets the text to the label:
public void setLabelText(String text)
{
lbZeit.setText(text);
}
Now I want to change this text from another class like my SerialHandlerClass. First, I need the controller, am I right? So I did this:
FXMLLoader loader = new FXMLLoader(FXMLDocumentController.class.getResource("FXMLDocument.fxml"));
loader.load();
controller = (FXMLDocumentController) loader.getController();
Now I run the "setLabelText" method....
controller.setLabelText("asd");
... and nothing happens...
It's very funny, because when I add System.out.println(text); to the "setLabelText(String text)" method, the program writes the correct text to the console.
But, why?
Sorry for my bad english, it's not my native language :)
Thanks,
Julian
You are not updating the label because you are creating another instance of FXMLDocumentController when you use the FXMLoader.
You should set the controller instance, that contains the label, as a parameter to the other Class.
Below you have the code that could solve your need. Here I set the Controller instance to the Connector class, so you can call the setLabelText method from the other class:
public class Connector {
public static void Connecting(FXMLDocumentController controller) {
try {
System.out.println("Connector.Connecting(): Called");
controller.setLabelText("Bye World");
} catch (IOException ex) {
Logger.getLogger(Connector.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("FXMLDocumentController.#handleButtonAction");
label.setText("Hello World!");
Connector.Connecting(this);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public void setLabelText(String text)
{
System.out.println("FXMLDocumentController.setLabelText(): Called");
label.setText(text);
}
}
Note:
If your routine is going to take longer to execute whatever it needs to, you might want to use a Task, so you don't freeze your UI. To update the Label, you have to bind the text property and then update the Text value using the updateMessage() method.
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("FXMLDocumentController.#handleButtonAction");
label.setText("Hello World!");
Task<Boolean> connectorTask = new ConnectorTask();
label.textProperty().bind(connectorTask.messageProperty());
connectorTask.setOnSucceeded(e -> {
// this is going to be called if the task ends up without error
label.textProperty().unbind();
});
new Thread(connectorTask).start();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
//public void setLabelText(String text)
//{
// System.out.println("FXMLDocumentController.setLabelText(): Called");
// label.setText(text);
//}
public class ConnectorTask extends Task<Boolean> {
#Override
protected Boolean call() throws Exception {
// ... do whatever you need here
// then you call this method to update the TextProperty from the Label that was bound.
updateMessage("Bye World");
return Boolean.TRUE;
}
}
}
NOTE:
There is a possible duplicate question for this, please see my answer for this question here!

#FXML public static vars in javafx? [duplicate]

This question already has an answer here:
javafx 8 compatibility issues - FXML static fields
(1 answer)
Closed 8 years ago.
I'm introducing in javafx and i found it very interesting. But I have got a problem that i can't solve.
In the simplest case of a Hello World I can't put a #FXML public static var like this:
public class FXMLDocumentController implements Initializable
{
#FXML
public static Label label;
#FXML
private void handleButtonAction(ActionEvent event)
{
System.out.println("You clicked me!");
label.setText("Hello World!");
}
}
If I change it to private it works.
The cause of that I want to made this vars publics is because I'm using diferents controllers for diferents views (in my real app) and i want to communicate between theirs.
PS: Sorry for my bad english
You should not use a static field here. Your controller belongs to one view and every time the view is created by the FXML Loader new instances of all nodes in the view will be created. SO by calling the FXML Loader 2 times you will receive 2 instances of the view. In addition a new instance of your controller class will be created whenever you load the view by using the FXML viewer. By using a static field you will override values of old controller instances and this will end in horrible bugs. Here is a short introduction to the static keyword: What does the 'static' keyword do in a class?
If you just remove "static" it will work.
Instead of using public fields you should add getter and setter methods to your controller class and mark the field as private: Why use getters and setters?
I want to do something similar like this:
I'm working with tabs and i have this two controllers:
FXML_Tab1Controller.java:
public class FXML_Tab1Controller implements Initializable {
FXML_Tab2Controller tab2controller;
#FXML public Label Label1;
#FXML public TextField TextField1;
#FXML public Button Button1;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML private void actionButton1(ActionEvent event)
{
Label1.setText(tab2controller.TextField2.getText());
}
}
FXML_Tab2Controller.java:
public class FXML_Tab2Controller implements Initializable {
FXML_Tab1Controller tab1controller;
#FXML public Label Label2;
#FXML public TextField TextField2;
#FXML public Button Button2;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML private void actionButton2(ActionEvent event){
Label2.setText(tab1controller.TextField1.getText());
}
}
something similar like that video:
https://www.youtube.com/watch?v=XLVx46ycxco

Resources