Trying to add children to a GridPane with row and column indices. They show up in the gui, but using GridPane.getColumnIndex(node) always returns null.
GridPane grid = new GridPane();
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
grid.setGridLinesVisible(true);
Button myBtn = new Button("foo");
GridPane.setColumnIndex(myButton,i);
GridPane.setRowIndex(myButton,j);
grid.getChildren().add(myButton);
}
When I try to use this algorithm I got from the answer on javafx GridPane retrive specific Cell content, the program crashes with a NullPointerException.
private Node getNodeFromGridPane(GridPane gridPane, int col, int row)
{
for (Node node : gridPane.getChildren()) {
if (GridPane.getColumnIndex(node) == col && GridPane.getRowIndex(node) == row) {
return node;
}
}
return null;
}
Why are GridPane.getRowIndex() and GridPane.getColumnIndex() returning null even after calling setColumnIndex() and setRowIndex()?
So it seems I got it to work if you run it you will see that all the buttons are red white or blue this is because they were found
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
grid.setGridLinesVisible(true);
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
grid.add(new Button("foo"),i,j);
// Button myBtn = new Button("foo");
// GridPane.setColumnIndex(myBtn, i);
// GridPane.setRowIndex(myBtn, j);
// grid.getChildren().add(myBtn);
}
}
Scene scene = new Scene(grid);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
String[] colors = {"-fx-background-color: red;", "-fx-background-color: blue;", "-fx-background-color: white;"};
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
Node node = getNodeFromGridPane(grid,i,j);
if(node instanceof Button)
node.setStyle(colors[(int) (Math.random() * 3)]);
}
}
}
private Node getNodeFromGridPane(GridPane gridPane, int col, int row) {
for (Node node : gridPane.getChildren())
if (GridPane.getColumnIndex(node) != null
&& GridPane.getColumnIndex(node) != null
&& GridPane.getRowIndex(node) == row
&& GridPane.getColumnIndex(node) == col)
return node;
return null;
}
public static void main(String[] args) { launch(args); }
}
Related
I am trying to make a snake game using maven project on JavaFX. In my code snake was moving but the previous coordinates are not cleared on the scene. Are there any solution for this? Here is my code:
public class Main extends Application {
int sceneX = 600;
int sceneY = 400;
ArrayList <Point> snake = new ArrayList<>();
private GraphicsContext gc;
private void run(GraphicsContext gc) {
drawSnake(gc);
for (int i = snake.size()-1; i>=1; i--) {
snake.get(i).x = snake.get(i - 1).x;
snake.get(i).y = snake.get(i - 1).y;
}
snake.get(0).x++;
}
private void drawSnake(GraphicsContext gc) {
for (int i =0; i<3; i++) {
gc.fillRoundRect(snake.get(i).getX(), snake.get(i).getY(),5,5,3,3);
}
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SNAKE");
Group root = new Group();
Canvas canvas = new Canvas(sceneX, sceneY);
root.getChildren().add(canvas);
Scene scene = new Scene (root);
primaryStage.setScene(scene);
primaryStage.show();
gc = canvas.getGraphicsContext2D();
for (int i = 0; i<3; i++) {
snake.add(new Point(300, 300));
}
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(130), e -> run(gc)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
I agree with what #jewelsea mentioned in the comments.
If you don't have very complex drawings/painting on the canvas, I think using scenegraph approach should make things simple.
If you opt for canvas, one possible drawback I can think of is : you need to redraw the entire canvas. Because clearing only part of the rectangle will not be a solution if you have some layered drawings (like background.. etc)
private void run(GraphicsContext gc) {
for (int i = 0; i < snake.size(); i++) {
gc.clearRect(snake.get(i).x, snake.get(i).y, size, size);
}
int lastIndex = snake.size() - 1;
for (int i = 0; i < lastIndex; i++) {
snake.get(i).x = snake.get(i + 1).x;
}
snake.get(lastIndex).x += size;
drawSnake(gc);
}
In the below gif for canvas approach, clearing the snake alone is not sufficient if I have some background.
On the other hand, if I use scenegraph approach, once we include the building blocks of the snake, all we need to do is to update the position of the building blocks.
private void run() {
int lastIndex = shapes.size() - 1;
for (int i = 0; i < lastIndex; i++) {
shapes.get(i).setLayoutX(shapes.get(i + 1).getLayoutX());
}
shapes.get(lastIndex).setLayoutX(shapes.get(lastIndex).getLayoutX() + size);
}
Below is the complete demo differentiating the two approaches:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
public class SnakeGameDemo extends Application {
int width = 300;
int height = 200;
int startX = 100;
int startY = 100;
int size = 5;
int speed = 250;
List<Point> snake = new ArrayList<>();
List<Rectangle> shapes = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SNAKE");
GridPane root = new GridPane();
root.setPadding(new Insets(10));
root.setHgap(10);
root.setVgap(10);
root.addRow(0, new Label("Canvas"), new Label("SceneGraph"));
root.addRow(1, canvasApproach(), scenegraphApproach());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node canvasApproach() {
Canvas canvas = new Canvas(width, height);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.RED);
gc.fillRoundRect(0, 0, width, height, 0, 0);
gc.setFill(Color.GREEN);
for (int i = 0; i < 3; i++) {
snake.add(new Point(startX + (i * size), startY));
}
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(speed), e -> run(gc)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return canvas;
}
private void run(GraphicsContext gc) {
for (int i = 0; i < snake.size(); i++) {
gc.clearRect(snake.get(i).x, snake.get(i).y, size, size);
}
int lastIndex = snake.size() - 1;
for (int i = 0; i < lastIndex; i++) {
snake.get(i).x = snake.get(i + 1).x;
}
snake.get(lastIndex).x += size;
drawSnake(gc);
}
private void drawSnake(GraphicsContext gc) {
snake.forEach(block -> {
gc.fillRoundRect(block.getX(), block.getY(), size, size, 3, 3);
});
}
private Node scenegraphApproach() {
Pane pane = new Pane();
pane.setStyle("-fx-background-color:red;");
pane.setPrefSize(width, height);
for (int i = 0; i < 3; i++) {
Rectangle block = new Rectangle(size, size, Color.GREEN);
// Position the shapes in the Pane using the layoutX/Y properties.
block.setLayoutX(startX + (i * size));
block.setLayoutY(startY);
pane.getChildren().add(block);
shapes.add(block);
}
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(speed), e -> run()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return pane;
}
private void run() {
int lastIndex = shapes.size() - 1;
for (int i = 0; i < lastIndex; i++) {
shapes.get(i).setLayoutX(shapes.get(i + 1).getLayoutX());
}
shapes.get(lastIndex).setLayoutX(shapes.get(lastIndex).getLayoutX() + size);
}
}
package com.example.project;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import java.net.URL;
import java.util.ResourceBundle;
public class Scene2Controller implements Initializable {
#FXML
private ComboBox boardSizeCombo;
#FXML
private GridPane grid;
#FXML
private ComboBox colourCombo;
#Override
public void initialize(URL url, ResourceBundle rb) {
colourCombo.getItems().add("Red and White");
colourCombo.getItems().add("Orange and White");
boardSizeCombo.getItems().add("9x9");
boardSizeCombo.getItems().add("10x10");
boardSizeCombo.setOnAction((actionEvent ->{
int selectedIndex = boardSizeCombo.getSelectionModel().getSelectedIndex();
if (selectedIndex == 0){
int count = 0;
double s = 100; // side of rectangle
for (int i = 0; i < 8; i++) {
count++;
for (int j = 0; j < 8; j++) {
Rectangle r = new Rectangle(s, s, s, s);
if (count % 2 == 0)
r.setFill(Color.WHITE);
grid.add(r, j, i);
count++;
}
}
}
if (selectedIndex == 1){
int count = 0;
double s = 100; // side of rectangle
for (int i = 0; i < 8; i++) {
count++;
for (int j = 0; j < 8; j++) {
Rectangle r = new Rectangle(s, s, s, s);
if (count % 2 == 0)
r.setFill(Color.BLUE);
grid.add(r, j, i);
count++;
}
}
}
}
));
}
}
The user selects a option from the comboBox. In this case, the user selects a size for the board and then whatever size the user has selected, the outcome will be displayed on the gridpane. However, it is doing what is screenshotted. Can someone help with the constraints please. Thanks
You need to fix your loops.
E.g. for a 9x9 grid
for (int i = 0; i < 8; i++) {
That loops from 0 to 7 ... a total of 8 iterations, not 9. For the 10x10 grid you seem to have the same loop.
Here is a basic solution:
import javafx.scene.control.ComboBox;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.application.Application;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class GridExample extends Application {
private ComboBox boardSizeCombo;
private GridPane grid;
private ComboBox colourCombo;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
grid = new GridPane();
colourCombo = new ComboBox();
boardSizeCombo = new ComboBox();
colourCombo.getItems().add("Red and White");
colourCombo.getItems().add("Orange and White");
colourCombo.getSelectionModel().select(0);
colourCombo.setOnAction(this::createBoard);
boardSizeCombo.getItems().add("9x9");
boardSizeCombo.getItems().add("10x10");
boardSizeCombo.getSelectionModel().select(0);
boardSizeCombo.setOnAction(this::createBoard);
createBoard(null);
HBox topPane = new HBox(new Label("Try it..."));
VBox sidePane = new VBox(8, colourCombo, boardSizeCombo);
BorderPane root = new BorderPane(grid, topPane, null, null, sidePane);
root.setPadding(new Insets(8));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setMinWidth(700);
primaryStage.setMinHeight(600);
primaryStage.show();
}
void createBoard(Event e) {
int colourIndex = colourCombo.getSelectionModel().getSelectedIndex();
int sizeIndex = boardSizeCombo.getSelectionModel().getSelectedIndex();
int n = sizeIndex == 0 ? 9 : 10;
Color baseColour = colourIndex == 0 ? Color.RED : Color.ORANGE;
grid.getChildren().clear();
double s = 50; // side of rectangle
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Rectangle r = new Rectangle(s, s, s, s);
r.setFill((((i ^ j) & 1) == 0) ? Color.WHITE : baseColour);
grid.add(r, j, i);
}
}
}
}
Does anyone know why transparency drawing on a Canvas works perfectly fine using drawImage(), but doesn't work at all with a PixelWriter? I initially thought it may have something to do with Blend or some other mode/setting on the canvas/context, but haven't had any luck with that yet.
I need per-pixel variable transparency, not a single transparency value for the entire draw operation. I'll be rendering a number of "layers" (similar to how GIMP layers work, with optional transparency per-pixel). An additional open question is whether I'm better off first drawing the FINAL intended output to a WritableImage and then just drawing to the Canvas, for performance reasons, but that seems to defeat the point of using a Canvas in the first place...
Below is an example which shows a partially transparent Color being first drawn to an Image and then to the Canvas, and directly to the Canvas with setColor(). The transparent area is the Image draw, the opaque area is the setColor part. How do we get setColor() to respect Color alpha transparency for each pixel?
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
public class TransparencyTest extends Application {
private static final int width = 800;
private static final int height = 600;
private Scene scene;
private final Canvas canvas = new Canvas(width, height);
#Override
public void start(Stage stage) {
scene = new Scene(new Group(canvas));
stage.setScene(scene);
render();
stage.show();
exitOnEsc();
}
private void render() {
drawTransparentBg(canvas, 0, 0, width, height);
Color color = Color.web("#77000077");
WritableImage image = new WritableImage(200, 200);
for (int x = 0; x < 200; x++) {
for (int y = 0; y < 200; y++) {
image.getPixelWriter().setColor(x, y, color);
}
}
canvas.getGraphicsContext2D().drawImage(image, 50, 50);
for (int x = 0; x < 50; x++) {
for (int y = 0; y < 50; y++) {
canvas.getGraphicsContext2D().getPixelWriter().setColor(x, y, color);
}
}
}
public void drawTransparentBg(Canvas canvas, int xPos, int yPos, int width, int height) {
int gridSize = 8;
boolean darkX = true;
String darkCol = "#111111";
String lightCol = "#222266";
for (int x = xPos; x < canvas.getWidth(); x += gridSize) {
boolean dark = darkX;
darkX = !darkX;
if (x > width) {
break;
}
for (int y = yPos; y < canvas.getHeight(); y += gridSize) {
if (y > height) {
break;
}
dark = !dark;
String color;
if (dark) {
color = darkCol;
} else {
color = lightCol;
}
canvas.getGraphicsContext2D().setFill(Paint.valueOf(color));
canvas.getGraphicsContext2D().fillRect(x, y, gridSize, gridSize);
}
}
}
private void exitOnEsc() {
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode().equals(KeyCode.ESCAPE)) {
Platform.exit();
}
});
}
}
The GraphicsContext begins with the default BlendMode, and all forms of drawImage() use the current mode. In contrast, PixelWriter methods replace values, ignoring the BlendMode.
The example below lets you experiment with the supported BlendMode values to see the effect. Related examples are shown here and here.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ChoiceBox;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class TransparencyTest extends Application {
private static final int S = 8;
private static final int W = S * 36;
private static final int H = W;
private final Canvas canvas = new Canvas(W, H);
private final GraphicsContext g = canvas.getGraphicsContext2D();
private BlendMode mode = g.getGlobalBlendMode();
#Override
public void start(Stage stage) {
render(mode);
BorderPane root = new BorderPane(new Pane(canvas));
ObservableList<BlendMode> modes
= FXCollections.observableArrayList(BlendMode.values());
ChoiceBox<BlendMode> cb = new ChoiceBox<>(modes);
cb.setValue(mode);
cb.valueProperty().addListener((o) -> {
render(cb.getValue());
});
root.setBottom(cb);
stage.setScene(new Scene(root));
stage.show();
}
private void render(BlendMode mode) {
drawBackground();
g.setGlobalBlendMode(mode);
Color color = Color.web("#7f00007f");
int s = 24 * 8;
WritableImage image = new WritableImage(s, s);
for (int x = 0; x < s; x++) {
for (int y = 0; y < s; y++) {
image.getPixelWriter().setColor(x, y, color);
}
}
s = 6 * 8;
g.drawImage(image, s, s);
for (int x = 0; x < s; x++) {
for (int y = 0; y < s; y++) {
g.getPixelWriter().setColor(x, y, color);
}
}
}
public void drawBackground() {
g.setGlobalBlendMode(BlendMode.SRC_OVER);
Color darkCol = Color.web("#333333");
Color lightCol = Color.web("#cccccc");
boolean dark = false;
for (int x = 0; x < W; x += S) {
dark = !dark;
for (int y = 0; y < H; y += S) {
dark = !dark;
g.setFill(dark ? darkCol : lightCol);
g.fillRect(x, y, S, S);
}
}
}
public static void main(String[] args) {
launch(args);
}
}
i wanna create pyramid left,right,center angle, using Circle shape in GridPane, i have already done with the left angle, but spent 2 days in creating right and center angle with no luck.
i will be thankful if anyone knows or can give me some algo ideas please help me!
Output already done left angle
With the following code
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j <= i; j++) {
Circle circle = new Circle();
circle.setStroke(Paint.valueOf(Color.BLACK.toString()));
circle.radiusProperty().bind(ballsModel.radiusProperty());
circle.strokeWidthProperty().bind(ballsModel.strokeProperty());
circle.fillProperty().bind(Bindings.createObjectBinding(() -> Paint.valueOf(ballsModel.getColor().name())));
grid.addRow(i, circle);
}
}
Need to figure out following patterns:
center angle
right angle
an example for the pyramid part
package pyramid;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Pyramid extends Application {
void createPyramid(){
gridPane.setHgap(5);
gridPane.setVgap(5);
int center = 4 ;
for (int row = 0; row <= 4; row++ ) {
int range = (1 +(2* row));
int startColumn = center-range/2 ;
for(int i = 0 ; i<range; i++){
Circle circle = new Circle(20,javafx.scene.paint.Color.BLUE);
gridPane.add(circle,startColumn+i , row);
}
}}
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(gridPane);
this.createPyramid();
Scene scene = new Scene(root, 600, 600);
primaryStage.setTitle("Pyramid");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}}
I have a problem to draw this image (below link) I had his in the middle of flow pane but only half of this! How to draw full image>?
http://s16.postimg.org/lrus832dx/Capture2.png
http://s27.postimg.org/hsujjp1s3/Capture1.png
Ok, I think I understand your problem now. I suggest you to use a GridPane instead, then you can do it this way:
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
int rows = 2;
int columns = 2;
int rectangleWidth = 50;
int rectangleHeight = 50;
root.setMaxSize(rectangleWidth*columns, rectangleHeight*rows);
for(int row = 0; row < rows; row++)
{
for(int col= 0; col< columns; col++)
{
Rectangle rectangle = new Rectangle(rectangleWidth, rectangleHeight);
root.add(rectangle, col, row);
if(row % 2 == 0)
{
rectangle.setFill(col % 2 == 0 ? Color.ORANGE : Color.BLACK);
}
else
{
rectangle.setFill(col % 2 != 0 ? Color.ORANGE : Color.BLACK);
}
}
}
Scene scene = new Scene(new StackPane(root), 700, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
We need to wrap the GridPane containing the Rectangles in a StackPane to center it in its parent.