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.
Related
I am having trouble deleting the previous row of my second table of my Products Stock In GUI in Java.
The flow of the functionality of my GUI is this:
If the user will select a row in the first table of the GUI, the values of those rows will be reflected on the second rows
If the user will select another/different row in the first table, the previous reflected row which was selected in the first table must be deleted and is replaced by the current selected row.
To sum it up, I have to replace/delete the previous reflected rows if I will select a different row in the first table then replace that previous selected row into the current selected row.
Here's my source code:
private void firstTableMouseClicked(java.awt.event.MouseEvent evt) {
DefaultTableModel secondTblmodel = (DefaultTableModel) secondTable.getModel();
int selectPRoww = firstTable.getSelectedRow();
for (int x = 0; x < ProductAllData2[selectPRoww].length; x++) {
//ProductAllData2 is a 3D array
if (ProductAllData2[selectPRoww][x][0] != null) {
if (setRowCountID2 == 0)
secondTblmodel.setRowCount(0);
//Reflects and displays the values of the selected
// row from the first table to the second table
secondTblmodel.addRow(ProductAllData2[selectPRoww][x]);
// deletes previous row if index of selected row is
// greater than the previous selected row (doesn't work)
if (selectPRoww > selectPRoww) {
secondTblmodel.removeRow(selectPRoww - 1);
}
// deletes previous row if index of selected row is
// less than the previous selected row (doesn't work)
if (selectPRoww < selectPRoww) {
secondTblmodel.removeRow(selectPRoww + 1);
}
setRowCountID2++;
}
}
}
Am I missing something in my functionality or do I need to modify the for loops?
In my opinion, your logic should not be like this.
As the second table (View) depends on the selection of the first table, then the second table should always be cleared, then you do this logic.
//Reflects and displays the values of the selected
// row from the first table to the second table
secondTblmodel.addRow(data);
Important Point: Please use ListSelectionListener on table1, rather than depending on mouse clicks.
Example of usage:
table1.getSelectionModel().addListSelectionListener(new SharedListSelectionHandler());
class SharedListSelectionHandler implements ListSelectionListener {
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel lsm = (ListSelectionModel) e.getSource();
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean isAdjusting = e.getValueIsAdjusting();
output.append("Event for indexes "
+ firstIndex + " - " + lastIndex
+ "; isAdjusting is " + isAdjusting
+ "; selected indexes:");
if (lsm.isSelectionEmpty()) {
output.append(" <none>");
} else {
// Find out which indexes are selected.
int minIndex = lsm.getMinSelectionIndex();
int maxIndex = lsm.getMaxSelectionIndex();
for (int i = minIndex; i <= maxIndex; i++) {
if (lsm.isSelectedIndex(i)) {
output.append(" " + i);
}
}
}
output.append(newline);
}
}
How to Write a List Selection Listener | The Java™ Tutorials
Similar StackOverflow: Deleting row from JTable after valueChanged event is triggered
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
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.
I am looking to get the value of the selected row in an AbstractTableModel and I am noticing some things. It is correctly reporting what sell (row) I am on, when it is selected, but as soon as I click my button to remove, the selected row value goes to 0. Resulting in the 0 row always being removed. I want to get the value int selectedRow and use it to remove it from the table and my ArrayLists.
ListSelectionModel rsm = table.getSelectionModel();
ListSelectionModel csm = table.getColumnModel().getSelectionModel();
csm.addListSelectionListener(new SelectionDebugger(columnCounter,csm));
columnCounter = new JLabel("(Selected Column Indices Go Here)");
columnCounter.setBounds(133, 62, 214, 14);
csm.addListSelectionListener(new SelectionDebugger(columnCounter,csm));
contentPane1.add(columnCounter);
rowCounter = new JLabel("(Selected Column Indices Go Here)");
rowCounter.setBounds(133, 36, 214, 14);
rsm.addListSelectionListener(new SelectionDebugger(rowCounter, rsm));
contentPane1.add(rowCounter);
SelectionDebugger:
public class SelectionDebugger implements ListSelectionListener {
JLabel debugger;
ListSelectionModel model;
public SelectionDebugger(JLabel target, ListSelectionModel lsm) {
debugger = target;
model = lsm;
}
public void valueChanged(ListSelectionEvent lse) {
if (!lse.getValueIsAdjusting()) {
// skip all the intermediate events . . .
StringBuffer buf = new StringBuffer();
int[] selection = getSelectedIndices(model.getMinSelectionIndex(),
model.getMaxSelectionIndex());
if (selection.length == 0) {
buf.append("none");
//selectedRow = buf.toString();
}
else {
for (int i = 0; i < selection.length -1; i++) {
buf.append(selection[i]);
buf.append(", ");
}
buf.append(selection[selection.length - 1]);
}
debugger.setText(buf.toString());
System.out.println("CampaignConfiguration: Selected Row: " + selection[selection.length - 1]);
// Set the selected row for removal;
selectedRow = selection[selection.length - 1];
}
}
// This method returns an array of selected indices. It's guaranteed to
// return a nonnull value.
protected int[] getSelectedIndices(int start, int stop) {
if ((start == -1) || (stop == -1)) {
// no selection, so return an empty array
return new int[0];
}
int guesses[] = new int[stop - start + 1];
int index = 0;
// manually walk through these . . .
for (int i = start; i <= stop; i++) {
if (model.isSelectedIndex(i)) {
guesses[index++] = i;
}
}
// ok, pare down the guess array to the real thing
int realthing[] = new int[index];
System.arraycopy(guesses, 0, realthing, 0, index);
return realthing;
}
}
}
The TableModel has nothing to do with selection. The View(JTable) is responsible for the selection.
I want to get the value int selectedRow and use it to remove it from the table and my ArrayLists.
You should NOT have separate ArrayLists. The data should only be contained in the TableModel.
If you want to delete a row from the table (and the TableModel) then you can use the getSelectedIndex() method of the table in your ActionListener added to the "Delete" button. Something like:
int row = table.getSelectedIndex();
if (row != -1)
{
int modelRow = table.convertRowIndexToModel( row );
tableModel.removeRow( modelRow );
}
If you are not using the DefaultTableModel, then your custom TableModel will need to implement the "removeRow(...)" method.
I am just beginning to program using Java Swing. I am not very familiar with each of the many Java Swing Classes, and have ran into a stumbling block regarding the creation of a Table.
I am trying to figure out how to add a table to a Java Swing Program I am making. I have looked at docs.oracle.com to try and figure it out but I am getting an error when I try to access the values of the table. I will include my code to help show what I am currently trying to do and where the error is.
This is my code for making the table and the table model (I think the problem is that my table model is not working correctly):
/*Create the Table Entry*/
columnNames = new String[listoffields.size()+1];
columnNames[0] = "Record Number";
for(int a = 1; a < columnNames.length;a++){
columnNames[a] = listoffields.get(a-1).getTitle();
}
int count = 1;
data = new Object[numrecords][listoffields.size()+1];
for(int a = 0; a < numrecords;a++){
for(int b = 0; b < listoffields.size()+1;b++){
if(b == 0){
data[a][b] = count;
count++;
}
else{
data[a][b] = "";
}
}
}
/* create the table */
JTable table = new JTable(data,columnNames);
table.setCellSelectionEnabled(true);
table.setModel(new AbstractTableModel() {
public String getColumnName(int col) {
return columnNames[col].toString();
}
public int getRowCount() { return data.length; }
public int getColumnCount() { return columnNames.length; }
public Object getValueAt(int row, int col) {
return data[row][col];
}
public boolean isCellEditable(int row, int col) {
return (col >= 1);
}
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
});
JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);
/* addthe table to the JTable tableentry*/
tabelentry.setLayout(new BoxLayout(ProjectFrame.tabelentry, BoxLayout.Y_AXIS));
tabelentry.add(scrollPane);
tabelentry.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
After the table is displayed and I edit the blank values for each cell, I click a submit button that submits the cell. However, the following error is encountered:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
at java.util.Vector.elementAt(Unknown Source)
at javax.swing.table.DefaultTableModel.getValueAt(Unknown Source)
at Client.GUIComponents.ProjectFrame$1.submit(NameOfFile.java:LineNumber)
The error is encountered when trying to submit as follows:
public void submit(){
String recordvalues = ""; //contains a String representation of what I want to submit
for(int a = 0; a < numberoffields;a++){
for(int b = 0; b < numberofrecords;b++){
if(a != 0){ //I want to skip the first column
recordvalues = recordvalues + (String) tabelentry.getModel().getValueAt(b, a) + ","; //The error is at this line in the code
}
}
}
}
I provided my code to give an example of the current problems I am encountering and what I have currently tried. To generalize my question, I am wondering how to correctly write a table model class so that I can access the values at each of the cells in the table. Any assistance would be helpful. Thanks a ton!
My immediate thought is that you're creating two different table models.
JTable table = new JTable(data,columnNames);
table.setCellSelectionEnabled(true);
table.setModel(new AbstractTableModel() {...
new JTable(data,columnNames) is actually creating it's own DefaultTableModel based on the information you are providing it. Try removing the table.setModel(new AbstractTableModel() {... code
The other, associated, problem is, I don't know how data and columnNames are declared.
Have a read through How to Use Tables for more details
Updated
The other problem, as pointed about by Hovercraft, is you adding one table to another and then accessing the wrong model.
The table creation should look more like...
tableEntry = new JTable(data,columnNames);
JScrollPane scrollPane = new JScrollPane(tableEntry );
tableEntry .setFillsViewportHeight(true);
// Don't forget to add the scroll pane to you view !!
Then you submit method should look more like...
public void submit(){
String recordvalues = ""; //contains a String representation of what I want to submit
TableModel model = tableEntry.getModel();
for(int a = 0; a < model.getColumnCount();a++){
for(int b = 0; b < model.getRowCount();b++){
if(a != 0){ //I want to skip the first column
recordvalues = recordvalues + (String) model.getValueAt(b, a) + ","; //The error is at this line in the code
}
}
}
}
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
This exception means that the column size of your table is 0, I think it's because you don't add columns into your TableModel.
Try to create several TableColumn Column = new TableColumn(); then add these columns to your table