Dynamically adding checkboxes in JavaFX - java

I'm trying to create a game prediction system in Java with GUI based on JavaFX using scene builder. It contains a HashMap containing a game object in which there is an ArrayList containing the participating athletes. Both of which will be decided by the user at runtime. Only a certain type of athletes will be eligible for participating in a certain type for a game, for example, swimmers will be able to participate only in swimming games. I can get the eligible athletes at by matching their types with game object and athlete object, but how do I create the checkboxes(for the eligible athletes at runtime?

You can use following approach to add or remove checkboxes at runtime
package com.grsdev.stackoverflow.question170919.pack02;
import javafx.application.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* #author gaurav salvi
*
*/
public class JavaFxCheckBoxDemo extends Application{
private static VBox root;
private static CheckBox box=new CheckBox("Apple");
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
root=new VBox();
Button button=new Button(" toggle ");
button.setOnAction(JavaFxCheckBoxDemo::buttonClicked);
root.getChildren().add(button);
root.getChildren().add(box);
Scene value=new Scene(root,200,200);
stage.setScene(value);
stage.show();
}
private static void buttonClicked(ActionEvent event){
if(root.getChildren().contains(box)){
root.getChildren().remove(box);
}else{
root.getChildren().add(box);
}
}
}

I think, dynamically creating objects in Scene Builder and linking them in the controller on basis of their ID would very difficult if not impossible. One possible workaround for the given problem would be adding several static objects and set their visibility to hidden. As and how athletes were created and getting added to the ArrayList in the Game object I changed the visibility for the given checkbox.

Related

Problem with extending SimpleStringProperty

I am just trying to extend a SimpleStringProperty in OpenJFX 11.0.1 to add some extra functionality. But ist seems not so easy, I experienced strange behavior of my extended Property and I don't know why. I think it should work.
My in this sample code simplified SimpleStringProperty extension contains another readonly string property which should be updated every time the the user types into a bound TextField. In this case remove all not allowed characters and convert the prefix. (I know this is not perfect but short enough to show)
After starting the sample code you will get a window with a rows of Controls. Typing in a String like "001 (242) 555666" the label should show the normalized phone number like "+1242555666".
The initial conversion works correcty.
I never get any exceptions.
The conversion is called when I type in new digits.
But if you play around with typing and deleting after a few seconds the set() method of my property isn't longer triggered by the bidirectional binding to the TextField.
To simplify the example I didn't use a TextFormatter. If I use one the problem doesn't change.
Can anyone help me figure out the problem?
Windows and OS X show the same behavior with OpenJFX 11 and OpenJFX 11.0.1
I tried the same code with JDK 1.8 and there it works fine.
package testproperty;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
public class TestProperty extends Application {
// attempt to create an own property
public class myPhoneNumberProperty extends SimpleStringProperty {
private final ReadOnlyStringWrapper normalizedNumber = new ReadOnlyStringWrapper("");
public ReadOnlyStringProperty normalizedNumberProperty() { return normalizedNumber.getReadOnlyProperty(); }
public String getNormalizedNumber() { return normalizedNumber.get(); }
public myPhoneNumberProperty() {
super();
}
public myPhoneNumberProperty(String s) {
super(s);
calculate();
}
#Override
public void set(String s) {
super.set(s);
calculate();
}
private void calculate() {
// some calculations (only for test purposes)
String original = this.get();
String result = original.replaceAll("[^0123456789]","");
if (result.startsWith("00")) result = result.replaceFirst("00", "+");
if (original.startsWith("+")) result = "+".concat(result);
normalizedNumber.set(result);
}
}
#Override
public void start(Stage primaryStage) {
// create my property
myPhoneNumberProperty phoneNumberA = new myPhoneNumberProperty("+34 952 111 222");
// set up grid pane
GridPane grid = new GridPane();
grid.setPadding(new Insets(5,5,5,5));
grid.setVgap(20);
grid.setHgap(20);
// set up the row
Label labelA = new Label("Enter phone number");
TextField textFieldA = new TextField();
textFieldA.textProperty().bindBidirectional(phoneNumberA);
Label labelB = new Label("Normalized number");
Label labelN = new Label();
labelN.textProperty().bind(phoneNumberA.normalizedNumberProperty());
grid.addRow(0, labelA, textFieldA, labelB, labelN);
// complete scene
Scene scene = new Scene(grid, 1000, 100);
primaryStage.setTitle("PhoneNumberProperty TestProg");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Your phoneNumberA property object is being garbage collected. To fix this you must keep a strong reference to the object. One option is to make it an instance field.
JavaFX implements bindings using weak listeners/references. Bidirectional bindings have no strong references to the other property. This is different from unidirectional bindings where a reference to the observable value must be kept in order to unbind from it later.

Can I use multiple classes to control one FXML file?

I made an application in Scene Builder on one FXML. I am making a server with JavaFX so I can learn JavaFX and get more familiar with Java's networking libraries.
I have a server terminal Tab and additional tabs within a TabPane. I wanted to make classes that extend upon the main controller class to handle components in each tab.
While trying to implement this I found that the FXMLLoader won't be able to read things if the #FXML annotated variables are static. And the #FXML annotated event listener method won't be read if that is static either.
And if I try any kind of workaround I get nullpointerexceptions when trying to change text in the TextArea. I really don't want to have to use multiple FXML files but it's seeming like I'll have to because if I can't make these static then it just won't work.
Within the Server Terminal Tab there is a TextArea, a TextField, and a Button.
Here's my working code:
package me.Cronin.Keith.JavaServer;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class JavaServer extends Application {
#FXML
public Button btnSendCommand;
#FXML
public TextField consoleInputField;
#FXML
public TextArea serverTerminal;
public static FXMLLoader fxmlLoader = new FXMLLoader(JavaServer.class.getResource("JavaServer.fxml"));
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage) throws IOException
{
Parent p = fxmlLoader.load();
Scene scene = new Scene(p);
stage.setTitle("Java Server v1.0");
stage.setScene(scene);
stage.show();
}
#FXML
public void clickSendCommand(MouseEvent event)
{
serverTerminal.setText("I got clicked..");
}
}
Here's the other class that I don't know what to do with yet:
package me.Cronin.Keith.JavaServer.Terminal;
import me.Cronin.Keith.JavaServer.JavaServer;
public class Terminal extends JavaServer {
public static void logTerminal(String msg)
{
}
}
Is there anything I can do to change this to support what I want to do?
I want to be able to control the variables (JavaFX Components from FXML) in JavaServer.class with my other classes that extend upon it.
I have seen this question here:
Multiple controller for one FXML file
But it doesn't answer my question.
It's seeming like I'd have to put everything in the main controller class or have multiple Fxml files.
I was able to make this work by replacing my Terminal class with this:
Terminal.java
package me.Cronin.Keith.JavaServer.Terminal;
import me.Cronin.Keith.JavaServer.JavaServer;
public class Terminal extends JavaServer {
public static void logTerminal(String msg)
{
JavaServer mainController = fxmlLoader.getController();
mainController.serverTerminal.setText(msg + "\n");
}
}
So the answer is YES you can.
In JavaServer.java I just called Terminal.class statically and then used the static method logTerminal() within it which worked. I extended JavaServer.java in Terminal.java so I could statically call the controller's FXMLLoader.

JavaFX Choiceox change not updating graphics

When I change the underlying observable array list the graphics choice box doesn't update. There must be a newer solution than what I have seen suggested here for example:
JavaFX: Update of ListView if an element of ObservableList changes
int selected = productsChoiceBox.getSelectionModel().getSelectedIndex();
Product prod = products.get(selected);
prod.setName(productName.getText());
prod.setUrl(productUrl.getText());
Any thoughts? I would like to avoid removing and adding.
The "standard" answer is to use an ObservableList with an extractor. However, when I tested this out, it didn't behave as advertised, and it seems like there is a bug (my guess is that ChoiceBox is not correctly handling wasUpdated type changes fired in its ListChangedListener) which I will report at JIRA. Update: filed report at https://javafx-jira.kenai.com/browse/RT-38394
The factory method FXCollections.observableArrayList(Callback) creates an (empty) observable array list. The provided Callback is a function that maps each element in the list to an array of Observables. The list registers listeners with those observables, and if those properties change, the list fires update notifications to its listeners.
This produces strange results with a ChoiceBox, however; one possible workaround would be to use a ComboBox which seems to work fine.
Here's some sample code. Select an item: then type in the text field and press enter to change the name of the selected item. Change ChoiceBox to ComboBox to see the correct behavior:
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ChoiceBoxUpdateExample extends Application {
#Override
public void start(Stage primaryStage) {
ChoiceBox<Item> choiceBox = new ChoiceBox<>();
ObservableList<Item> items = FXCollections.observableArrayList(
item -> new Observable[] {item.nameProperty()}); // the extractor
items.addAll(
IntStream.rangeClosed(1, 10)
.mapToObj(i -> new Item("Item "+i))
.collect(Collectors.toList()));
choiceBox.setItems(items);
TextField changeSelectedField = new TextField();
changeSelectedField.disableProperty()
.bind(Bindings.isNull(choiceBox.getSelectionModel().selectedItemProperty()));
changeSelectedField.setOnAction(event ->
choiceBox.getSelectionModel().getSelectedItem().setName(changeSelectedField.getText()));
BorderPane root = new BorderPane();
root.setTop(choiceBox);
root.setBottom(changeSelectedField);
Scene scene = new Scene(root, 250, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Item {
public final StringProperty name = new SimpleStringProperty();
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
public Item(String name) {
setName(name);
}
#Override
public String toString() {
return getName();
}
}
public static void main(String[] args) {
launch(args);
}
}
The correct and proper answer is from James_D, but if you REALLY want to use ChoiceBox, then try adding and removing:
int selected = productsChoiceBox.getSelectionModel().getSelectedIndex();
products.remove(selected);
products.add(selected, prod);
I do NOT believe this is the right way, but I tested it, and it does work. The ChoiceBox stays on the the removed and selected index and looks like it updates.

How to use javafx.beans.binding.Bindings.select(...) for concise value binding

Overview
As a Swing developer of ten years, I've been thrilled with the features introduced with JavaFX 2.0, especially the rich, fluent, high-level data-binding facilities. This facility alone is worth the cost of learning a new API (which is much less since abandoning FX script). It's going to have a direct impact on the readability and maintainably of my model/view synchronization code.
So far I'm having great success at first level and basic derived bindings, but am struggling to figure out the "JavaFX way" of binding one value to a value two or more levels of indirection in the data graph.
Problem
As shown in the code example below, I'm attempting to use javafx.beans.binding.Bindings.select() to synchronize the text value of a Label with one of the contained properties of the currently selected item in a ComboBox. This code is a simple example of something more complex I'm trying to do, so I understand that it's not hard to do this with the lower level bindings API. I'd like to know if it's possible with the higher-level fluent API, and if the select(...) method actually tracks changes in the indirect properties (i.e. update property if either the direct property or the selected subproperty change).
The documentation and examples on select(...) are sparse, so I'm hoping someone with advanced experience with this can tell me if I'm trying to use the API as designed, or if there's another way to use the high-level binding API to do what I want.
Sample Code
Here's the demo code. When run, there's a ComboBox with two items in it, and then two labels. The first label shows the toString() version of the selected item. The second label attempts to display one of the properties of the selected item, but only displays null.
import static javafx.beans.binding.Bindings.*;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/** Testing cascading binding change triggers. */
public class SandboxTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox(8);
root.setStyle("-fx-padding: 8;");
Scene s = new Scene(root);
stage.setWidth(200);
stage.setScene(s);
ComboBox<MoPoJo> list = new ComboBox<SandboxTest.MoPoJo>();
list.itemsProperty().set(FXCollections.observableArrayList(new MoPoJo("foo", "bar"), new MoPoJo("baz", "bat")));
Label direct = new Label();
direct.setTooltip(new Tooltip("Selected item to string"));
Label withSelect = new Label();
withSelect.setTooltip(new Tooltip("Second property of selected item"));
direct.textProperty().bind(convert(list.getSelectionModel().selectedItemProperty()));
withSelect.textProperty().bind(convert(select(list.getSelectionModel().selectedItemProperty(), "two")));
root.getChildren().addAll(list, direct, withSelect);
stage.show();
}
private static class MoPoJo {
private StringProperty _one = new SimpleStringProperty();
private StringProperty _two = new SimpleStringProperty();
private StringProperty _name = new SimpleStringProperty();
public MoPoJo(String o, String t) {
_one.set(o);
_two.set(t);
_name.bind(format("{ %s, %s }", oneProperty(), twoProperty()));
}
public StringProperty oneProperty() {
return _one;
}
public StringProperty twoProperty() {
return _two;
}
public ReadOnlyStringProperty nameProperty() {
return _name;
}
#Override
public String toString() {
return nameProperty().get();
}
}
}
Bindings.select can't access private class. Make MoPoJo a public class and your code will work.
public static class MoPoJo {
P.S: I believe that fact worth to be mentioned in docs, so I filed http://javafx-jira.kenai.com/browse/RT-20640 on JavaFX javadoc.

EventHandler<T extends Event> in JavaFX a class or interface

I have picked a basic example of printing "Hello World" on screen when the mouse is clicked The code goes like this.
package sample;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
*
* #author gauravp
*/
public class Sample extends Application {
/**
* #param args the command line arguments
*/
Button btn = new Button("ok");
//Label l = new Label("Done");
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("First Stage");
//Created anonymous inner class EventHandler<ActionEvent>
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.print("Hello World !!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
In documentation it is mentioned that EventHandler is an interface , but how come the interface be instantiated...
"new EventHandler<ActionEvent>()"
In a lot of confusion....please reply if you have any idea.. Here is the link
for the EventHandler interface :
http://docs.oracle.com/javafx/2.0/api/javafx/event/EventHandler.html
The syntax
new EventHandler<ActionEvent>() {
#Override // <- notice the annotation, it overrides from the interface.
public void handle(ActionEvent event) {
System.out.print("Hello World !!");
}
}
creates an "anonymous inner class" that implements EventHandler, and defines the handle method. If you inspect the classes generated when you compile your project, you will probably find a class file named Sample$1 (or similar) which is the class generated for this code.
You can read up on inner (anonymous) classes here: http://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html
To answer your question: EventHandler is an interface, and this code doesn't actually create an instance of it, but an instance of the newly declared anonymous class.
What you're seeing there is an anonymous inner class.
It's an implementation of the interface "on the spot", without creating a separate class with a name that implements the interface.
Anonymous inner classes are often used for event handlers of GUI components, as your example code shows.

Categories