I am facing right now a problem where I think that my main is executing a method over and over again, instead of one time. Its better if I explain it according to an example. I already were able to program a Minesweeper game. But i wrote it all in one class MAIN. This time I am trying to do it again but using methods and classes, for the sake of practice and better overview.
As you can see, in my Class Calculations, I am trying to create an Array of Labels. In my Main I am trying to add all the Labels from the Array inside the GridPane. Since it is a minesweeper game, i have to add also random bombs, which will be "X" in my example. I did this little test if it works lbs[10].setText("x"), just to see if it works. It doesnt. It will set the text of ALL labels to X once this method is called! I also want to set an onMouseClicked Event in this class. I would appreciate any help and thank you for your time to read this. I surrounded the codes with Hashtag -> ######
//Main
package application;
import...
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
GridPane grid = new GridPane();
Scene scene = new Scene(grid, (20 * 20), (20 * 20));
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
for(int i = 0; i < 20; i++) {
ColumnConstraints column = new ColumnConstraints(20);
grid.getColumnConstraints().add(column);
}
for(int i = 0; i < 20; i++) {
RowConstraints row = new RowConstraints(20);
grid.getRowConstraints().add(row);
}
//#########################################################
Calculations c = new Calculations();
int count = 0;
for (int x = 0; x < c.test().length/20; x++)
{
for (int y = 0; y < c.test().length/20; y++)
{
grid.add(c.test()[count], x, y);
count++;
}
}
//#########################################################
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
And here my class "Calculations"
package application;
import...
public class Calculations {
public Label[] test() {
Label label = new Label();
Label lbs[] = new Label[20*20];
int a = 0;
for (int i = 0 ; i < 400; i++) {
lbs[i] = label;
}
lbs[10].setText("x"); //##### <- doesnt work the way it should be
return lbs;
}
}
This is because all the elements in the array lbs point to the same Label label.
So, when you set the text of any one to "x", it changes the text of label, which is, actually, every label.
Change this line, in the loop:
lbs[i] = label;
to:
lbs[i] = new Label();
Related
I'm attempting to build Minesweeper in Java (Using Eclipse, if that matters for any reason whatsoever) and while making it, I've encountered an issue I haven't had before.
The window that's supposed to show up whenever a mine is pressed is opened... A lot. The window is only meant to open once and prompt the user to either continue or give up.
Important to note this only occurs whenever a mine is pressed on the top row of buttons. Not entirely sure if this is important, but the amount of times a window pops-up is 11. There are also no error codes given.
The following chunk of code is for whenever a user clicks on a mine.
//These are here just for clarification
private int continues = 3;
private Listen l = new Listen();
private class Listen extends MouseAdapter implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (source == mines[i][j])
{
//Though probably not optimal, I created mines based around a number referred to as difficulty
if (minePlacement[i][j] <= difficulty)
{
Frame f = new Frame();
Label L = new Label("You have " + continues + " continues remaining.");
Button B = new Button ("Try Again?");
Label La = new Label("You're out of continues...");
Button But = new Button("Exit");
f.setLayout(new FlowLayout());
B.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
f.dispose();
}
});
if (continues <= 0)
{
f.add(La);
f.add(But);
/*
For clarification,
This actionListener is used
solely to do System.exit(0);
*/
But.addActionListener(li);
f.setSize(250,125);
}
else
{
f.add(L);
f.add(B);
f.setSize(250,100);
}
f.setLocationRelativeTo(null);
f.setAlwaysOnTop(true);
f.setVisible(true);
f.setTitle("You Hit A Mine!");
continues--;
}
This following chunk of code is used in order to build the board itself.
for (int i = 0; i < rows; i++)
{
int x = (int) ((Math.random() + .1) * 10);
minePlacement[i][0] = x;
mines[i][0] = new Button();
mines[i][0].addMouseListener(l);
mines[i][0].addActionListener(l);
add(mines[i][0]);
for (int j = 0; j < cols; j++)
{
int y = (int) ((Math.random() + .1) * 10);
minePlacement[i][j] = y;
mines[i][j] = new Button();
mines[0][j].addMouseListener(l);
mines[0][j].addActionListener(l);
mines[i][j].addMouseListener(l);
mines[i][j].addActionListener(l);
add(mines[i][j]);
}
I've tried looking for other posts which may answer my question, though as it stands, I have failed to find any other posts that have been coded in Java. I've mainly found them in C++ or C, to which I don't understand how the solution was brought about.
I'd like to know if there's any way to set a limit on the amount of windows on the screen, or if there's a way to prevent the window from popping up more times than expected.
Thanks in advance!
I was attempting to layout a JavaFX stage using a GridPane when I ran into the following problem. If I setup the grid with the appropriate constraints and add newly instantiated StackPanes to it, the default sizing of the scene, stage, and it's contents ensures that the contents are visible:
However, if I add a JavaFX CSS style specifying a border to the newly instantiated StackPane before adding it to the GridPane, then the default sizing of things seems to collapse complete:
My code is as follows:
public static void main(final String[] args) {
Platform.startup(() -> {});
Platform.runLater(() -> {
final GridPane gridPane = new GridPane();
final Scene scene = new Scene(gridPane);
final Stage stage = new Stage();
stage.setScene(scene);
final List<StackPane> panes = new ArrayList<>();
for (int i = 0; i < 4; i++) {
// Create a new pane with a random background color for
// illustration
final StackPane p = createNewPane();
panes.add(p);
// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");
}
for (int r = 0; r < 2; r++) {
final RowConstraints rc = new RowConstraints();
rc.setPercentHeight(50);
gridPane.getRowConstraints().add(rc);
}
for (int c = 0; c < 2; c++) {
final ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(50);
gridPane.getColumnConstraints().add(cc);
}
for (int r = 0, i = 0; r < 2; r++) {
for (int c = 0; c < 2; c++) {
gridPane.add(panes.get(i++), c, r);
}
}
stage.show();
});
}
Curiously, if I move the stage.show() to right after I set the Scene, then everything works fine even with the CSS.
Can anyone help me understand, one, whether this is the expected behavior, and two, why the execution order of the stage.show() makes a difference?
Thanks!
What the issue is
Your example is a bit ambiguous. You don't set the preferred size of anything added to the Stage at any time. So, the JavaFX platform can really do whatever it wants in terms of sizing things. Setting a preferred percent size is not the same as setting a preferred absolute size. A percent size is relative, so the question becomes, relative to what? and the answer to that is unclear.
As to why this occurs:
// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");
I couldn't say. My guess is that the use of CSS is triggering some additional layout logic which effects the resizing in the absence of any size hints.
How to fix it
Anyway, the solution is just to make things more clear and specify preferred sizing for at least something in the application, then the application will initially be sized to that preferred sizing.
Here is an example:
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Starter {
public static void main(String[] args) {
Platform.startup(() -> {});
Platform.runLater(() -> {
final GridPane gridPane = new GridPane();
final Scene scene = new Scene(gridPane);
final Stage stage = new Stage();
stage.setScene(scene);
final List<StackPane> panes = new ArrayList<>();
for (int i = 0; i < 4; i++) {
// Create a new pane with a random background color for
// illustration
final StackPane p = createNewPane();
panes.add(p);
// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");
}
for (int r = 0; r < 2; r++) {
final RowConstraints rc = new RowConstraints();
rc.setPercentHeight(50);
gridPane.getRowConstraints().add(rc);
}
for (int c = 0; c < 2; c++) {
final ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(50);
gridPane.getColumnConstraints().add(cc);
}
for (int r = 0, i = 0; r < 2; r++) {
for (int c = 0; c < 2; c++) {
gridPane.add(panes.get(i++), c, r);
}
}
stage.show();
});
}
private static final Random random = new Random(42);
private static StackPane createNewPane() {
StackPane pane = new StackPane();
pane.setBackground(
new Background(
new BackgroundFill(
randomColor(), null, null
)
)
);
pane.setPrefSize(150, 100);
return pane;
}
private static Color randomColor() {
return Color.rgb(
random.nextInt(256),
random.nextInt(256),
random.nextInt(256)
);
}
}
The key part of the solution is the call:
pane.setPrefSize(150, 100);
which sets the preferred size for the stack panes which have been placed in your layout.
Alternatively, rather than doing the bottom up preferred sizing by setting a preferred size on each of the StackPanes, you could also accomplish a similar thing from a top-down perspective by setting appropriate constraints on the GridPane instead, for example:
gridPane.setPrefSize(300, 200);
Note
I'd advise using a JavaFX Application class instead of Platform.startup() call unless there is a really good reason to use the latter (which there is in this case - interfacing with Swing, as you have noted in your comment).
I want it so every time a button is pressed in my 4x4 grid, that it increments moves by 1 . This is creating a 4x4 layout of buttons. Each time any of those buttons are pressed, I want moves to increment. Basically I'm creating the memory game, where you flip cards over to match each other. I just have to keep count of the total amount of moves a player does to solve the puzzle.
private int moves = 0;
private GridPane makeGridPane(){
ConcentrationModel c = new ConcentrationModel();
GridPane grid = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth( 50 );
grid.getColumnConstraints().addAll(col1, col1, col1, col1);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight( 50 );
grid.getRowConstraints().addAll(row1, row1, row1, row1);
for(int row = 0; row < 4; row ++){
for(int col = 0; col < 4; col++){
Button btn = new Button();
ImageView image = new ImageView(c.getCards().get(0).getImage());
image.setFitWidth(WIDTH/4);
image.setFitHeight(HEIGHT/4);
btn.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btn.setGraphic(image);
grid.add(btn, col, row);
}
}
return grid;
}
You can create a single event handler and reuse it for all the buttons. Since you probably want the buttons to do other things too, I would recommend adding this as an event handler, instead of using the convenience method setOnAction(...):
EventHandler<ActionEvent> incrementMovesHandler = e -> moves++ ;
for(int row = 0; row < 4; row ++){
for(int col = 0; col < 4; col++){
Button btn = new Button();
btn.addEventHandler(ActionEvent.ACTION, incrementMovesHandler);
// ...
}
}
This is actually pretty simple. All you have to do is add this bit of code just before your grid.add(...):
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
moves++;
}
});
Or, equivalently, the Java 8 version:
btn.setOnAction((ActionEvent e) -> {moves++;});
(I can't currently test this, but it should work. If not, lemme know and I'll try to fix it.)
If you're concerned about memory, Christian points out below that this creates a new instance of EventHandler for every button. While this may not be too terrible, it's probably a bad habit to get into. The best way to handle (no pun intended) this is by making an object before your for loop:
EventHandler<ActionEvent> eh = new EventHandler<>() {
#Override public void handle(ActionEvent event) {
moves++;
}
};
Then, for each of your buttons, instead of the top-most code, you'd simply write:
btn.setOnAction(eh);
That way, a single EventHandler is being created and used to handle all the events. You'll want to use this one if you need to create more than just a few buttons, both because it's faster (doesn't need to allocate memory for each object) and more memory-efficient (...it, uh, doesn't need to allocate the memory for each object). In this case, I think it's pretty trivial, but it's good to know nonetheless.
You need to implement the ActionListener interface in your class.
Then in the actionPerformed function, just increment the moves variable.
You just have to call btn.addActionListener(this); for each of the button that
you created.
public class Sample extends JFrame implements ActionListener {
public Sample ()
{
}
private GridPane makeGridPane()
{
ConcentrationModel c = new ConcentrationModel();
GridPane grid = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth( 50 );
grid.getColumnConstraints().addAll(col1, col1, col1, col1);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight( 50 );
grid.getRowConstraints().addAll(row1, row1, row1, row1);
for(int row = 0; row < 4; row ++){
for(int col = 0; col < 4; col++){
Button btn = new Button();
ImageView image = new ImageView(c.getCards().get(0).getImage());
image.setFitWidth(WIDTH/4);
image.setFitHeight(HEIGHT/4);
btn.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btn.setGraphic(image);
btn.addActionListener(this);
grid.add(btn, col, row);
}
}
return grid;
}
public void actionPerformed(ActionEvent e)
{
moves++;
}
}
I want to make a grid with a specific amount of buttons.
I know how many buttons there are need to be because I get the number of rows and columns.
I could do a loop, but I don't know how you can place buttons next to eachother and underneath.
Secondly, the buttons need a Text and an Id, text is no problem, but how do you give them an id?
And at last, and probably most difficult, it can occur that there are a lot of rows, so that a scrollbar should be available.
At the end it should look something like this:
#Override
public void start(Stage stage) {
GridPane grid = new GridPane();
grid.setPadding(new Insets(BUTTON_PADDING));
grid.setHgap(BUTTON_PADDING);
grid.setVgap(BUTTON_PADDING);
for (int r = 0; r < NUM_BUTTON_LINES; r++) {
for (int c = 0; c < BUTTONS_PER_LINE; c++) {
int number = NUM_BUTTON_LINES * r + c;
Button button = new Button(String.valueOf(number));
grid.add(button, c, r);
}
}
ScrollPane scrollPane = new ScrollPane(grid);
stage.setScene(new Scene(scrollPane));
stage.show();
}
The best solution would be:
itemNumber starts from 0 to N:
Grid.getChildren().get(itemNumber).setId("bt"+itemNumber);
Grid.getChildren().get(itemNumber).getId();
I want to create a program that can simulate the influence of a social structure of a matrix / grid. It's like an Erdos Renyi structure, but an nxn matrix where each node initially has a random value of +1 or -1. Each node has 4 neighbors (top, bottom, left, right) which also has values of +1 or -1. Thus, I can find the neighborhood of each node, which should be just a list / vector of the 4 values of those neighbors. The value of each node is then influenced by the neighbors' values, and the grid's values keep simulating and changing until an equilibrium is reached.
So far, I have a code which can create a grid with random -1 or +1 variables. Now, I am not sure how to get the values of each node (and its neighbors) inside the main method. I am a little confused as to how to go about this project so far, as in how to get the values.
In the main method, I cannot do like... int x = grid[0][1] etc. Sorry that I'm rusty in Java, but what topics can I look up in finding out the procedure to do this? I can't remember what it's called.
Also, is there a way that I can edit what each button (or node) on the grid says in the main method? Like, I can change it while it's looping, and I can also write what the values of its neighbors are? Thanks!
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.GridLayout;
public class Grid {
JFrame frame = new JFrame(); // create frame
static JButton[][] grid;
// constructor
public Grid (int w, int l) {
frame.setLayout(new GridLayout(w, l));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
// create grid
grid = new JButton[w][l];
for (int y = 0; y < l; y++) {
int value = 0;
for (int x = 0; x < w; x++) {
// probability p
double p = 0.5;
// randomly allocate opinion
double random = Math.random();
if (random <= p)
value = -1;
else if (random > p)
value = 1;
grid[x][y] = new JButton("" + value);
frame.add(grid[x][y]);
}
}
}
public static void main (String[] args) {
// DIMENSION
// int d = 2;
// LENGTH
int l = 2;
// WIDTH
int w = 2;
new Grid(l,w); // create new Grid with parameters
}
}
I changed your approach a little bit, so it is more object-oriented. I hope this example makes clear, what you have to do. To get an basic understanding, google keywords like "encapsulation" or "object-orientation". There are many tutorials out there.
public class Grid {
private JFrame frame = new JFrame();
private JButton[][] grid; // Has to be a field of the object. static would make it to an field of the class
public Grid (int w, int l) {
frame.setLayout(new GridLayout(w, l));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
createGrid (w, l);
}
private void createGrid (int w, int l) {
// Your above implementation
}
public void stepInSimulation () {
// Your algorithm, you want to implement
}
public JButton[][] getGrid () {
return grid;
}
public static void main (String[] args) {
Grid grid = new Grid(2,2);
grid.getGrid ()[0][1]; // Access a value
grid.stepInSimulation (); // Do a step, call it in while loop, to do multiple steps
}
}
I hope this helps.