How To Add Objects In Grid from Bottom Up? - java

Im using a grid pane view to populate a grid using the add() method. By design of the add() the grid populates each row from top to bottom where location (0,0) is at the very top left most of the grid. Adding more rows are then appended below it and so forth. Is it possible to populate my grid so that the first row is at the bottom and add rows upward so that the location (0,0) is located at the bottom left? What is needed to achieve this? Ive looked at the different methods within GridPane but could not find how this is done. My hunch is I need to override the add() method but Im not sure how to implement this behavior.
I would prefer not to mirror this since I am dealing with images.
Here is the gist of code extracted from classes and helper methods for simplicity:
public enum LawnType
{
GRASS,
CRATER
}
lawnData = new LawnType[][] {
{CRATER,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
};
GridPane lawnViewer = new GridPane();
for (int x = 0 ; x < data.length ; x++) {
for (int y = 0 ; y < data[x].length ; y++) {
ImageView imageView;
switch(data[x][y]){
case GRASS:
imageView = new ImageView(new Image("mower/resources/longgrass1.png"));
imageView.setFitWidth(gridPixelSize);
imageView.setFitHeight(gridPixelSize);
gridPane.add(imageView,x,y);
break;
case CRATER:
imageView = new ImageView(new Image("mower/resources/crater.png"));
imageView.setFitWidth(gridPixelSize);
imageView.setFitHeight(gridPixelSize);
gridPane.add(imageView, x, y);
break;
default:
break;
}
}
}
Output:

You can add each image at a row relative to the bottom row, which is data[x].length - 1.
Replace each of these:
gridPane.add(imageView, x, y);
with this:
gridPane.add(imageView, x, data[x].length - 1 - y)

Related

Why does javaFX circle.getCenterX not work in this case?

Right now I'm trying to code a simple abacus with JavaFX. There are balls on horizontal rails arranged in a gridpane, and they move on mouseClick from side to side. I'm trying to add labels to each ball from 10-1, but it seems that by simply adding the circles into a gridpane they don't get a CenterX/Y assigned. Since that's how I'm trying to position my text it doesn't show up at all. The relevant part of my code is:
// Stack Gridpane into root pane to organize circles on rails
GridPane grid = new GridPane();
// loop to instantiate all circles in a 5x10 grid, add to gridpane
for (int row = 0; row < 5; row++) {
for (int count = 10; count > 0; count--) {
Circle circle = new Circle() ;
circle.setRadius(20);
grid.add(circle,count,row);
// generate Labels and position on circle
Text label = new Text(Integer.toString(count));
label.getStyleClass().add("text");
label.setX(circle.getCenterX());
label.setY(circle.getCenterY());
//bind label to circle movement
label.translateXProperty().bind(circle.translateXProperty());
root.getChildren().add(label);
}
}
root.getChildren().add(grid);
When I add a System.out.println(label) I can see that the labels get generated properly, but the coordinates always stay at 0/0. The circles themselves get arranged in a grid the way they are supposed to though. Any help would be greatly appreciated!
There are some problems here that I see.
Firstly, setX() and setY() do not work when the node is inside some kind of Pane subclass that manages layout for you. This is because the layout manager will override this value when it's trying to layout its children.
Secondly, you are adding the label to root node, which is going to make all the Text gather at the same place.
This is what I would do, using StackPane, which allows you to stack multiple nodes at the same place. Furthermore, it automatically centers all its children.
// Stack Gridpane into root pane to organize circles on rails
GridPane grid = new GridPane();
// loop to instantiate all circles in a 5x10 grid, add to gridpane
for (int row = 0; row < 5; row++) {
for (int count = 10; count > 0; count--) {
StackPane sp = new StackPane();
grid.add(sp, count, row);
Circle circle = new Circle();
circle.setRadius(20);
// generate Labels and position on circle
Text label = new Text(Integer.toString(count));
label.getStyleClass().add("text");
label.setFill(Color.RED); // Added so it's visible
// Don't need these
//label.setX(circle.getCenterX());
//label.setY(circle.getCenterY());
//bind label to circle movement
//label.translateXProperty().bind(circle.translateXProperty());
//root.getChildren().add(label);
// Add circle and label to StackPane so it'll handle layout for you
sp.getChildren().addAll(circle, label);
}
}
root.getChildren().add(grid);
From what I see, you do have some translation intended. That would be a little tricky to do, depending on what exactly you need.

JavaFX pannable GridPane

For my current project, I need a large even grid with a transparent background that one can zoom and pan across. I initially tried to use a ScrollPane with its setPannable(true), but soon discovered that a scrollpane background will remain opague even when you call setStyle("-fx-background-color: transparent") or setStyle("-fx-background: transparent"). So with that easy option eliminated, I need to make the GridPane in question directly pannable.
I've tried several things already, including trying to bind the mouse position to the GridPane's Translate property(which quite simply didn't work), and I failed to get the more promising alternative to work properly:
GridPane gridView = new GridPane();
int x;
int y;
gridView.setOnDragEntered( (event) -> {
x = event.getX();
y = event.getY();
});
gridView.setOnDragDetected( (event) -> {
gridView.setTranslateX(X - event.getX());
gridView.setTranslateY(Y - event.getY());
});
However, this only makes the map jump up and to the left, rather than to make it slowly pan with the mouse as intended.
Managed to mostly figure it out. The one remaining problem is that the GridPane is rather jittery when it is panned, but it's at an acceptable level for now.
int x = 0;
int y = 0;
gridView.setOnMouseDragged((event) -> {
if(x != 0){ //prevent from panning before values are initialized
gridView.setTranslateX( gridView.getTranslateX() + limit(event.getX() - x, 25));
gridView.setTranslateY( gridView.getTranslateY() + limit(event.getY() - y, 25));
}
x = (int)event.getX();
y = (int)event.getY();
});
Where the Limit(double num, double limit) method limits the input num to the range [-limit, limit]. Reducing the limit decreases the jitter, but it does so at the expense of decreasing responsiveness as well.

Possible to Drag Shapes Within a GridPane with JavaFX

Am I correct in thinking it is not possible to drag Shapes within a GridPane? Here's a link to some code that allows the user to drag shapes around the screen: Drag and Drop Shapes
I want my shapes to have the same behavior as above, but I want them in a GridPane (eventually I'd like their movement to be locked to the X or Y axis to be moved on to adjacent shapes).
I added the below code to the "start" method. It creates circles using the same method as the example code but instead adds them to a grid. Surprisingly, this removed the ability for them to be dragged around.
GridPane grid = new GridPane();
grid.setLayoutX(300);
grid.setLayoutY(100);
int n = 3;
int m = 3;
for (int r = 0; r < n; r++) {
for (int c = 0; c < m; c++) {
Circle circle = createCircle(100, 50, 30, Color.BLACK);
grid.add(circle, c, r);
}
}
root.getChildren().add(grid);
If you want to test this just add the above code to the "start" method of the example code, just above these lines:
primaryStage.setScene(scene);
primaryStage.show();
My theory is the GridPane, because it locks the circles to certain positions, doesn't allow this dragging behavior.
Any input on how I can achieve movement of the circles along the X and Y axis when dragged?
In general, layout panes such as GridPane manage the placement of their content. Changing the layout coordinates will not affect nodes that are placed in these panes. You may find it better to use a plain Pane and manage the layout yourself for functionality such as this.
If you do want to use a GridPane, transformations (such as translations, etc) are applied after layout coordinates are computed, so you can use a translation (e.g. the one built-in with the translateX and translateY properties) to manage dragging in a layout pane.
So you can do:
circle.setOnMouseDragged((t) -> {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
// No idea why they are doing this. c is just circle
Circle c = (Circle) (t.getSource());
c.setTranslateX(c.getTranslateX() + offsetX);
c.setTranslateY(c.getTranslateY() + offsetY);
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
});

Generating an N x N grid

What is the most painless way to create an N x N grid in a JavaFX application?
The only requirements I'm looking for is that the size of the grid will always take up the same amount of space, so more squares = smaller squares. I can set colors for the squares, and I can hover over each square individually and be able to show some for each square.
I won't know 'N' until the program runs and parses through some data to figure out how many total squares I need which is when I calculate the smallest NxN grid I can use.
From what I can tell my options are:
GridPane - Using the column constraints and row constraints to generate size and possibly add properties for hovering?
TableView - A lot more options for being able to give each cell data to show when hovered over but still difficult to just add rows and columns to start with.
Rectangles - Just generate and draw each rectangle while calculating the x and y coordinates for each square. This will make it easy to do the colors and hovering but I can't see how resizing would work but I'm ok with having a specific size for my application. I'll also have to calculate the best size to make each square to fill up the grids area.
I'm not necessarily looking for someone to code a solution, but if someone has dealt with this and knows a good way I'd like to hear about it.
Don't stray away from the original ideas. Why are you looking for "painless" ways when all the methods you've given are all viable? Here's one using your rectangles. The GridMaker.SCREEN_SIZE refers to the size of the screen you must have.
public static Pane makeGrid(int n){
double width = GridMaker.SCREEN_SIZE/n;
Pane p = new Pane();
Rectangle [][] rec = new Rectangle [n][n];
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
rec[i][j] = new Rectangle();
rec[i][j].setX(i * width);
rec[i][j].setY(j * width);
rec[i][j].setWidth(width);
rec[i][j].setHeight(width);
rec[i][j].setFill(null);
rec[i][j].setStroke(Color.BLACK);
p.getChildren().add(rec[i][j]);
}
}
return p;
}
Then simply add the mouse listener to the pane if you wish to make it change color.
p.setOnMouseClicked(new EventHandler <MouseEvent> (){
#Override
public void handle(MouseEvent me){
double posX = me.getX();
double posY = me.getY();
int colX = (int)(posX / width);
int colY = (int) (posY / width);
rec[colX][colY].setFill(Color.RED);
}
});
-- Edit
1)
2) For Hover, what kind of hover effects are you looking for? You can add Hover effects onto each rectangles, if you want me to show you how, I can definitely code it for you.

Android - Insert one image into another's transparent pixels

In Android, I'm attempting to layer two images (In this case, Bitmap objects) in a FrameLayout. One image is placed on top, with the other image inserted below it at that image's first transparent pixel.
Currently, I'm attempting the following using Bitmap.getPixel() to locate the transparent pixel:
for (int i = 0; i < image_count; i++)
{
//load page from internal memory
topImages[i] = getImage("p"+i+".png");
for (int x = 0; x < topImages[i].getWidth(); x++)
{
for (int y = 0; y < topImages[i].getHeight(); y++)
{
if (topImages[i].getPixel(x, y) == Color.TRANSPARENT)
{
bottomX[i] = x;
bottomY[i] = y;
break;
}
}
}
}
I then add the images to a Frame Layout, using two ImageViews, and use these values to position the lower image:
bottomView.setTranslationX(bottomX[position]);
bottomView.setTranslationY(bottomY[position]);
However, the placement of the bottom image is always inaccurate, usually by a margin down and to the right that is different (but always the same) for each given image. I've also tried setX() and setY(), as well as using parameters to set the TOP and LEFT values similarly, with the same results). What can I do to ensure that the placement is always consistent, with the image's top-left at (or close to) the first transparent pixel?
If you want to only display the images, you can add them as different frames in FrameLayout. In your xml, first declare the one you want to show as transparent pixels, then declare the one which will have transparent pixels.

Categories