JavaFx: How to validate mutilple TextFields creating at run time? - java

I am creating multiple TextFields at run time using for-loop and adding them to inside Gridpane(which has 8 columns) like this:
public static GridPane table(int rows){
GridPane table = new GridPane();
for(int i=0; i<rows; i++){
JFXTextField textField1 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField2 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField3 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField4 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField5 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField6 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField7 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
JFXTextField textField8 = new JFXTextField();
textField1.setAlignment(Pos.CENTER);
//add them to the GridPane
table.add(textField1, 0, i+1);
table.add(textField2, 1, i+1);
table.add(textField3, 2, i+1);
table.add(textField4, 3, i+1);
table.add(textField5, 4, i+1);
table.add(textField6, 5, i+1);
table.add(textField7, 6, i+1);
table.add(textField8, 7, i+1);
}
return table;
}
Next I'm creating another method to return component from table at specific row and column like this:
public static Node getComponent (int row, int column, GridPane table) {
for (Node component : table.getChildren()) { // loop through every node in the table
if(GridPane.getRowIndex(component) == row &&
GridPane.getColumnIndex(component) == column) {
return component;
}
}
return null;
}
Problem is here: I want to validate each of the TextField, so if user forget to write in any of the TextField, I want to disable the Button, for this purpose I'm using binding like this:
private void validatingGrid() {
GridPane table = (GridPane) anchorPane().getChildren().get(0);
for(int i=1 ; i<=comboBox().getValue(); i++){
JFXTextField text0 = ((JFXTextField)getComponent (i, 0, table));
JFXTextField text1 = ((JFXTextField)getComponent (i, 1, table));
JFXTextField text2 = ((JFXTextField)getComponent (i, 2, table));
JFXTextField text3 = ((JFXTextField)getComponent (i, 3, table));
JFXTextField text4 = ((JFXTextField)getComponent (i, 4, table));
JFXTextField text5 = ((JFXTextField)getComponent (i, 5, table));
JFXTextField text6 = ((JFXTextField)getComponent (i, 6, table));
JFXTextField text7 = ((JFXTextField)getComponent (i, 7, table));
button.disableProperty().bind(
Bindings.isEmpty(text0.textProperty())
.or(Bindings.isEmpty(text1.textProperty()))
.or(Bindings.isEmpty(text2.textProperty()))
.or(Bindings.isEmpty(text3.textProperty()))
.or(Bindings.isEmpty(text4.textProperty()))
.or(Bindings.isEmpty(text5.textProperty()))
.or(Bindings.isEmpty(text6.textProperty()))
.or(Bindings.isEmpty(text7.textProperty()))
);
}
}
But what's happening is it's only validating last row, let say if I create 3 rows of textfeilds in the Gridpane, so it's only validating 3rd row not 1st and 2nd rows and on the basis of 3rd row entries it's enabling the button but I want after validating all of the rows it should enable button otherwise not. Please help me how can I achieve this.

Your binding logic is correct. However, the problem because of the for loop [for(int i=1 ; i<=comboBox().getValue(); i++)], which ruins your work. All TextFields are at column index 0 and the only thing changes is the row index. So you should use getComponent(i, 0, table); for all TextFields in your for loop without changing the column index to 1 , 2 .. and so on. But that also won't solve the problem because in every loop you're assigning ALL TextFields to the same index and then overwrites it in every loop until all of them points to the TextField at index comboBox().getValue() and column 0 (That's why it's working for the last row as you mentioned).
I would suggest different approach, something like this:
First You need a method to check if all other TextFields are filled/ not empty:
/**
* Check if all the TextFields are filled and not empty
* #param table
*/
private static boolean isAllFilled(GridPane table){
for(Node node : table.getChildren()){ // cycle through every component in the table (GridPane)
if(node instanceof TextField){ // if it's a TextField
// after removing the leading spaces, check if it's empty
if(((TextField)node).getText().trim().isEmpty()){
return false; // if so, return false
}
}
}
return true;
}
Secondly, Listen to the Text Changes for every TextField in the Table, and with every change, check if all other TextField are filled / not empty:
/**
* To Validate the Table (GridPane)
* This method should be added to the tabPane change listener
* #param table
* #param button
*/
private void validateTable(GridPane table, Button button) {
for(Node node : table.getChildren()){ // cycle through every component in the table (GridPane)
if(node instanceof TextField){ // if it's a TextField
((TextField)node).textProperty().addListener((obs, old, newV)->{ // add a change listener to every TextField
// then check if the new value is not empty AND all other TextFields are not empty
if(!newV.trim().isEmpty()&&isAllFilled(table)){
button.setDisable(false); // then make the button active again
}
else{
button.setDisable(true); // or else, make it disable until it achieves the required condition
}
});
}
}
Also, you need to set the button to disable once after its creation.
Button button = new Button("Test");
button.setDisable(true);
Finally, you need to add the method in the tabPane Change Listener Block:
tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
.........
.........
.........
validateTable((GridPane) anchorPane().getChildren().get(0), test);
}
Test

Related

JavaFX removing performed action if checkbox is unselected

I am a beginner messing around with JavaFX. In my program, when a checkbox is selected I want it to display a label along with a ChoiceBox. However, when it is unselected, I want both of these to disappear. I'm not quite sure how to do this however.
Here is my code:
String [] options = new String [] {"A", "B", "C", "D", "E", "F"};
CheckBox [] cbs = new CheckBox[options.length];
for (int i = 0; i < options.length; i++){
final CheckBox cb = cbs[i] = new CheckBox(options[i]);
cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(observable.getValue() == true){
ChoiceBox<Integer> choice = new ChoiceBox<>();
Label label = new Label("How many of the selected accounts do you have?");
choice.getItems().addAll(1, 2, 3, 4, 5);
choice.setValue(1);
selection.setAlignment(Pos.BOTTOM_LEFT);
selection.getChildren().addAll(label, choice);
gp.add(selection, 0, 8);
} else if (observable.getValue() == false){
// remove above block if getValue() == false;
}
}
});
}
You need to keep a reference to the nodes. You could do this by adding fields to the anonymus ChangeListener or by adding a (effectively) final local variable to the loop body.
I'm not sure what selection is, but using the same node for multiple checkboxes seems like a bad idea.
The following example simply adds/removes a VBox containing the Label and ChoiceBox. Since this changes the layout though, I recommend disabling the nodes instead or changing the visibility instead of adding/removing the nodes.
#Override
public void start(Stage primaryStage) throws Exception {
String[] options = new String[]{"A", "B", "C", "D", "E", "F"};
GridPane grid = new GridPane();
for (int i = 0; i < options.length; i++) {
final int row = i;
String option = options[i];
CheckBox checkBox = new CheckBox(option);
ChoiceBox<Integer> choice = new ChoiceBox<>();
Label label = new Label("How many of the selected accounts do you have?");
choice.getItems().addAll(1, 2, 3, 4, 5);
VBox choiceContainer = new VBox(label, choice);
checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
if (newValue) {
grid.add(choiceContainer, 1, row);
} else {
grid.getChildren().remove(choiceContainer);
}
});
grid.add(checkBox, 0, row);
}
Scene scene = new Scene(grid);
primaryStage.setScene(scene);
primaryStage.show();
}

JavaFX - Clicking inside GridPane prints out Pane instead of TextField

I'm trying to make a Sudoku Game in JavaFX. I made the 9x9 grid using GridPane and TextField.
Now I want to change the background color of the TextField when user clicks inside it. To check that everyting is fine I am prining the target od the MouseEvent.
My problem is that when I click in the center of TextField, the target is Pane and when i I click elsewhere the target is my GridPane and the background color is changing.
What should I do? I can't figure out how to do it!
public class SudokuGrid {
public static final int GRID_SIZE = 9;
private TextField[][] sudokuCells;
private GridPane sudokuGrid;
public SudokuGrid () {
sudokuCells = new TextField[GRID_SIZE][GRID_SIZE];
createSudokuGrid();
for (int row = 0; row < GRID_SIZE; row++) {
for(int col = 0; col < GRID_SIZE; col++) {
sudokuCells[row][col] = new TextField() {
#Override
public void replaceText(int start, int end, String text) {
// If the replaced text would end up being invalid, then simply
// ignore this call!
if (text.matches("[1-9]|\\s")) {
super.setText(text);
}
}
};
sudokuCells[row][col].setPrefSize(60, 60);
sudokuCells[row][col].setStyle("-fx-background-color: yellow;");
sudokuGrid.add(sudokuCells[row][col], col, row);
sudokuGrid.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
Object source = e.getTarget();
System.out.println(source);
if(source instanceof TextField) {
((TextField) source).setStyle("-fx-background-color: green;");
}
}
});
}
}
sudokuGrid.setPrefSize(270, 270); // 30 * 9
sudokuGrid.setGridLinesVisible(true);
}
private void createSudokuGrid() {
sudokuGrid = new GridPane();
for (int i = 0; i < GRID_SIZE; i++) {
RowConstraints rc = new RowConstraints();
rc.setVgrow(Priority.ALWAYS) ; // allow row to grow
rc.setFillHeight(true); // ask nodes to fill height for row
// other settings as needed...
sudokuGrid.getRowConstraints().add(rc);
ColumnConstraints cc = new ColumnConstraints();
cc.setHgrow(Priority.ALWAYS) ; // allow column to grow
cc.setFillWidth(true); // ask nodes to fill space for column
// other settings as needed...
sudokuGrid.getColumnConstraints().add(cc);
}
}
The source of the event is the object on which you set the event filter; i.e. in this case it is sudokuGrid. So the condition
if (source instanceof TextField)
in your handler will never be true, since the only possible source is the sudokuGrid.
If you want to change the background color of the text field, you can add the event filter to the text field itself:
TextField sudokuCell = sudokuCells[row][col];
sudokuCell.addEventFilter(MouseEvent.MOUSE_PRESSED, e ->
sudokuCell.setStyle("-fx-background-color: green;"));
Better still would be to respond to changes in the text field's focused property (because using a mouse listener will not change the background if the user uses the Tab key to navigate to different text fields):
TextField sudokuCell = sudokuCells[row][col];
sudokuCell.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (isNowFocused) {
sudokuCell.setStyle("-fx-background-color: green;");
} else {
sudokuCell.setStyle("");
}
});
And even better would just be to use an external css file to do this:
sudoku-grid.css:
.text-field:focused {
-fx-background-color: green ;
}
and then in your Java code associate the CSS file with the grid:
sudokuGrid.getStyleSheets().add("sudoku-grid.css");
and remove the handlers entirely.

Javafx get text from textfields in gridpane

for a project I'm working on, I have to display the values of a matrix in a screen. I chose to do this by using text fields in a gridpane as following code indicates:
for(int row = 0; row < length; row++){
for(int column = 0; column < width; column++){
// Create a new TextField in each Iteration
TextField tf = new TextField();
tf.setPrefHeight(50);
tf.setPrefWidth(50);
tf.setAlignment(Pos.CENTER);
tf.setEditable(true);
tf.setText(String.valueOf(this.getElement(row, column)));
// Iterate the Index using the loops
setRowIndex(tf,row);
setColumnIndex(tf,column);
table.getChildren().add(tf);
}
}
If I change the values inside that screen for the text fields, I want to be able to save them. In order to do that, I have to be able to get the text from the text fields. I tried following code, but the iteration over the elements of the table are defined as Nodes, and therefor don't have a .getText() method.
OkButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle (ActionEvent event){
for (Node nd:table.getChildren()){
//Code goes here but Node does not have .getText() method
}
Stage stage = (Stage) OkButton.getScene().getWindow();
stage.close();
}
});
Does anyone know how to get those values?
Thanks a lot!
Assuming that table is of type GridPane, you should add your TextFields like this:
table.add(tf, column, row);
For accessing an element, when it's col and row indices are known there is no easy way:
public Node getNodeByRowColumnIndex(final int row,final int column,GridPane gridPane) {
Node result = null;
ObservableList<Node> childrens = gridPane.getChildren();
for(Node node : childrens) {
if(gridPane.getRowIndex(node) == row && gridPane.getColumnIndex(node) == column) {
result = node;
break;
}
}
return result;
}
See also the answer to JavaFX: Get Node by row and column.

Add selectListener to table in SWT

In SWT I want to show a Table with 4 columns: 1st a number, 2nd a String, 3rd a checkBox and 4th a radio button.
Once all rows are set, I want to add another row for 3rd column (check all/none), and 4th column (clean radio selected).
This is the code (It compiles, but I haven't tested, yet):
//Create table
Table table = new Table(layoutComposite, SWT.BORDER);
table.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL));
String[] tableItems = new String[] {"", "Player", "Show", "Highlight"};
int[] tableSizes = new int[] {30, 150, 20, 20};
// Header Columns and sizes for table
for (int i = 0; i < tableItems.length; i++) {
TableColumn tableColumn = new TableColumn(table, SWT.NONE);
tableColumn.setText(tableItems[i]);
tableColumn.setWidth(tableSizes[i]);
}
table.setHeaderVisible(true);
table.setLinesVisible(false);
// Create items (void)
for (int i = 0; i < positions.size(); i++) {
new TableItem(table, SWT.NONE);
}
TableItem[] items = table.getItems();
// Check and Radio Buttons
Button[] checks = new Button[items.length + 1];
Button[] radios = new Button[items.length + 1];
// Add elements
for (int i = 0; i < items.length + 1; i++) {
// Pos and Player only in first items.length rows
if (i < items.length) {
items[i].setText(0, String.valueOf(positions.get(i).getPos()));
items[i].setText(1, positions.get(i).getPlayer().getPlayerName());
}
TableEditor editorCheck = new TableEditor(table);
checks[i] = new Button(table, SWT.CHECK);
checks[i].pack();
checks[i].setSelection(true);
editorCheck.minimumWidth = checks[i].getSize().x;
editorCheck.setEditor(checks[i], items[i], 2);
TableEditor radioCheck = new TableEditor(table);
radios[i] = new Button(table, SWT.RADIO);
radios[i].pack();
radios[i].setSelection(false);
radioCheck.minimumWidth = radios[i].getSize().x;
radioCheck.setEditor(radios[i], items[i], 3);
}
table.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
if (e.detail == SWT.CHECK) {
if (e.item instanceof Button) {
// TODO:
// Get checked item selected
// If last row (ie: == checks[items.length]), select or unselect all check related buttons
//
//((Button)e.item).get // Problem here!!!!
}
}
else if (e.detail == SWT.RADIO) {
// TODO:
// Get radio selected
// If last row (ie: radios[items.length]), clean selected item (if any)
}
// TODO Auto-generated method stub
super.widgetSelected(e);
}
});
Now I need to add a Listener over check and radio buttons. My question is: How do I know which check or radio button is selected?
should I do something like this?
for (int i = 0; i < checks.length; i++) {
if (e.item == checks[i]) { // found selected
if (i == checks.length -1 ) { // last one
// Select or unselect all
}
// doStuff () ;
}
}
If this is correct, Is there an easy way to know which is selected? If not, how can I do it?
Any other hint will be very welcome.
you can add a selection listener to each checkbox and radio button after you create them:
checks[i].addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent event) {
// your code here...
// get the current checkbox/radio button from the event:
Widget src = event.widget;
// ...
}
});
do the same with radio buttons:
radios[i].addSelectionListener(...);
i hope this helps you. enjoy.

How can I determine which JCheckBox caused an event when the JCheckBox text is the same

I am working on a program that needs to determine which JCheckBox was selected. I am using three different button groups, two of which have overlapping text. I need to be able to determine which triggered the event so I can add the appropriate charge (COSTPERROOM vs COSTPERCAR) to the total(costOfHome). What I cant figure out is how to differentiate the checkbox source if the text is the same. I was thinking of trying to change the text on one button group to strings like "one" "two" etc, but that introduces a bigger problem with how I have created the checkboxes in the first place. Any ideas would be appreciated, thanks in advance!
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JMyNewHome extends JFrame implements ItemListener {
// class private variables
private int costOfHome = 0;
// class arrays
private String[] homeNamesArray = {"Aspen", "Brittany", "Colonial", "Dartmour"};
private int[] homeCostArray = {100000, 120000, 180000, 250000};
// class constants
private final int MAXROOMS = 3;
private final int MAXCARS = 4;
private final int COSTPERROOM = 10500;
private final int COSTPERCAR = 7775;
JLabel costLabel = new JLabel();
// constructor
public JMyNewHome ()
{
super("My New Home");
setSize(450,150);
setLayout(new FlowLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Font labelFont = new Font("Time New Roman", Font.BOLD, 24);
setJLabelString(costLabel, costOfHome);
costLabel.setFont(labelFont);
add(costLabel);
JCheckBox[] homesCheckBoxes = new JCheckBox[homeNamesArray.length];
ButtonGroup homeSelection = new ButtonGroup();
for (int i = 0; i < homeNamesArray.length; i++)
{
homesCheckBoxes[i] = new JCheckBox(homeNamesArray[i], false);
homeSelection.add(homesCheckBoxes[i]);
homesCheckBoxes[i].addItemListener(this);
add(homesCheckBoxes[i]);
}
JLabel roomLabel = new JLabel("Number of Rooms in Home");
add(roomLabel);
ButtonGroup roomSelection = new ButtonGroup();
JCheckBox[] roomCheckBoxes = new JCheckBox[MAXROOMS];
for (int i = 0; i < MAXROOMS; i++)
{
String intToString = Integer.toString(i + 2);
roomCheckBoxes[i] = new JCheckBox(intToString);
roomSelection.add(roomCheckBoxes[i]);
roomCheckBoxes[i].addItemListener(this);
add(roomCheckBoxes[i]);
}
JLabel carLabel = new JLabel("Size of Garage (number of cars)");
add(carLabel);
ButtonGroup carSelection = new ButtonGroup();
JCheckBox[] carCheckBoxes = new JCheckBox[MAXCARS];
for (int i = 0; i < MAXCARS; i++)
{
String intToString = Integer.toString(i);
carCheckBoxes[i] = new JCheckBox(intToString);
carSelection.add(carCheckBoxes[i]);
carCheckBoxes[i].addItemListener(this);
add(carCheckBoxes[i]);
}
setVisible(true);
}
private void setJLabelString(JLabel label, int cost)
{
String costOfHomeString = Integer.toString(cost);
label.setText("Cost of Configured Home: $ " + costOfHomeString + ".00");
invalidate();
validate();
repaint();
}
public void itemStateChanged(ItemEvent e) {
JCheckBox source = (JCheckBox) e.getItem();
String sourceText = source.getText();
//JLabel testLabel = new JLabel(sourceText);
//add(testLabel);
//invalidate();
//validate();
//repaint();
for (int i = 0; i < homeNamesArray.length; i++)
{
if (sourceText == homeNamesArray[i])
{
setJLabelString(costLabel, costOfHome + homeCostArray[i]);
}
}
}
}
I would
Use JRadioButtons for this rather than JCheckBoxes since I think it is GUI standard to have a set of JRadioButtons that only allow one selection rather than a set of JCheckBoxes.
Although you may have "overlapping text" you can set the button's actionCommand to anything you want to. So one set of buttons could have actionCommands that are "room count 2", "room count 3", ...
But even better, the ButtonGroup can tell you which toggle button (either check box or radio button) has been selected since if you call getSelection() on it, it will get you the ButtonModel of the selected button (or null if none have been selected), and then you can get the actionCommand from the model via its getActionCommand() method. Just first check that the model selected isn't null.
Learn to use the layout managers as they can make your job much easier.
For instance, if you had two ButtonGroups:
ButtonGroup fooBtnGroup = new ButtonGroup();
ButtonGroup barBtnGroup = new ButtonGroup();
If you add a bunch of JRadioButtons to these ButtonGroups, you can then check which buttons were selected for which group like so (the following code is in a JButton's ActionListener):
ButtonModel fooModel = fooBtnGroup.getSelection();
String fooSelection = fooModel == null ? "No foo selected" : fooModel.getActionCommand();
ButtonModel barModel = barBtnGroup.getSelection();
String barSelection = barModel == null ? "No bar selected" : barModel.getActionCommand();
System.out.println("Foo selected: " + fooSelection);
System.out.println("Bar selected: " + barSelection);
Assuming of course that you've set the actionCommand for your buttons.
Checkboxes have item listeners like any other swing component. I would decouple them, and simply add listeners to each
{
checkBox.addActionListener(actionListener);
}
http://www.java2s.com/Code/Java/Swing-JFC/CheckBoxItemListener.htm

Categories

Resources