reverse multiplication table javafx - java

for this application, I need to enter a number and display the numbers that when multiplied together will give you the number in question. For example, if you enter 42 then the labels for 6*7 and 7*6 would change color. I figured out how to get the answers but I cant quite figure out how to manipulate the labels in the multiplication table to change color. To give you an idea,
main class
package application;
import java.util.List;
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane pane = new BorderPane();
pane.setTop(getHbox1());
HBox prompt = new HBox(15);
prompt.setPadding(new Insets(15, 15, 15, 15));
prompt.setAlignment(Pos.TOP_CENTER);
prompt.getStyleClass().add("hbox2");
Label lblProblem = new Label("Enter problem: ");
prompt.getChildren().add(lblProblem);
TextField tfProblem = new TextField();
prompt.getChildren().add(tfProblem);
Button btnFindAnswer = new Button("Find answers");
btnFindAnswer.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
int x = showFactors(tfProblem);
}
});
prompt.getChildren().add(btnFindAnswer);
pane.setCenter(prompt);
pane.setBottom(setUpGrid());
Scene scene = new Scene(pane, 550, 650);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("lab 7");
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox getHbox1() {
HBox hbox = new HBox(15);
hbox.setPadding(new Insets(15, 15, 15, 15));
hbox.setAlignment(Pos.TOP_CENTER);
hbox.getStyleClass().add("hbox1");
Label lblProblem = new Label("Reverse Multiplication Table");
hbox.getChildren().add(lblProblem);
return hbox;
}
public GridPane setUpGrid() {
GridPane pane = new GridPane();
Label[][] labels = new Label[11][11];
for (int row = 0; row < 11; row++)
for (int col = 0; col < 11; col++) {
Label l = new Label();
setUpLabel(l, col, row);
labels[row][col] = l;
pane.add(l, col, row);
}
return pane;
}
public void setUpLabel(final Label l, final int col, final int row) {
l.setPrefHeight(50);
l.setPrefWidth(50);
l.setAlignment(Pos.CENTER);
l.setStyle("-fx-stroke-border: black; -fx-border-width: 1;");
String a = String.valueOf(row);
String b = String.valueOf(col);
if (row == 0 || col == 0) {
l.getStyleClass().add("gridBorders");
if(row == 0)
l.setText(b);
else if (col == 0)
l.setText(a);
} else {
l.setText(a + " * " + b);
l.getStyleClass().add("gridInside");
}
}
public int showFactors(TextField problem) {
FactorCalculator calc = new FactorCalculator();
int number = Integer.parseInt(problem.getText());
List<Integer> factors = calc.findFactor(number);
for(int i = 0; i < factors.size() - 1; i++) {
return factors.get(i);
}
return 0;
}
public static void main(String[] args) {
launch(args);
}
}
factorCalculator class
package application;
import java.util.ArrayList;
import java.util.List;
public class FactorCalculator {
public List<Integer> list = new ArrayList<Integer>();
public List<Integer> findFactor(int problem) {
int incrementer = 1;
if(problem % 2 != 0) {
incrementer = 2;
}
while(incrementer <= problem) {
if(problem % incrementer == 0) {
list.add(incrementer);
}
incrementer++;
}
return list;
}
}
application css
{
-fx-text-alignment: center;
}
.hbox1 {
-fx-background-color: gray;
}
.hbox2 {
-fx-background-color: white;
}
.gridBorders {
-fx-background-color: gray;
-fx-text-fill:#A3FF47;
-fx-border-style: solid;
-fx-border-width: 1;
-fx-stroke-border: black;
}
.gridInside {
-fx-background-color: red;
-fx-text-fill: white;
-fx-border-style: solid;
-fx-border-width: 1;
-fx-stroke-border: black;
}
.gridAnswer {
-fx-background-color: white;
-fx-text-fill: black;
}

Just use your style "gridAnswer" and set it
l.getStyleClass().add( "gridAnswer");
or remove it
l.getStyleClass().remove( "gridAnswer");
depending on your needs.
Edit: May I suggest a different approach?
Just create a custom cell which has all the information you need. Something like this:
private class AnswerCell extends Label {
int a;
int b;
int value;
public AnswerCell( int a, int b) {
this.a = a;
this.b = b;
this.value = a * b;
setText( a + " * " + b);
}
public boolean matches( int matchValue) {
return value == matchValue;
}
public void highlight() {
getStyleClass().add( "gridAnswer");
}
public void unhighlight() {
getStyleClass().remove( "gridAnswer");
}
}
In your setup method you simply add the cells and put them into a global list:
List<AnswerCell> answerCells = new ArrayList<>();
And to find the answers you do this:
for( AnswerCell cell: answerCells) {
cell.unhighlight();
}
for( AnswerCell cell: answerCells) {
if( cell.matches(number)) {
cell.highlight();
}
}

ReverseMultiplication.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package reversemultiplication;
import java.util.List;
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
/**
*
* #author reegan
*/
public class ReverseMultiplication extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane pane = new BorderPane();
pane.setTop(getHbox1());
HBox prompt = new HBox(15);
prompt.setPadding(new Insets(15, 15, 15, 15));
prompt.setAlignment(Pos.TOP_CENTER);
prompt.getStyleClass().add("hbox2");
Label lblProblem = new Label("Enter problem: ");
prompt.getChildren().add(lblProblem);
TextField tfProblem = new TextField();
prompt.getChildren().add(tfProblem);
GridPane gridPane = setUpGrid();
GridpaneHelper gh = new GridpaneHelper(gridPane);
Button btnFindAnswer = new Button("Find answers");
btnFindAnswer.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
List<int[]> x = showFactors(tfProblem);
for (int[] x1 : x) {
Node node = gh.getChildren()[x1[0]][x1[1]];
node.setStyle("-fx-background-color: green");
}
}
});
prompt.getChildren().add(btnFindAnswer);
pane.setCenter(prompt);
pane.setBottom(gridPane);
Scene scene = new Scene(pane, 550, 650);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("lab 7");
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox getHbox1() {
HBox hbox = new HBox(15);
hbox.setPadding(new Insets(15, 15, 15, 15));
hbox.setAlignment(Pos.TOP_CENTER);
hbox.getStyleClass().add("hbox1");
Label lblProblem = new Label("Reverse Multiplication Table");
hbox.getChildren().add(lblProblem);
return hbox;
}
public GridPane setUpGrid() {
GridPane pane = new GridPane();
Label[][] labels = new Label[11][11];
for (int row = 0; row < 11; row++) {
for (int col = 0; col < 11; col++) {
Label l = new Label();
setUpLabel(l, col, row);
labels[row][col] = l;
pane.add(l, col, row);
}
}
return pane;
}
public void setUpLabel(final Label l, final int col, final int row) {
l.setPrefHeight(50);
l.setPrefWidth(50);
l.setAlignment(Pos.CENTER);
l.setStyle("-fx-stroke-border: black; -fx-border-width: 1;");
String a = String.valueOf(row);
String b = String.valueOf(col);
if (row == 0 || col == 0) {
l.getStyleClass().add("gridBorders");
if (row == 0) {
l.setText(b);
} else if (col == 0) {
l.setText(a);
}
} else {
l.setText(a + " * " + b);
l.getStyleClass().add("gridInside");
}
}
public List<int[]> showFactors(TextField problem) {
FactorCalculator calc = new FactorCalculator();
int number = Integer.parseInt(problem.getText());
System.out.println(number);
List<int[]> factors = calc.findFactor(number, 10);
System.out.println(factors);
return factors;
}
public static void main(String[] args) {
launch(args);
}
}
GridpaneHelper.java help gridpane node access.
package reversemultiplication;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;
public class GridpaneHelper {
GridPane gridPane;
public GridpaneHelper(GridPane gridPane) {
this.gridPane = gridPane;
}
private int size() {
return gridPane.getChildren().size();
}
public int getColumnSize() {
int numRows = gridPane.getRowConstraints().size();
for (int i = 0; i < gridPane.getChildren().size(); i++) {
Node child = gridPane.getChildren().get(i);
if (child.isManaged()) {
int columnIndex = GridPane.getColumnIndex(child);
int columnEnd = GridPane.getColumnIndex(child);
numRows = Math.max(numRows, (columnEnd != GridPane.REMAINING ? columnEnd : columnIndex) + 1);
}
}
return numRows;
}
public int getRowSize() {
int numRows = gridPane.getRowConstraints().size();
for (int i = 0; i < gridPane.getChildren().size(); i++) {
Node child = gridPane.getChildren().get(i);
if (child.isManaged()) {
int rowIndex = GridPane.getRowIndex(child);
int rowEnd = GridPane.getRowIndex(child);
numRows = Math.max(numRows, (rowEnd != GridPane.REMAINING ? rowEnd : rowIndex) + 1);
}
}
return numRows;
}
public Node[] getColumnChilds(int columnNo) {
if (columnNo < getRowSize()) {
return getChildren()[columnNo];
}
return null;
}
public Node[] getRowChilds(int rowNo) {
Node n[] = new Node[getRowSize()];
if (rowNo <= getRowSize()) {
for (int i = 0; i < getRowSize(); i++) {
n[i] = getColumnChilds(i)[rowNo];
}
return n;
}
return null;
}
public Node[] getChildRowVia() {
Node n[] = new Node[size()];
int col = getColumnSize();
int arrIncre = 0;
for (int i = 0; i < col; i++) {
for (Node n1 : getRowChilds(i)) {
if (n1 != null) {
n[arrIncre] = n1;
arrIncre++;
}
}
}
return n;
}
public Node[][] getChildren() {
Node[][] nodes = new Node[getRowSize()][getColumnSize()];
for (Node node : gridPane.getChildren()) {
int row = gridPane.getRowIndex(node);
int column = gridPane.getColumnIndex(node);
nodes[row][column] = node;
}
return nodes;
}
public Integer postion(Node node, Pos pos) {
if (node != null) {
switch (pos) {
case Row:
return gridPane.getRowIndex(node);
case Column:
return gridPane.getColumnIndex(node);
}
}
return null;
}
enum Pos {
Row,
Column;
}
}
One new method added to FactorCalculator.java class file this help to how many combination are made.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package reversemultiplication;
import java.util.ArrayList;
import java.util.List;
/**
*
* #author reegan
*/
class FactorCalculator {
public List<Integer> list = new ArrayList<Integer>();
private int problem = 0;
public List<int[]> findFactor(int problem, int limit) {
int incrementer = 1;
this.problem = problem;
while (incrementer <= limit) {
if (problem % incrementer == 0) {
list.add(incrementer);
}
incrementer++;
}
return combinational();
}
public List<int[]> combinational() {
List<int[]> arrays = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < list.size(); j++) {
if (list.get(i) * list.get(j) == problem) {
int[] inx = new int[2];
inx[0] = list.get(i);
inx[1] = list.get(j);
arrays.add(inx);
}
}
}
return arrays;
}
}
add your css file in your project ,but this example code not clear in previous set style.after i will tell this.

The answer from Roland/ItachiUchiha is good: here is another approach.
Define an IntegerProperty to hold the current value:
public class Main extends Application {
private final IntegerProperty value = new SimpleIntegerProperty();
// ...
}
Now just have each label observe the value:
public void setUpLabel(final Label l, final int col, final int row) {
value.addListener((obs, oldValue, newValue) -> {
if (col * row == newValue.intValue()) {
l.getStyleClass().add("gridAnswer");
} else {
l.getStyleClass().remove("gridAnswer");
}
});
// all previous code...
}
Finally, just set the value when the button is pressed:
btnFindAnswer.addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
// you probably don't need this any more:
int x = showFactors(tfProblem);
value.set(Integer.parseInt(tfProblem.getText()));
}
});

Related

JavaFX Tetris not working as intended

Can someone explain how to solve this? DeleteRows() method is not working as intended.
It's a Tetris game and I'm trying to delete rows. When the blocks move down, the blocks are still counted as 0 so new blocks go through them. But this occurs only in the top row of the moved blocks:
package application;
import java.util.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.input.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Tetris extends Application {
public static final int MOVE_AMOUNT = 25;
public static final int SIZE = 25;
public static int XLIMIT = SIZE * 10;
public static int YLIMIT = SIZE * 24;
public static int[][] GRID = new int[XLIMIT/SIZE][YLIMIT/SIZE];
private static Pane group = new Pane();
private static Shape object;
private static Scene scene = new Scene(group, XLIMIT, YLIMIT);
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) throws Exception {
for(int[] a: GRID){
Arrays.fill(a, 0);
}
for(int i = 0; i <= XLIMIT; i+= SIZE){
Line a = new Line(i, 0, i, YLIMIT);
group.getChildren().add(a);
}
for(int i = 0; i <= YLIMIT; i+= SIZE){
Line a = new Line(0, i, XLIMIT, i);
group.getChildren().add(a);
}
for(int i = 0; i <= YLIMIT; i+= SIZE){
Text a = new Text("" + i);
a.setY(i);
group.getChildren().add(a);
}
for(int i = SIZE; i < XLIMIT; i+= SIZE){
Text a = new Text("" + i);
a.setY(10);
a.setX(i);
group.getChildren().add(a);
}
Shape a = TetrisHolder.createRect();
group.getChildren().addAll(a.a, a.b, a.c, a.d);
moveOnKeyPress(scene, a.a, a.b, a.c, a.d);
object = a;
stage.setScene(scene);
stage.show();
Timer myTimer=new Timer();
TimerTask task =new TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable(){
public void run(){
CheckDown(object);
}
});
}
};
myTimer.schedule(task,0,300);
}
private void moveOnKeyPress(Scene scene, Rectangle rect, Rectangle rect2, Rectangle rect3, Rectangle rect4) {
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent event) {
Shape shape = new Shape(rect, rect2, rect3, rect4);
switch (event.getCode()) {
case RIGHT:
TetrisHolder.CheckRight(shape);
break;
case DOWN:
CheckDown(shape);
break;
case LEFT:
TetrisHolder.CheckLeft(shape);
break;
case UP:
//TetrisHolder.CheckTurn(shape);
break;
}
}
});
}
private void CheckTurn(Shape shape){
}
private void DeleteRows(Pane pane){
ArrayList<Node> rects = new ArrayList<Node>();
ArrayList<Integer> lines = new ArrayList<Integer>();
int full = 0;
for(int i = 0; i < GRID[0].length; i++){
for(int j = 0; j < GRID.length; j++){
if(GRID[j][i] == 1)
full++;
}
if(full == GRID.length)
lines.add(i/*+lines.size()*/);
full = 0;
}
for(Node node: pane.getChildren()) {
if(node instanceof Rectangle) {
rects.add(node);
}
}
if(lines.size() > 0)
do{
for(Node node: rects){
Rectangle a = (Rectangle)node;
if(a.getY() == lines.get(0)*SIZE){
GRID[(int)a.getX()/SIZE][(int)a.getY()/SIZE] = 0;
pane.getChildren().remove(node);
}
if(a.getY() < lines.get(0)*SIZE){
GRID[(int)a.getX()/SIZE][(int)a.getY()/SIZE] = 0;
a.setY(a.getY() + SIZE);
GRID[(int)a.getX()/SIZE][(int)a.getY()/SIZE] = 1;
}
}
lines.remove(0);
rects.clear();
for(Node node: pane.getChildren()) {
if(node instanceof Rectangle) {
rects.add(node);
}
}
} while(lines.size() > 0);
}
private void CheckDown(Shape shape){
if((shape.c.getY() == YLIMIT - SIZE) || checkA(shape) || checkB(shape) || checkC(shape) || checkD(shape)){
GRID[(int)shape.a.getX()/SIZE][(int)shape.a.getY()/SIZE] = 1;
GRID[(int)shape.b.getX()/SIZE][(int)shape.b.getY()/SIZE] = 1;
GRID[(int)shape.c.getX()/SIZE][(int)shape.c.getY()/SIZE] = 1;
GRID[(int)shape.d.getX()/SIZE][(int)shape.d.getY()/SIZE] = 1;
DeleteRows(group);
Shape a = TetrisHolder.createRect();
object = a;
group.getChildren().addAll(a.a, a.b, a.c, a.d);
moveOnKeyPress(shape.a.getScene(), a.a, a.b, a.c, a.d);
}
if(shape.c.getY() + MOVE_AMOUNT < YLIMIT){
int checka = GRID[(int)shape.a.getX()/SIZE][((int)shape.a.getY()/SIZE) + 1];
int checkb = GRID[(int)shape.b.getX()/SIZE][((int)shape.b.getY()/SIZE) + 1];
int checkc = GRID[(int)shape.c.getX()/SIZE][((int)shape.c.getY()/SIZE) + 1];
int checkd = GRID[(int)shape.d.getX()/SIZE][((int)shape.d.getY()/SIZE) + 1];
if(checka == 0 && checka == checkb && checkb == checkc && checkc == checkd){
shape.a.setY(shape.a.getY() + MOVE_AMOUNT);
shape.b.setY(shape.b.getY() + MOVE_AMOUNT);
shape.c.setY(shape.c.getY() + MOVE_AMOUNT);
shape.d.setY(shape.d.getY() + MOVE_AMOUNT);
}
}
}
private boolean checkA(Shape shape){
return (GRID[(int)shape.a.getX()/SIZE][((int)shape.a.getY()/SIZE) + 1] == 1);
}
private boolean checkB(Shape shape){
return (GRID[(int)shape.b.getX()/SIZE][((int)shape.b.getY()/SIZE) + 1] == 1);
}
private boolean checkC(Shape shape){
return (GRID[(int)shape.c.getX()/SIZE][((int)shape.c.getY()/SIZE) + 1] == 1);
}
private boolean checkD(Shape shape){
return (GRID[(int)shape.d.getX()/SIZE][((int)shape.d.getY()/SIZE) + 1] == 1);
}
}
This is the code. The problem is at DeleteRows() method.

Unable to scroll down JTable after extending it?

I have extended JTable so it can sort the data alphabetically and separate the data by sections according to their first letter.
See this question and marked answer for more clarification
The extended table works fine. However when sorted, by clicking on the table's header, I am facing a problem when scrolling down the table (using mouse-wheel).
Here is the code to the extended table. (Note: you can also find this code in the hyperlink above).
import java.awt.BorderLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableModel;
import javax.swing.JScrollPane;
import javax.swing.table.DefaultTableModel;
public class SectionedTable
extends JTable {
private static final long serialVersionUID = 1;
private final NavigableMap<Integer, String> sectionHeadings
= new TreeMap<>();
private final NavigableMap<Integer, Integer> rowTopEdges
= new TreeMap<>();
// Used when calling SwingUtilities.layoutCompoundLabel.
private final Rectangle iconBounds = new Rectangle();
private final Rectangle textBounds = new Rectangle();
public SectionedTable() {
init();
}
public SectionedTable(TableModel model) {
super(model);
init();
}
private void init() {
setShowGrid(false);
setAutoCreateRowSorter(true);
recomputeSections();
recomputeRowPositions();
}
private void recomputeSections() {
if (sectionHeadings == null) {
return;
}
sectionHeadings.clear();
RowSorter<? extends TableModel> sorter = getRowSorter();
if (sorter == null) {
return;
}
for (RowSorter.SortKey key : sorter.getSortKeys()) {
SortOrder order = key.getSortOrder();
if (order != SortOrder.UNSORTED) {
int sortColumn = key.getColumn();
String lastSectionStart = "";
int rowCount = getRowCount();
for (int row = 0; row < rowCount; row++) {
System.out.println("row er : " + row);
System.out.println("rowcount er : " + rowCount);
System.out.println("sortColumn er : " + sortColumn);
Object value = getValueAt(row, sortColumn);
if (value == null) {
value = "?";
}
String s = value.toString();
if (s.isEmpty()) {
s = "?";
}
String sectionStart = s.substring(0,
s.offsetByCodePoints(0, 1));
sectionStart = sectionStart.toUpperCase();
if (!sectionStart.equals(lastSectionStart)) {
sectionHeadings.put(row, sectionStart);
lastSectionStart = sectionStart;
}
}
break;
}
}
}
private void recomputeRowPositions() {
if (rowTopEdges == null) {
return;
}
rowTopEdges.clear();
int y = getInsets().top;
int rowCount = getRowCount();
int rowHeight = getRowHeight();
for (int row = 0; row < rowCount; row++) {
rowTopEdges.put(y, row);
y += getRowHeight(row);
if (sectionHeadings.containsKey(row)) {
y += rowHeight;
}
}
}
#Override
public void tableChanged(TableModelEvent event) {
//super.tableChanged(event);
recomputeSections();
recomputeRowPositions();
super.tableChanged(event);
}
#Override
public void sorterChanged(RowSorterEvent event) {
recomputeSections();
recomputeRowPositions();
super.sorterChanged(event);
}
#Override
public void validate() {
super.validate();
recomputeRowPositions();
}
#Override
public int rowAtPoint(Point location) {
Map.Entry<Integer, Integer> entry = rowTopEdges.floorEntry(location.y);
if (entry != null) {
int row = entry.getValue();
return row;
}
return -1;
}
#Override
public Rectangle getCellRect(int row,
int column,
boolean includeSpacing) {
Rectangle rect = super.getCellRect(row, column, includeSpacing);
int sectionHeadingsAbove = sectionHeadings.headMap(row, true).size();
rect.y += sectionHeadingsAbove * getRowHeight();
return rect;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
boolean ltr = getComponentOrientation().isLeftToRight();
int rowHeight = getRowHeight();
FontMetrics metrics = g.getFontMetrics();
int ascent = metrics.getAscent();
for (Map.Entry<Integer, String> entry : sectionHeadings.entrySet()) {
int row = entry.getKey();
String heading = entry.getValue();
Rectangle bounds = getCellRect(row, 0, true);
bounds.y -= rowHeight;
bounds.width = getWidth();
bounds.grow(-6, 0);
iconBounds.setBounds(0, 0, 0, 0);
textBounds.setBounds(0, 0, 0, 0);
String text = SwingUtilities.layoutCompoundLabel(this,
metrics, heading, null,
SwingConstants.CENTER, SwingConstants.LEADING,
SwingConstants.CENTER, SwingConstants.CENTER,
bounds, iconBounds, textBounds, 0);
g.drawString(text, textBounds.x, textBounds.y + ascent);
int lineY = textBounds.y + ascent / 2;
if (ltr) {
g.drawLine(textBounds.x + textBounds.width + 12, lineY,
getWidth() - getInsets().right - 12, lineY);
} else {
g.drawLine(textBounds.x - 12, lineY,
getInsets().left + 12, lineY);
}
}
}
public static void main(String[] args) {
DefaultTableModel model;
Object[][] data = new Object[50][5];
String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "Amy";
}
}
for (int i = 10; i < 20; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "Bob";
}
}
for (int i = 20; i < 30; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "Joe";
}
}
for (int i = 30; i < 50; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "M";
}
}
SectionedTable table = new SectionedTable();
model = new DefaultTableModel(data, columnNames);
table.setModel(model);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new SectionedTable();
table.setModel(model);
JScrollPane scrollPane = new JScrollPane(table);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setSize(500, 500);
frame.setVisible(true);
}
});
}
}
I've been struggling to find out what might cause this problem and I'm not near any solution. I think it might be at the rowAtPoint() or getCellRect(), but I'm not sure. Can someone spot where this problem might lie in the code?
I noticed that the getScrollableUnitIncrement(...) method is returning 0 when the scrolling stops functioning.
As a quick hack I added the following to your table:
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
{
int increment = super.getScrollableUnitIncrement(visibleRect, orientation, direction);
if (increment == 0)
increment = 16;
return increment;
}
Don't know if it will cause other problems.
From this I was able to determine that 0 is returned and therefore no scrolling is done.

Shifting tiles in a puzzle game

I am trying to make a puzzle game but the problem is that I can't do the EventHandler setOnMouseClicked.Also I want to know how to make possible that to re-arrange the puzzle so it shows random images in every-coordinate. This is how far I got.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class eliss extends Application{
#Override
public void start(Stage primaryStage) {
ImageView view[][] = new ImageView[5][5];
Image imag = new Image("http://images.cdn.autocar.co.uk/sites/autocar.co.uk/files/styles/gallery_slide/public/ferrari-laferrari-zfye-059_1.jpg?itok=hfLNxUD9",600,600,false,true);
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setVgap(2);
pane.setHgap(2);
PuzzleImage(view, imag, pane);
Scene scene = new Scene(pane,1100,1100);
primaryStage.setTitle("Elis");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] x)
{
launch(x);
}
public void PuzzleImage( ImageView view[][],Image imag,GridPane pane)
{
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
{
if(j==4 && i==4) view[i][j]=null;
else{
view[i][j]=new ImageView(imag);
Rectangle2D rect = new Rectangle2D(120*i,120*j,120,120);
view[i][j].setViewport(rect);
pane.add(view[i][j], i, j);
}
}
}
}
}
What you are asking is too much for a question on StackOverflow. However, I got a little bit of spare time, so I quickly drafted up some code to demonstrate what you need:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PuzzleGame extends Application {
private Image image = new Image("http://images.cdn.autocar.co.uk/sites/autocar.co.uk/files/styles/gallery_slide/public/ferrari-laferrari-zfye-059_1.jpg?itok=hfLNxUD9",600,600,false,true);
private static double SCENE_WIDTH = 1024;
private static double SCENE_HEIGHT = 768;
public static int TILE_ROW_COUNT = 5;
public static int TILE_COLUMN_COUNT = 5;
public static double TILE_SIZE = 120;
public static double offsetX = (SCENE_WIDTH - TILE_ROW_COUNT * TILE_SIZE) / 2;
public static double offsetY = (SCENE_HEIGHT - TILE_COLUMN_COUNT * TILE_SIZE) / 2;
List<Cell> cells = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
// create grid
for (int x = 0; x < TILE_ROW_COUNT; x++) {
for (int y = 0; y < TILE_COLUMN_COUNT; y++) {
// create tile
ImageView tile = new ImageView(image);
Rectangle2D rect = new Rectangle2D(TILE_SIZE * x, TILE_SIZE * y, TILE_SIZE, TILE_SIZE);
tile.setViewport(rect);
// consider empty cell, let it remain empty
if (x == (TILE_ROW_COUNT - 1) && y == (TILE_COLUMN_COUNT - 1)) {
tile = null;
}
cells.add(new Cell(x, y, tile));
}
}
// shuffle cells
shuffle();
// create playfield
Pane pane = new Pane();
// put tiles on playfield, assign event handler
for (int i = 0; i < cells.size(); i++) {
Cell cell = cells.get(i);
Node imageView = cell.getImageView();
// consider empty cell
if (imageView == null)
continue;
// click-handler: swap tiles, check if puzzle is solved
imageView.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
moveCell((Node) mouseEvent.getSource());
});
// position images on scene
imageView.relocate(cell.getLayoutX(), cell.getLayoutY());
pane.getChildren().add(cell.getImageView());
}
Scene scene = new Scene(pane, SCENE_WIDTH, SCENE_HEIGHT);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* Swap images of cells randomly
*/
public void shuffle() {
Random rnd = new Random();
for (int i = 0; i < 1000; i++) {
int a = rnd.nextInt(cells.size());
int b = rnd.nextInt(cells.size());
if (a == b)
continue;
// skip bottom right cell swap, we want the empty cell to remain there
if( cells.get(a).isEmpty() || cells.get(b).isEmpty())
continue;
swap( cells.get(a), cells.get(b));
}
}
public void swap( Cell cellA, Cell cellB) {
ImageView tmp = cellA.getImageView();
cellA.setImageView(cellB.getImageView());
cellB.setImageView(tmp);
}
public boolean checkSolved() {
boolean allSolved = true;
for (Cell cell : cells) {
if (!cell.isSolved()) {
allSolved = false;
break;
}
}
System.out.println("Solved: " + allSolved);
return allSolved;
}
public void moveCell(Node node) {
// get current cell using the selected node (imageview)
Cell currentCell = null;
for (Cell tmpCell : cells) {
if (tmpCell.getImageView() == node) {
currentCell = tmpCell;
break;
}
}
if (currentCell == null)
return;
// get empty cell
Cell emptyCell = null;
for (Cell tmpCell : cells) {
if (tmpCell.isEmpty()) {
emptyCell = tmpCell;
break;
}
}
if (emptyCell == null)
return;
// check if cells are swappable: neighbor distance either x or y must be 1 for a valid move
int steps = Math.abs(currentCell.x - emptyCell.x) + Math.abs(currentCell.y - emptyCell.y);
if (steps != 1)
return;
System.out.println("Transition: " + currentCell + " -> " + emptyCell);
// cells are swappable => create path transition
Path path = new Path();
path.getElements().add(new MoveToAbs(currentCell.getImageView(), currentCell.getLayoutX(), currentCell.getLayoutY()));
path.getElements().add(new LineToAbs(currentCell.getImageView(), emptyCell.getLayoutX(), emptyCell.getLayoutY()));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(100));
pathTransition.setNode(currentCell.getImageView());
pathTransition.setPath(path);
pathTransition.setOrientation(PathTransition.OrientationType.NONE);
pathTransition.setCycleCount(1);
pathTransition.setAutoReverse(false);
final Cell cellA = currentCell;
final Cell cellB = emptyCell;
pathTransition.setOnFinished(actionEvent -> {
swap( cellA, cellB);
checkSolved();
});
pathTransition.play();
}
private static class Cell {
int x;
int y;
ImageView initialImageView;
ImageView currentImageView;
public Cell(int x, int y, ImageView initialImageView) {
super();
this.x = x;
this.y = y;
this.initialImageView = initialImageView;
this.currentImageView = initialImageView;
}
public double getLayoutX() {
return x * TILE_SIZE + offsetX;
}
public double getLayoutY() {
return y * TILE_SIZE + offsetY;
}
public ImageView getImageView() {
return currentImageView;
}
public void setImageView(ImageView imageView) {
this.currentImageView = imageView;
}
public boolean isEmpty() {
return currentImageView == null;
}
public boolean isSolved() {
return this.initialImageView == currentImageView;
}
public String toString() {
return "[" + x + "," + y + "]";
}
}
// absolute (layoutX/Y) transitions using the pathtransition for MoveTo
public static class MoveToAbs extends MoveTo {
public MoveToAbs(Node node) {
super(node.getLayoutBounds().getWidth() / 2, node.getLayoutBounds().getHeight() / 2);
}
public MoveToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
// absolute (layoutX/Y) transitions using the pathtransition for LineTo
public static class LineToAbs extends LineTo {
public LineToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static void main(String[] args) {
launch(args);
}
}
You can use Collections.shuffle to generate a random permutation of a List This can be used to place the tiles in a random order:
private static final int COLUMN_COUNT = 5;
private static final int ROW_COUNT = 5;
private static void fillGridPane(GridPane pane, ImageView[][] view, Image imag) {
List<ImageView> images = new ArrayList<>(24);
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
ImageView iv = new ImageView(imag);
images.add(iv);
view[i][j] = iv;
Rectangle2D rect = new Rectangle2D(120 * i, 120 * j, 120, 120);
iv.setViewport(rect);
}
}
Collections.shuffle(images);
Iterator<ImageView> iter = images.iterator();
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
pane.add(iter.next(), i, j);
}
}
}
The onMouseClicked event handler can be attached to the GridPane and MouseEvent.getTarget can be used to get the node that was clicked:
pane.setOnMouseClicked(event -> {
if (move(event)) {
if (checkWin()) {
new Alert(Alert.AlertType.INFORMATION, "You Win!").show();
}
}
});
private int emptyTileX = 4;
private int emptyTileY = 4;
private boolean move(MouseEvent event) {
Object target = event.getTarget();
if (target instanceof ImageView) {
ImageView iv = (ImageView) target;
int row = GridPane.getRowIndex(iv);
int column = GridPane.getColumnIndex(iv);
int dx = Math.abs(column - emptyTileX);
int dy = Math.abs(row - emptyTileY);
if ((dx == 0 && dy == 1) || (dx == 1 && dy == 0)) {
// swap image and empty tile, if they are next to each other
GridPane.setConstraints(iv, emptyTileX, emptyTileY);
emptyTileX = column;
emptyTileY = row;
return true;
}
}
return false;
}
To check the winning condition, you can simply check the positions of the ImageViews in the GridPane
private boolean checkWin() {
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
ImageView iv = view[i][j];
if (GridPane.getColumnIndex(iv) != i || GridPane.getRowIndex(iv) != j) {
return false;
}
}
}
return true;
}

Chessboard with automatic resizing

So, I am trying to display a chessboard in javaFX. I will have to perform different operations and draw on some of the tiles so I chose to use a Canvas for each tile and a GridPane to arrange them for me in a grid fashion.
Unfortunately I am having some problems with the resizing of the grid tiles; I want my whole chessboard to automatically adapt its size to the Scene. Therefore, I have added a ChangeListener to both the height and width properties of the GridPane which takes care of resizing the tiles. This only works when the window gets bigger, when the window is reduced to a smaller size everything still gets bigger!
Here's the shortest SSCCE I came up with which reproduces my problem:
package chessboardtest;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.stage.Stage;
public class ChessboardTest extends Application {
final int size = 10;
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
final GridPane chessboard = new GridPane();
fillChessboard(chessboard, size);
ChangeListener<Number> resizeListener = new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
double newWidth = chessboard.getWidth() / size;
double newHeight = chessboard.getHeight() / size;
for(Node n: chessboard.getChildren()) {
Canvas canvas = (Canvas)n;
canvas.setWidth(newWidth);
canvas.setHeight(newHeight);
}
}
};
chessboard.widthProperty().addListener(resizeListener);
chessboard.heightProperty().addListener(resizeListener);
root.getChildren().add(chessboard);
root.setPadding(new Insets(10));
VBox.setVgrow(chessboard, Priority.ALWAYS);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("chessboard");
primaryStage.setScene(scene);
primaryStage.show();
}
void fillChessboard(GridPane pane, int size) {
class RedrawListener implements ChangeListener<Number> {
Color color;
Canvas canvas;
public RedrawListener(Canvas c, int i) {
if(i % 2 == 0) {
color = Color.BLACK;
}
else {
color = Color.WHITE;
}
canvas = c;
}
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
canvas.getGraphicsContext2D().setFill(color);
canvas.getGraphicsContext2D().fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
}
for(int row = 0; row < size; row++) {
for(int col = 0, i = row; col < size; col++, i++) {
Canvas c = new Canvas();
RedrawListener rl = new RedrawListener(c, i);
c.widthProperty().addListener(rl);
c.heightProperty().addListener(rl);
pane.add(c, row, col);
}
}
}
public static void main(String[] args) {
launch(args);
}
}
If you don't need a canvas (and you probably don't), just use StackPanes for the squares and make them fill the width and the height. You can always add a canvas (or anything else) to the StackPanes to display their content.
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Chessboard extends Application {
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
final int size = 8 ;
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col ++) {
StackPane square = new StackPane();
String color ;
if ((row + col) % 2 == 0) {
color = "white";
} else {
color = "black";
}
square.setStyle("-fx-background-color: "+color+";");
root.add(square, col, row);
}
}
for (int i = 0; i < size; i++) {
root.getColumnConstraints().add(new ColumnConstraints(5, Control.USE_COMPUTED_SIZE, Double.POSITIVE_INFINITY, Priority.ALWAYS, HPos.CENTER, true));
root.getRowConstraints().add(new RowConstraints(5, Control.USE_COMPUTED_SIZE, Double.POSITIVE_INFINITY, Priority.ALWAYS, VPos.CENTER, true));
}
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This is a nice solution, but resizing is so much easier with data binding in Java FX. You can hide all listener business this way.
Here is a solution much like James D's, but using Rectangles insread of Canvases for the squares:
public class ResizeChessboard extends Application {
GridPane root = new GridPane();
final int size = 8;
public void start(Stage primaryStage) {
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
Rectangle square = new Rectangle();
Color color;
if ((row + col) % 2 == 0) color = Color.WHITE;
else color = Color.BLACK;
square.setFill(color);
root.add(square, col, row);
square.widthProperty().bind(root.widthProperty().divide(size));
square.heightProperty().bind(root.heightProperty().divide(size));
}
}
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Dynamically add elements to a fixed-size GridPane in JavaFX

I would like to display a grid containing a various number of rectangles in JavaFX. It is important that this grid cannot be resized.
I chose the GridPane layout. I dynamically add javafx.scene.shape.Rectangle to it. Here's how my grid looks like with 2 rows and 4 columns.
Upon resizing, I would like it to keep the same overall shape, that is to say each Rectangle having the same size and keeping an horizontal and vertical gaps between my Rectangles.
However, here's what I get with a 4x4 grid:
The problems being:
The last row and last column do not have the same size as the rest of the Rectangles.
The gaps have disappeared.
Here is my code responsible for refreshing the display:
public void refreshConstraints() {
getRowConstraints().clear();
getColumnConstraints().clear();
for (int i = 0; i < nbRow; i++) {
RowConstraints rConstraint = new RowConstraints();
// ((nbRow - 1) * 10 / nbRow) = takes gap into account (10% of height)
rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
getRowConstraints().add(rConstraint);
}
for (int i = 0; i < nbColumn; i++) {
ColumnConstraints cConstraint = new ColumnConstraints();
cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
getColumnConstraints().add(cConstraint);
}
}
Using the setFillWidth and setHgrow yields no result either, the gap is kept between my Rectangles, but the Rectangles aren't resized and they overlap the rest of my GUI elements.
EDIT: MCVE code:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DynamicGrid extends Application {
//Class containing grid (see below)
private GridDisplay gridDisplay;
#Override
public void start(Stage primaryStage) {
//Represents the grid with Rectangles
gridDisplay = new GridDisplay(400, 200);
//Fields to specify number of rows/columns
TextField rowField = new TextField();
TextField columnField = new TextField();
//Function to set an action when text field loses focus
buildTextFieldActions(rowField, columnField);
HBox fields = new HBox();
fields.getChildren().add(rowField);
fields.getChildren().add(new Label("x"));
fields.getChildren().add(columnField);
BorderPane mainPanel = new BorderPane();
mainPanel.setLeft(gridDisplay.getDisplay());
mainPanel.setBottom(fields);
Scene scene = new Scene(mainPanel);
primaryStage.setTitle("Test grid display");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void buildTextFieldActions(final TextField rowField, final TextField columnField) {
rowField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (!t1) {
if (!rowField.getText().equals("")) {
try {
int nbRow = Integer.parseInt(rowField.getText());
gridDisplay.setRows(nbRow);
gridDisplay.updateDisplay();
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
}
});
columnField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (!t1) {
if (!columnField.getText().equals("")) {
try {
int nbColumn = Integer.parseInt(columnField.getText());
gridDisplay.setColumns(nbColumn);
gridDisplay.updateDisplay();
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
}
});
}
//Class responsible for displaying the grid containing the Rectangles
public class GridDisplay {
private GridPane gridPane;
private int nbRow;
private int nbColumn;
private int width;
private int height;
private double hGap;
private double vGap;
public GridDisplay(int width, int height) {
this.gridPane = new GridPane();
this.width = width;
this.height = height;
build();
}
private void build() {
this.hGap = 0.1 * width;
this.vGap = 0.1 * height;
gridPane.setVgap(vGap);
gridPane.setHgap(hGap);
gridPane.setPrefSize(width, height);
initializeDisplay(width, height);
}
//Builds the first display (correctly) : adds a Rectangle for the number
//of rows and columns
private void initializeDisplay(int width, int height) {
nbRow = height / 100;
nbColumn = width / 100;
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
rectangle.setStroke(Paint.valueOf("orange"));
rectangle.setFill(Paint.valueOf("steelblue"));
gridPane.add(rectangle, i, j);
}
}
}
//Function detailed in post
//Called in updateDisplay()
public void refreshConstraints() {
gridPane.getRowConstraints().clear();
gridPane.getColumnConstraints().clear();
for (int i = 0; i < nbRow; i++) {
RowConstraints rConstraint = new RowConstraints();
rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
gridPane.getRowConstraints().add(rConstraint);
}
for (int i = 0; i < nbColumn; i++) {
ColumnConstraints cConstraint = new ColumnConstraints();
cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
gridPane.getColumnConstraints().add(cConstraint);
}
}
public void setColumns(int newColumns) {
nbColumn = newColumns;
}
public void setRows(int newRows) {
nbRow = newRows;
}
public GridPane getDisplay() {
return gridPane;
}
//Function called when refreshing the display
public void updateDisplay() {
Platform.runLater(new Runnable() {
#Override
public void run() {
//The gridpane is cleared of the previous children
gridPane.getChildren().clear();
//A new rectangle is added for row*column
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
rectangle.setStroke(Paint.valueOf("orange"));
rectangle.setFill(Paint.valueOf("steelblue"));
gridPane.add(rectangle, i, j);
}
}
//Call to this function to update the grid's constraints
refreshConstraints();
}
});
}
}
}
Seems like a TilePane is a better fit for this use case than a GridPane.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
// java 8 code
public class DynamicTiles extends Application {
//Class containing grid (see below)
private GridDisplay gridDisplay;
//Class responsible for displaying the grid containing the Rectangles
public class GridDisplay {
private static final double ELEMENT_SIZE = 100;
private static final double GAP = ELEMENT_SIZE / 10;
private TilePane tilePane = new TilePane();
private Group display = new Group(tilePane);
private int nRows;
private int nCols;
public GridDisplay(int nRows, int nCols) {
tilePane.setStyle("-fx-background-color: rgba(255, 215, 0, 0.1);");
tilePane.setHgap(GAP);
tilePane.setVgap(GAP);
setColumns(nCols);
setRows(nRows);
}
public void setColumns(int newColumns) {
nCols = newColumns;
tilePane.setPrefColumns(nCols);
createElements();
}
public void setRows(int newRows) {
nRows = newRows;
tilePane.setPrefRows(nRows);
createElements();
}
public Group getDisplay() {
return display;
}
private void createElements() {
tilePane.getChildren().clear();
for (int i = 0; i < nCols; i++) {
for (int j = 0; j < nRows; j++) {
tilePane.getChildren().add(createElement());
}
}
}
private Rectangle createElement() {
Rectangle rectangle = new Rectangle(ELEMENT_SIZE, ELEMENT_SIZE);
rectangle.setStroke(Color.ORANGE);
rectangle.setFill(Color.STEELBLUE);
return rectangle;
}
}
#Override
public void start(Stage primaryStage) {
//Represents the grid with Rectangles
gridDisplay = new GridDisplay(2, 4);
//Fields to specify number of rows/columns
TextField rowField = new TextField("2");
TextField columnField = new TextField("4");
//Function to set an action when text field loses focus
buildTextFieldActions(rowField, columnField);
HBox fields = new HBox(10);
fields.getChildren().add(rowField);
fields.getChildren().add(new Label("x"));
fields.getChildren().add(columnField);
BorderPane mainPanel = new BorderPane();
mainPanel.setCenter(gridDisplay.getDisplay());
mainPanel.setTop(fields);
Scene scene = new Scene(mainPanel, 1000, 800);
primaryStage.setTitle("Test grid display");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void buildTextFieldActions(final TextField rowField, final TextField columnField) {
rowField.focusedProperty().addListener((ov, t, t1) -> {
if (!t1) {
if (!rowField.getText().equals("")) {
try {
int nbRow = Integer.parseInt(rowField.getText());
gridDisplay.setRows(nbRow);
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
});
columnField.focusedProperty().addListener((ov, t, t1) -> {
if (!t1) {
if (!columnField.getText().equals("")) {
try {
int nbColumn = Integer.parseInt(columnField.getText());
gridDisplay.setColumns(nbColumn);
} catch (NumberFormatException nfe) {
System.out.println("Please enter a valid number.");
}
}
}
});
}
}
Thanks a lot for your answer. TilePanes are indeed a lot easier to use, although what you've written does not completely answer my question.
I wanted to have a pane in which the children would resize, and not the pane itself. It seems setting the maxSize and prefSize doesn't have any effect.
EDIT: I managed to do it using two JavaFX Property in my GridDisplay class, corresponding to the fixed height and width of my grid:
public class GridDisplay {
private ReadOnlyDoubleProperty heightProperty;
private ReadOnlyDoubleProperty widthProperty;
...
}
Then I assign to these members the values corresponding to the desired fixed size in the constructor. The size of the children inside the grid correspond to a fraction of the height and width of the grid, depending on the number of rows and columns. Here's what my updateDisplay() looks like:
public void updateDisplay() {
gridPane.getChildren().clear();
for (int i = 0; i < nbColumn; i++) {
for (int j = 0; j < nbRow; j++) {
Rectangle rectangle = new Rectangle(100, 100);
//Binding the fraction of the grid size to the width
//and heightProperty of the child
rectangle.widthProperty().bind(widthProperty.divide(nbColumn));
rectangle.heightProperty().bind(heightProperty.divide(nbRow));
gridPane.add(rectangle, i, j);
}
}
}

Categories

Resources