I've been doing a CrossWord puzzle in java for a project. However I haven't been able to fix one problem I'm having with drawing the CrossWord into a JPanel
The program takes the data from a 2D array of chars and generates a grid of a custom type of JLabels with it. This is the code:
public void update(Observable o, Object o1) {
if(model.getMatrix() != null){
configurePanel(model.getRows(), model.getCols());
this.add(panel);
this.pack();
this.revalidate();
this.repaint();
}
}
private void configurePanel(int w, int h) {
if (panel!=null){
this.remove(panel);
}
panel = new JPanel();
panel.setBounds(40, 40, w*50, h*50);
panel.setLayout(new GridLayout(w, h));
labels = createLabels(model.getMatrix());
for (int i = 0; i < w; i++){
for(int j = 0; j < h; j++){
panel.add(labels[i][j]);
}
}
}
private CWlabel[][] createLabels(char[][] matrix) {
int w = matrix.length;
int h = matrix[0].length;
labels = new CWlabel[w][h];
for (int i = 0; i < w; i++){
for(int j = 0; j < h; j++){
char c = matrix[i][j];
labels[i][j] = new CWlabel();
labels[i][j].setBorder(BorderFactory.createLineBorder(Color.black));
labels[i][j].setOpaque(true);
if (c != ' '){
labels[i][j].setBackground(Color.white);
} else {
labels[i][j].setBackground(Color.black);
}
}
}
return labels;
}
My main problem is in configurePanel(), where the size of the panel are set proportional to the crossword dimensions, which should ensure every component inside it is perfectly square, however this is not the case as seen here
In the example shown the difference is subtle but still notizable. The strange part its that if I manually replace,
panel.setBounds(40, 40, w*50, h*50);
with,
panel.setBounds(40, 40, 400, 450);
the result appears to be proportional to the amount of rows and columns as shown here
By my count you have 9 rows and 8 columns so calling configurePanel like the following:
configurePanel(model.getRows(), model.getCols());
Is going to result in a width of 450 and a height of 400, but you want a width of 400 and a height of 450. Thus switch around the first and second parameters, like the following:
configurePanel(model.getCols(), model.getRows());
Note: You shouldn't use absolute positioning, it causes many formatting problems down the road (resizing components, adding components, etc.). You should instead use a layout manager with more customization like GridBagLayout or MigLayout.
Related
I am trying to create John Conway's Game Of Life. I have a function which receives a boolean matrix, and create white square on my JPanel (the square being JPanel itself) if a cell in the matrix is set to true, and black square if false. Here is the function:
public static void applyMatrixToGamePanel(JPanel panel, boolean[][] matrix, int cubeSize)
{
for(int i = 0; i < matrix.length; i++)
{
for(int j = 0; j < matrix[0].length; j++)
{
JPanel cube = new JPanel();
cube.setSize(cubeSize, cubeSize);
cube.setVisible(true);
if(matrix[i][j])
{
cube.setBackground(Color.WHITE);
}
else
{
cube.setBackground(Color.BLACK);
}
panel.add(cube);
}
}
}
I pass to panel my main game's JPanel which I set as the only component of my main JFrame.
the problem being, that no matter what size I pass in, the squares remain at a relatively small, fixed size.
This is how it looks (painted red just to outline):
I am trying to put a label in every single panel I create but I only get the label in the last panel created. Its for a game of snakes and ladders and I am trying to initiate the board for the game. I need every panel to have a number.
public InitBoard() {
JPanel jPanel1 = new JPanel(new GridLayout(10, 10, 0, 0));
JPanel[][] pa = new JPanel[10][10];
//jPanel1.setSize(1024,1080);
int counter=1;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
JPanel tiles = new JPanel();
tiles.setBorder(BorderFactory.createLineBorder(Color.BLACK));
number++;
label1.setText(String.valueOf(number));
tiles.add(label1);
if(counter == 1) {
tiles.setBackground(Color.green);
}
if (counter == 2) {
tiles.setBackground(Color.red);
}
if(counter == 3 ){
tiles.setBackground(Color.cyan);
}
counter++;
pa[i][j] = tiles;
jPanel1.add(pa[i][j]);
if(counter ==5){
counter =1;
}
}
}
add(jPanel1, BorderLayout.CENTER);
setSize(960, 960);
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
}
Following image is shown when I run the file.
Any help is appreciated in advance.
I can't see this from the code that you posted, but it seems that you are re-using the same JLabel instance (label1) for all the tiles. This somehow leads to the label being overwritten and re-used for all tiles except the last one.
Just use this line:
tiles.add(new JLabel(String.valueOf(number)));
instead of what you currently have:
label1.setText(String.valueOf(number));
tiles.add(label1);
Making this change, I got your code to work as intended, showing a number on every tile. If the numbers need to be stored somewhere or if you need to be able to change them later, you might want to create an array similar to the one you have for the JPanels (pa).
I'm not really keen on Java GUI, but am learning as I go. I am in the process of making a very simple and basic Sudoku puzzle. Right now I am just on the basic layout of it.
I wanted to see if there was a simple way of adding a text field to each little rectangle that I have drawn out (81 total rectangles - 9x9 puzzle). So that a user can type something in there.
I am delving into it, but wanted to get the code up here to see if anyone had any tips, cause truth be told, I am mega lost with this.
Here is what I have so far...
import java.awt.*;
import javax.swing.*;
class MyCanvas extends JComponent {
public void paint(Graphics g) {
int coordinateX = 0;
int coordinateY = 0;
// maximum 9 rows
int rows = 9;
int columns = 1;
// make a 9x9 puzzle
while (columns <= rows) {
//for loop to increment the boxes
for (int j = 1; j <= rows; j++) {
// add and assign coordinte x... equivalent to x = x + 30
coordinateX += 30;
// where x and y determine start of the box and 30 determines the size
g.drawRect(coordinateX, coordinateY, 30, 30);
} //end of for loop
//reset the value of x to start a new row
coordinateX = 0;
coordinateY += 30;
columns++;
} //end of while loop
} //end of void paint
} //end of class
public class DrawRect {
public static void main(String[] a) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(100, 100, 500, 500);
window.getContentPane().add(new MyCanvas());
window.setVisible(true);
} // end of void main
} // end of class
Hopefully someone has some pointers that could help me out, cause boy oh boy do I need it. Was kinda thrown into the lion's den without prior knowledge or practice, but I'm trying hard.
Thanks guys!!
You could use a GridLayout(9,9) and an array of arrays of JTextField's
This is, of course, just an example of how I would do it. There are other ways to do this.
Find below a generic example.
Solution
public static void main(String[] args) {
JTextField[][] boxes = new JTextField[9][9];
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(9,9));
frame.setSize(500, 500);
for (int i = 0 ; i < 9 ; i++){
for (int j = 0 ; j < 9 ; j++){
boxes[i][j] = new JTextField("0");
frame.add(boxes[i][j]);
}
}
frame.setVisible(true);
}
Output
I am trying to make a simple version of Conway's Game of Life where the computer generates a grid of rectangles and fills in the rectangles that represent "live" cells. The problem I am having is that I cannot get the grid to clear after the first pattern, so all the patterns are generated on the same grid and it looks like a big blob of colored rectangles.
Here is my code:
public class GameofLife {
static JPanel panel;
static JFrame frame;
public static void main(String[] args) throws InterruptedException{
int [][] array = new int [17][17];
/*
* Set the pattern for Conway's Game of Life by manipulating the array below.
*/
array[2][4]=1;
array[2][5]=1;
array[2][6]=1;
panel = new JPanel();
Dimension dim = new Dimension(400,400);
panel.setPreferredSize(dim);
frame = new JFrame();
frame.setSize(1000, 500);
Container contentPane = frame.getContentPane();
contentPane.add(panel);
frame.setVisible(true);
/*
* Runs the Game of Life simulation "a" number of times.
*/
int [][] end = new int [array.length][array[0].length];
int a=0;
while(a<4){
for(int i=1; i<=array.length-2; i++)
{
for(int j=1; j<=array[0].length-2; j++)
{
int counter = surround(array,i,j);
if(array[i][j]==1 && counter<=2)
{
end[i][j]=0;
}
if(array[i][j]==1 && counter==3)
{
end[i][j]=1;
}
if(array[i][j]==1 && counter>4)
{
end[i][j]=0;
}
if(array[i][j]==0 && counter==3)
{
end[i][j]=1;
}
if(array[i][j]==1 && counter==4)
{
end[i][j]=1;
}
}
}
Graphics g = panel.getGraphics();
Graphics(array,g);
a++;
for(int i=0; i<array.length; i++)
{
for(int j=0; j<array[0].length; j++)
{
array[i][j]=end[i][j];
end[i][j]=0;
}
}
Thread.sleep(1000);
g.dispose();
}
}
public static int surround(int[][] initial, int i, int j){
int[][] surrounding = {{initial[i-1][j-1],initial[i-1][j],initial[i-1][j+1]},
{initial[i][j-1],initial[i][j],initial[i][j+1]},
{initial[i+1][j-1],initial[i+1][j],initial[i+1][j+1]}};
int counter = 0;
for(int a=0; a<=2; a++)
{
for(int b=0; b<=2; b++)
{
if(surrounding[a][b]==1)
{
counter ++;
}
}
}
return counter;
}
public static void Graphics(int[][] array, Graphics g)
{
int BOX_DIM=10;
for(int i=0; i<array.length; i++)
{
for(int j=0; j<array[0].length; j++)
{
g.drawRect(i*BOX_DIM, j*BOX_DIM, 10,10);
g.setColor(Color.BLACK);
if(array[i][j]==1)
{
g.fillRect(i*BOX_DIM, j*BOX_DIM, 10, 10);
}
}
}
}
}
Any help is much appreciated!
You need to draw rectangles for both "alive" and "dead" cells but color them differently. Live cells could be black and dead cells white but if you don't redraw every cell during every iteration you'll run into the issue you've described. That being said...you seem to have answered your own question.
You COULD fix this immediate concern by clearing grid at the top of the Graphics() function:
g.setColor(Color.??); // Choose desired background color here
g.fillRect( 0, 0, array.length * BOX_DIM, array[0].length * BOX_DIM);
BUT, consider that your approach is fighting against the natural way of handling graphics in Java. Any time your window gets clipped, it may not repaint in a timely manner.
You might want to consider sub-classing JPanel and overriding its onPaint() method, then invalidating the panel whenever the data model changes.
I want to create a small grid (let's say 2 x 2) where each individual point in the grid (4 points total) will have its own value of integer. I will also want to be able to call on the values of each point by its location. This is because I want each point's integer to be affected by its neighboring points. What is the best way that I can get each point to have its own values and then be able to call upon those values?
I have this so far, which only makes the grid and displays its values. In the end, I will also want to display its neighbors' values in its location. Thanks!
edit: just looking for best approach to solving this problem to guide me in right direction to go about this.
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.GridLayout;
public class Grid {
JFrame frame = new JFrame(); // create frame
JButton[][] grid; // name grid
// constructor
public Grid (int width, int length) {
frame.setLayout(new GridLayout(width, length));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
// create grid
grid = new JButton[width][length]; // allocate size
for (int y = 0; y < length; y++) {
for (int x = 0; x < width; x++) {
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
}
}
Use a nested int array to make a grid.
int[][]grid = new int[2][2];
for(int x=0; x<2;x++){
for(int y=0; y<2; y++){
grid[x][y] = value;
}
}
This will return a grid you can address (call the value of) by calling int val = grid[x][y]