Is there any other way to implement setOnMouseClicked on JavaFX - java

I had to create a matrix in javaFX. I created it without any problem with GridPane. The next thing is to create like "buttons" on the right side of the matrix, these buttons will move +1 element of the matrix to the right. Like this:
110 <-
101 <- //ie: I clicked this button
100 <-
The result:
110 <-
110 <-
100 <-
The way I handle this bit-shifting-moving was with circular linked list. I don't have any problem with that I think you can ommit that part. I use this method:
private void moveRowRight(int index){
//recives a index row and moves +1 the elements of that row in the matrix.
}
cells is the matrix
The problem is that, first the matrix can be modified by the user input i.e. 5x5 6x6 7x7, so the number of buttons will also change. I tried using a BorderPane(center: gridpane(matrix), right: VBox()) and this is the part of the code how I added some HBox inside the Vbox (right part of the border pane) and using the setOnMouseClicked.
private void loadButtonsRight(){
for(int i = 0; i < cells[0].length ; i++){
HBox newBox = new HBox();
newBox.getChildren().add(new Text("MOVE"));
newBox.prefHeight(50);
newBox.prefWidth(50);
newBox.setOnMouseClicked(e -> {
moveRowRight(i);
});
VBRightButtons.getChildren().add(newBox); //where I add the HBox to the VBox (right part of the Border Pane)
}
}
}
But then there's this problem.
Local variables referenced from lambda expression must be final or effectively final
It seems that I cannot implement lambda with a value that will change. Is there any way to help me to put "buttons" that depends of the matrix size and that uses the method I've created?

The message tells you all you need to know to fix the problem:
Local variables referenced from lambda expression must be final or effectively final
Assign your changing variable to a final constant and use the constant value in the lambda instead of the variable:
final int idx = i;
newBox.setOnMouseClicked(e ->
moveRowRight(idx);
);
If you wish to understand this more, see the baeldung tutorial
https://www.baeldung.com/java-lambda-effectively-final-local-variables

Related

Final variable being changed without any = statements

I'm making an autoclicking project in java for personal use, and using the following method to get coordinates of a click from a class that extends MouseAdapter. The click is being done on a JFrame.
int[] mouseCoordinates = new int[2]; //The coordinates of the click
mouseCoordinates = mouseListenerExample.getCoordinates();
final int[] baseCoordinates = mouseCoordinates; //The base coordinates (no click) which is this problem//
int[][] totalCoordinates = new int[4][2]; //An array that collects all coordinates of 4 mouse clicks
for (int i = 0; i < 4; i++){ //the goal is to get the coordinates of 4 clicks
while (mouseCoordinates[0] == baseCoordinates[0]){
mouseCoordinates = mouseListenerExample.getCoordinates(); //The problem occurs here: when mouseListenerExample.getCoordinates() changes, mouseCoordinates is changed, baseCoordinates is also changing, which it shouldnt, since there are no statements that say so.
if (mouseCoordinates[0] != baseCoordinates[0]) {
break;
}
}
totalCoordinates[i] = mouseListenerExample.getCoordinates();
mouseListenerExample.setCoordinates(baseCoordinates);
mouseCoordinates = baseCoordinates;
}
Is there some statement that is changing baseCoordinates that I am missing?
Your baseCoordinates variable has been declared final, and so it cannot change, but since it is an int[] or int-array, it is a reference variable, and so what cannot change is the reference itself, not the state of the reference, and so the ints held within the array can (and in your case -- do) change.
You're changing the values held by mouseCoordinates. Since the baseCoordinates refers to the exact same int[] object, then this will likewise change the values for baseCoordinates. Best to create a completely new int object for the final variable if you don't want it changed.
Do something like:
final int[] baseCoordinates = new int[mouseCoordinates.length];
System.arraycopy( mouseCoordinates, 0, baseCoordinates , 0, mouseCoordinates.length );
So after some looking around, i just replaced the arrays with individual ints, and added a print statement right before the break, and it made it work.

Java - textField for loop not completing

I've been trying to make a 9x9 grid of textFields, where each textField is allocated to an element in a 2d array. For example the top left textField is field[0][0], the one right of that is field[1][0], and the bottom right textField is field[8][8].
So far I have
TextField[][] fields = new TextField[9][9]; {
for (Y=0;Y<9;Y++) {
XPosition=0;
for (X=0;X<9;X++) {
fields[X][Y] = new TextField(1);
fields[X][Y].setColumns(1);
fields[X][Y].setBounds(XPosition, YPosition ,32, 32);
frame.getContentPane().add(fields[X][Y]);
XPosition=XPosition+32;
}
YPosition = YPosition+32;
}
}
For some reason when I run the program, only the first 5 textFields in the top row get created. I have a feeling all the other textFields are placed under the visible ones. The frame is definitely big enough, and I can't figure out any issues with the code.
Use a grid layout : https://docs.oracle.com/javase/tutorial/uiswing/layout/grid.html
Absolute positioning is not reliable.
Adding
frame.getContentPane().setLayout(null);
has fixed the error

Javafx: GridPane: Prevent column to grow width when text is too long

I have some legacy code to fix and I'm struggling on the following:
I have 2 nested gridpanes. Inside the inner grid, text has to be added.
The column widths of both inner and outer grids are calculated relative to the screen size using following function:
private GridPane createGridPane( int []colsPercent, int []rowsPercent ) {
GridPane gridPane = new GridPane()
// setup columns
ColumnConstraints []colConst = new ColumnConstraints[colsPercent.length];
for( int i = 0 ; i < colsPercent.length ; i++ ) {
colConst[i] = new ColumnConstraints();
colConst[i].setPercentWidth(colsPercent[i]);
colConst[i].setFillWidth(true);
}
gridPane.getColumnConstraints().addAll(colConst);
// setup rows
RowConstraints []rowConst = new RowConstraints[rowsPercent.length];
for( int i = 0 ; i < rowsPercent.length ; i++ ) {
rowConst[i] = new RowConstraints();
rowConst[i].setPercentHeight(rowsPercent[i]);
rowConst[i].setFillHeight(true);
}
gridPane.getRowConstraints().addAll(rowConst);
return gridPane;
}
The grids and their contents are added by following code:
gridPane = createGridPane(colConstFeedbackScreenPercent,
rowConstFeedbackScreenPercent);
gridPane.setGridLinesVisible(true);
GridPane.setHgrow(gridPane, Priority.NEVER);
GridPane innerGridPane = createGridPane(colConstFeedbackInnerPercent,
rowConstFeedbackInnerPercent);
GridPane.setHgrow(innerGridPane, Priority.NEVER);
innerGridPane.setGridLinesVisible(true);
gridPane.add(innerGridPane, 1, 1);
If I add text to the inner gridPane, like so:
VBox vBox = new VBox();
vBox.setSpacing(25);
{
Text text = new Text(IRAPfx.getData().getOptionString("Localised text 6"));
text.setFont(Font.font("Arial",FontPosture.ITALIC,FONT_SIZE_INSTRUCTIONS));
text.setFill(Color.WHITE);
GridPane.setHgrow(text, Priority.NEVER);
vBox.getChildren().add(text);
}
...
GridPane.setHgrow(vBox, Priority.NEVER);
innerGridPane.add(vBox, 1, 1 + resultsCell );
The inner grid, and together with this inner grid the outer grid, will grow when the text string is too large to fit in its cell, resulting in the whole grid (inner and outer) being "stretched" to fit this long contents, and running over the right edge of the screen.
What I would like to archieve is that the long string runs over the outer grid, or even out of the screen if it is really long, but not stretching the whole grid.
As you can see in the code, I tried setting Hgrow to NEVER to no avail. Any suggestions how to fix it (I can't find examples or tips on the www..) would be very welcome.
Thanks in advance,
Joris
I found a partial solution to it:
You can clip or wrap the text to the available width of the cell by using 'label' in stead of 'text'. See below:
For wrapping:
{
Label label = new Label(IRAPfx.getData().getOptionString("Localised text 6"));
label.setFont(Font.font("Arial",FontPosture.ITALIC,FONT_SIZE_INSTRUCTIONS));
label.setWrapText(true);
label.setTextFill(Color.WHITE);
vBox.getChildren().add(label);
}
For clipping:
{
Label label = new Label(IRAPfx.getData().getOptionString("Localised text 6"));
label.setFont(Font.font("Arial",FontPosture.ITALIC,FONT
label.setTextOverrun(OverrunStyle.CLIP);
label.setTextFill(Color.WHITE);
vBox.getChildren().add(label);
}
This more or less solves my problem, however I still did not manage to let the text run over into the next columns (the ones to the right of the cell where the text/label is put).
Any help on that still welcome!
Have you tried the setWrappingWidth method on Text object, you can define a predefined with of this way, 200 in this case:
text.setWrappingWidth(200);
It isn't really clear to me why you have the Text elements inside a VBox before putting them into the GridPane. But you can do something like this:
vbox.maxWidthProperty().bind(innerGridPane.widthProperty().multiply(innerGridPane.getColumnConstraints().get(1).percentWidthProperty());
text.wrappingWidthProperty().bind(vbox.maxWidthProperty());
Unfortunately, you can't just get a GridPane column and get it's width property to bind it, so you have to recalculate it using the ColumnConstraints properties.
Any case where you need to have items dynamically change behaviours base on window sizes and so on, you'll need to bind the relevant properties together.

Replace a node at (row,col) in a JavaFX GridPane

I am making a grid-style game/simulation based on bugs "sensing" and eating food. I am using a gridPane (called worldGrid) of labels to show the grid of bugs and food. This is obviously going to be constantly updated when a bug moves cells towards food etc.
I currently have a function updateGrid(int col, int row, String cellContent) which I want to replace the label at [row,col] with a label that has the new text in cellContent.
I have the follow which works
worldGrid.add(new Label(cellContent), row,col);
however im worried that that is just adding a label on top of the current label and obviously over 100 iterations of the simulation thats not ideal.
I have tried this before adding the label:
worldGrid.getChildren().remove(row,col);
However I then get an IllegalArgumentException when trying to do the add line.
Any ideas on how to do this? Or even better, any ideas on how best to show a constantly changing grid that will eventually use sprites instead of text?
The col/row provided by grid.add(node, col, row) (ATTENTION first comes col!) is only a layout constraint. This does not provide any means to access columns or rows like slots in a 2-dimensional array. So to replace a node, you have to know its object itself, e.g. remember them in a separate array.
Then you are able to call getChildren().remove(object)... e.g.:
GridPane grid = new GridPane();
Label first = new Label("first");
Label second = new Label("second");
grid.add(first, 1, 1);
grid.add(second, 2, 2);
second.setOnMouseClicked(e -> {
grid.getChildren().remove(second);
grid.add(new Label("last"), 2, 2);
});
box.getChildren().addAll(grid);
I agree with Jens-Peter but I would add that you can use GridPane's getColumnIndex and getRowIndex to obtain a particular node's location.
For example ( this is in a component which extends GridPane ):
// set the appropriate label
for (Node node : getChildren()) {
if (node instanceof Label
&& getColumnIndex(node) == column
&& getRowIndex(node) == row) {
((Label)node).setTooltip(new Tooltip(tooltip));
}
}
in other words, go through all the nodes in the GridPane and check for a match of the conditions you want, in this case row and column.
You called getChildren().remove() method directly will cause the gridpane to go out of sync with the constraints. When you add, it also setup the constraint for the node. Add clearConstraints() method.

Connect Four Java Game Project, need help on basic concepts

I am working on a lab, it's a connect four game. I'm having trouble specifically with basic concepts like how classes communicate with each other, how to use private instance variables, how to use an ArrayList, how to compare JLabels or set them as something comparable...
To give a brief breakdown I have four classes GUI, Game, Player, Name
I can create the GUI by using two four loops, the game is a grid with 7 columns of 6 pieces. The pieces are images,
JLabel Piece = new JLabel("images/blank.png");
for example to denote an empty spot.
The GUI is based on a JFrame, single content pane and four panels, one for a header which indicates who is playing and who won, another for the 7 buttons accompanying the 7 rows, the grid itself of the possible places to be played and then a button panel which gives you the option to replay.
I'm lacking in a lot of concepts. For instance, the replay button shouldn't appear until the game has ended.
I don't understand how to use an ArrayList. I tried to use
ArrayList<ArrayList<JLabel>> myList = new ArrayList<ArrayList<JLabel>>();
So when I create the GUI by running two for loops like so
For ( c = 0 ; c<8 ; c++) {
ArrayList<JLabel> column = new ArrayList<JLabel>();
For ( r = 0 ; r<7 ; r++) {
ArrayList<JLabel> row = new ArrayList<JLabel>();
JLabel empty = new JLabel("images/blank.png");
row.add(empty);
}
column.add(row);
}
Even this small step I've already got confused.
I know the two for loops above are not correct specifically the ArrayList.
I don't know how to create the arraylist and then use them.
using something like
column.get().get();
myList.get().get();
to get a specific piece.
I don't know how to pass that to an argument so that for example if I push on button 7 for column 7, and no pieces have been played yet, I can start from the lowest area column 7 row 6 and update that to a played piece, red or yellow for the appropriate player.
This is vague and I doubt I'll get anywhere but I am desperate for help. There isn't much time available from the TA's / Teacher and I believe I am lacking significantly to be able to finish this project.
I understand how it works/what I have to do in words but in terms of applying Java code...
I'd appreciate any help.
OK first off you should use an array of Enums. ArrayLists are intended for lots of items and that can have rapidly changing numbers. Arrays are intended to house data in a grid of some sorts. Since you are using a static board, use arrays! They are also much less memory-intensive. Example:
//Note you should use [column][row] as that is common practice.
States[][] grid = new States[7][6];
//And initialize it:
for(int i = 0; i < grid.length; i++)
for(int o = 0; o < grid[i].length; o++)
grid[i][o] = EMPTY_JLABEL;
Then declare an enum (this is a new class) (NOTE: replace FULL_PLAYER_ONE_JLABEL and FULL_PLAYER_TWO_JLABEL with the JLabels that have the image for each.):
public enum States {
FULL_PLAYER_ONE(FULL_PLAYER_ONE_JLABEL), FULL_PLAYER_TWO(FULL_PLAYER_TWO_JLABEL), EMPTY(EMPTY_JLABEL);
//The image of the appropriate state.
private JLabel label;
//Enum constructors must be private
private States(JLabel label) {
this.label = label;
}
public JLabel getLabel() {
return label;
}
}
In your GUI, have a JButton that is only added to the frame when the game is over. Also add a button to indicate when each column has been clicked by the player.
JButton button = new JButton();
//Initialize JButton and add to frame...
//Anytime before the frame is set to visible:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* Perform tests for for what should happen.
For example test whose turn it is then call a method to add a piece to that column. Then call a checker to see if someone has won. If so, display the replay button, otherwise do nothing (allow other player to move).
*/
}
}

Categories

Resources