JavaFX - Dealing with duplicate children error - RadioButtons - java

I have three radio buttons, I want my GUI to be uploaded depend on the checkbox I click.
I had referred to this guide: http://docs.oracle.com/javafx/2/ui_controls/radio-button.htm
which seems ideal for my needs. However, it cannot recognize the type despite importing the correct libraries. I decided to write my own little code.
We have horizontal boxes and vertical boxes. I have added the labels and text elements into one horizontal box (indicated by hb, hb1, hb2..) and clubs them all together in one vertical box.
Here is my code for event listener for my radio button:
test1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
try{
//Remove all existing children
vb.getChildren().removeAll(hb,hb2,hb3,hb4,hb5);
log("Cleared");
} catch(Exception e)
{
log(e.getMessage());
}
try{
//Add email and email text into a horizontal box
hb.getChildren().addAll(email, email_text);
hb.setSpacing(10);
//Added the box to the vertical box
vb.getChildren().add(hb);
log("Added the email input box");
}
catch(Exception e){
log(e.getMessage());
}
}
});
Declarations:
final ToggleGroup group = new ToggleGroup();
final RadioButton test1 = new RadioButton("Test 1");
test1.setToggleGroup(group);
grid.add(test1, 0, 1);
final RadioButton test2 = new RadioButton("Test 2");
test2.setToggleGroup(group);
grid.add(test2, 0, 2);
final VBox vb = new VBox(10); // main container
final HBox hb = new HBox(); // Email , Email Text
final HBox hb2 = new HBox(); // Corporate , Corporate Name
I am getting an error : Children: duplicate children added: parent = HBox#359889fd. I did a little research on this : java.lang.IllegalArgumentException: Children: duplicate children added: parent = VBox#872be7 . Honestly, even if we both are working on the same issue, I am having a real hard time getting his code.
My main issue: If I am removing ALL the element in the beginning, why is there a duplicate error?
Perhaps, my code may be completely wrong and made no sense. Do you recommend any other approach?

The main issue was I was removing the vb box but I was not removing the contents of hb. Whereas, I am adding hb. I should have also removed from hb.

Related

How do I make all the fields in my javafx form clickable using GridPane and HBox?

I want to make a form with three sections, two with fields and one with buttons.
public class Form extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
GridPane fp = new GridPane();
fp.setAlignment(Pos.TOP_CENTER);
fp.setHgap(6);
fp.setVgap(6);
fp.add(new Label("Name: "), 0, 0);
TextField name = new TextField();
name.setPrefWidth(450);
fp.add(name, 1, 0);
GridPane sp = new GridPane();
sp.setAlignment(Pos.CENTER);
sp.setHgap(6);
sp.setVgap(6);
sp.add(new Label("Another Name: "), 1, 0);
TextField anothername = new TextField();
anothername.setPrefWidth(120);
sp.add(anothername, 2, 0);
HBox hbox = new HBox();
hbox.setAlignment(Pos.BOTTOM_CENTER);
Button btn1 = new Button("Button 1");
hbox.getChildren().add(btn1);
Scene scene = new Scene(root, 500, 500);
root.getChildren().addAll(fp, sp, hbox);
stage.setScene(scene);
stage.show();
}
}
formatting and some text might be off but that is my general solution. I made a root stack pane to hold all the parts of my form. I then made two grid panes to hold text fields and an hbox to hold my buttons along the bottom.
example of how it looks
My problem is that only the name field can be clicked. If I try to click another name field it wont work. I can press tab to cycle through the fields and button but I want to be able to click on each field individually. Is there a better way to create one scene with multiple panes or hboxes? I am also open to only having one grid pane, but I thought having two would be easier for formatting since I want to separate different fields. Thank you!
The issue you're facing is caused by your using a StackPane as the root element of your scene.
A StackPane, as the name suggests, stacks its children one on top of the other. Any children placed on top will be the ones receiving events (such as clicking on the anothername field).
You have added 3 nodes as children of your StackPane:
GridPane #1 fp
GridPane #2 sp
HBox hbox
Since the HBox was added last, it is the only node that can receive click events.
Using your example, I've added borders to each of the 3 items above to illustrate how JavaFX is laying them out:
As you can see, each child of the StackPane get resized to fill the entire area (I used different widths for the borders so you can see them all).
You can try this yourself by adding the following code before you show your stage:
fp.setStyle("-fx-border-color: green; -fx-border-width: 15px");
sp.setStyle("-fx-border-color: blue; -fx-border-width: 10px");
hbox.setStyle("-fx-border-color: red; -fx-border-width: 5px");
To solve this, you will need to rethink your layout entirely; a StackPane is certainly not the correct layout pane to use in your case.
I highly recommend working through the examples in Oracle's Working With Layouts in JavaFX tutorial to get a better grasp on how to best layout your scene.

Accessing Arbitrary nodes from EventHandler target

I'm working on a small JavaFX application. In this application I have the following components :
BorderPane -> as the root element
HBox top, bottom -> top and bottom regions
VBox left, right -> left and right regions
FlowPane center -> central region
When the central region is clicked i need to access a node in the top region containing some text. In order to access it i climb the graph upwards from the event's target like this :
public EventHandler<MouseEvent> fieldClicked = (MouseEvent e) -> {
FlowPane target = (FlowPane)e.getTarget();
BorderPane root = (BorderPane)target.getParent();
HBox top = (HBox)root.getChildren().get(0);
HBox top_left = (HBox)top.getChildren().get(0);
Text total = (Text)top_left.getChildren().get(0);
ObservableList<Node> dices = target.getChildren();
/* Do some stuff with retrieved nodes */
};
Is there a better and less verbose way to access an arbitrary node in the scene graph beside iteratively calling Node.getParent()
If you do not store the field in some other way, no. You may attach some id to find a node via CSS selector (lookup) but in that case you're better of doing this a different way:
Store the nodes you need to access in fields (or effectively final local variables, if you register event handlers in the same scope where you create the nodes).
...
private BorderPane root;
private HBox top;
private Text total;
private FlowPane target;
public EventHandler<MouseEvent> fieldClicked = (MouseEvent e) -> {
ObservableList<Node> dices = target.getChildren();
/* Do some stuff with fields */
};
private void initializeNodes() {
...
total = new Text();
top = new HBox(total);
root.setTop(top);
target = new FlowPane();
root.setCenter(target);
...
}
It's better to decouple the modification of certain values from the layout of the scene as much as possible anyways, since this makes it easier for you to rearrange the scene without having to worry about event handlers navigating the scene correctly via up-/downward navigation though the scene. Furthermore you'll get into trouble, if you're using your approach in cases where you use a "parent" other than a Pane or Group, e.g. ScrollPane since the skin of ScrollPane inserts the content node into the scene as it's descendant, but not as it's child and it doesn't do this until the first layout pass.
BTW: Note that it's Event.getSource that yields the node the event handler was triggered for, not Event.getTarget.
To get a specific Node, you can use the lookup() method of the javafx.scene.Scene class.
For Example, you can set an ID on the node containing some text and then find it with scene.lookup("#theID");
public EventHandler<MouseEvent> fieldClicked = (MouseEvent e) -> {
FlowPane target = (FlowPane)e.getTarget();
Text total = (Text) target.getScene().lookup("#myTextID");
/* Do some stuff with retrieved nodes */
};
The ID you can set by:
Text text = new Text("My Text element somewhere");
text.setId("myTextID");
I'm new to JavaFX so I don't know if this is the best way either. But I hope, this is what you are looking for.
By the way, if you want to get to the root node, you can instead use:
public EventHandler<MouseEvent> fieldClicked = (MouseEvent e) -> {
FlowPane target = (FlowPane)e.getTarget();
BorderPane root = (BorderPane) target.getScene().getRoot();
};
Its maybe helpful when you have more Elements in your FlowPane, then you also don't have to call that often Node.getParent().
Hope it helps!

Vbox not filling up all of the available space in a gridpane

I am currently developing a messenger in javafx. my general layout is a gridpane with a customized Vbox containing a ListView and a Textfield. The problem is that as you can see below there is a big emty area beneath the textfield. I already tried setting the rowspan to 2, which didn't work.
The important Code:
Main:
chatBox = new ChatBox();
gridPane.add(chatBox, 1, 0, 1, 2);
ChatBox(extends Vbox):
private static ListView<Message> messages;
private TextField inputField;
public ChatBox() {
inputField = new TextField();
messages = new ListView<>();
init();
getChildren().addAll(messages, inputField);
}
Try adding this in the ChatBox class:
VBox.setVgrow(messages, Priority.ALWAYS);
And add this in the main class:
GridPane.setVgrow(chatBox, Priority.ALWAYS);
You need to set the vgrow using RowConstraints. Assuming your first row contains the ListView that is supposed to take all the available space:
RowConstraints constraints = new RowConstraints();
constraints.setVgrow(Priority.ALWAYS);
gridPane.getRowConstraints().addAll(constraints, new RowConstraints());
In order to force the list view to take all the height available in its parent, you can use the following method:
messages.setMaxHeight(Double.MAX_VALUE);
If the problem comes from the VBox, its maxHeight can also be modified with the same method.

Returning a node from a cell on GridPane. (JavaFX)

I have a school project or something like that and I am trying to make a sign up panel for users. This panel opens when user clicks on sign up. It looks like this.
What I am trying to do is I want to disable that Create Button and It will be enabled only if there are 3 checks on the dialog.
I am using a GridPane on Dialog and I was thinking about returning those certain nodes (Checks which are ImageViews) at those cells and check whether the condition is true. However, I could not figure out how to return a node from GridPane. If you have any other approach for this problem it is fine too.
This is the code's relevant part.
public void SignUp(){
//Create the custom dialog.
Dialog signUpDialog = new Dialog();
//Dialog Title
signUpDialog.setTitle("Sign Up");
//Setting "OK" button type.
ButtonType buttonTypeCreate = new ButtonType("Create", ButtonBar.ButtonData.OK_DONE);
//Adding Button types.
signUpDialog.getDialogPane().getButtonTypes().addAll(buttonTypeCreate, ButtonType.CANCEL);
//Creating the GridPane.
GridPane gridPane = new GridPane();
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.setPadding(new Insets(20, 150, 10, 10));
//Setting the Check Icon.
Image imageCheck = new Image("resources/check_icon.png");
//Setting 3 different ImageViews for Check Icon because can't add duplicates to GridPane.
ImageView imageViewCheck1 = new ImageView(imageCheck);
ImageView imageViewCheck2 = new ImageView(imageCheck);
ImageView imageViewCheck3 = new ImageView(imageCheck);
//Setting the X Icon.
Image imageX = new Image("resources/x_icon.png");
//Setting 3 different ImageViews for X Icon because can't add duplicates to GridPane.
ImageView imageViewX1 = new ImageView(imageX);
ImageView imageViewX2 = new ImageView(imageX);
ImageView imageViewX3 = new ImageView(imageX);
//TextField for User ID.
TextField textFieldDialogUserID = new TextField();
textFieldDialogUserID.setPromptText("User ID");
textFieldDialogUserID.setAlignment(Pos.CENTER_RIGHT);
//PasswordField for Password.
PasswordField passwordFieldDialogPassword = new PasswordField();
passwordFieldDialogPassword.setPromptText("Password");
passwordFieldDialogPassword.setAlignment(Pos.CENTER_RIGHT);
//PasswordField for Confirm Password.
PasswordField passwordFieldDialogConfirmPassword = new PasswordField();
passwordFieldDialogConfirmPassword.setPromptText("Confirm Password");
passwordFieldDialogConfirmPassword.setAlignment(Pos.CENTER_RIGHT);
gridPane.add(new Label("User ID"), 0, 0);
gridPane.add(textFieldDialogUserID, 1, 0);
gridPane.add(new Label("Password"), 0, 1);
gridPane.add(passwordFieldDialogPassword, 1, 1);
gridPane.add(new Label("Confirm Password"), 0, 2);
gridPane.add(passwordFieldDialogConfirmPassword, 1, 2);
gridPane.add(imageViewX1,2,0);
gridPane.add(imageViewX2,2,1);
gridPane.add(imageViewX3,2,2);
signUpDialog.getDialogPane().setContent(gridPane);
Stage signUpStage = (Stage) signUpDialog.getDialogPane().getScene().getWindow();
signUpStage.getIcons().add(new Image("resources/application_icon.png"));
Optional<Pair<String, String>> result = signUpDialog.showAndWait();
}
Create an appropriate BooleanBinding which expresses when the button should be disabled. You can use the Bindings utility class to create the expression, including comparison, ands and ors. To make the code more readable do a static import of the functions.
Get the create button from your panel and bind the boolean expression to the disable property of your button.
If any of the values change the JavaFX framework will automatically reevaluate the bindings and update the button's state accordingly.
import static javafx.beans.binding.Bindings.*;
BooleanBinding notComplete = or(
equal(textFieldDialogUserID.textProperty(), null),
equal(passwordFieldDialogPassword.textProperty(), null));
Node createButton = signUpDialog.getDialogPane().lookupButton(buttonTypeCreate);
createButton.disableProperty().bind(notComplete);
You can use the same mechanism to control the visibility of each checkmark. Create a 'incomplete' BooleanBinding for each textfield and bind it with a not binding to the visible property of the checkmark. Use all these BooleanBindings in a compound or to determine the button state. This way the button state and checkmarks will always be in sync.
I could not figure out how to return a node from GridPane.
gridPane.getChildren()
provides the list of nodes, but you already have your components textFieldDialogUserID, passwordFieldDialogPassword, passwordFieldDialogConfirmPassword.
=> Add an action listener for each of them, that checks the values when its value is changed. depending on the result, enable/disable the Create button (per default, it should be disabled).
you can have an example : http://docs.oracle.com/javafx/2/ui_controls/text-field.htm

Set tooltip on TableColumn(JavaFX) without side effect

There is a way to set tooltip like that
if (tooltipText != null) {
Label lb = new Label(column.getText());
lb.setTooltip(new Tooltip(tooltipText));
column.setText(null);
column.setGraphic(lb);
}
Unfortunately then will be exists ugly side-effect.
We set null to text of column but menuItems invoke column.getText(). If we don't do this, there will be double name in header.How to solve it? Suppose by means of css..
This answer doesn't erase side-effect
How to add a tooltip to a TableView header cell in JavaFX 8
Note: The following code relies on internals of the TableView skin. These could be subject to change, since they reside in the com.sun packages.
CSS lookup can be used to get access to the TableColumnHeader nodes after the first layout pass on the TableView. Those can be used to retrieve the Label that is used to display the TableColumn's text property.
#Override
public void start(Stage primaryStage) {
TableView tv = createTableView();
Scene scene = new Scene(tv);
// generate layout pass
tv.applyCss();
tv.layout();
// assign tooltips to headers
tv.lookupAll(".column-header").stream().forEach(n -> {
TableColumnHeader header = (TableColumnHeader) n;
Tooltip tooltip = new Tooltip("Tooltip: " + header.getTableColumn().getText());
((Control) header.lookup(".label")).setTooltip(tooltip);
});
primaryStage.setScene(scene);
primaryStage.show();
}

Categories

Resources