Setting a label text in Swing undos all button location movements - java

I have the weirdest bug ever.
I have this puzzle game that moves puzzle pieces (which really are buttons with images attached to them).
Everything worked fine until I tried to change the text of some label (to indicate how many steps the player has done).
Everytime I call someControl.setText("text");, the puzzle pieces that moved are set back to the their first position. I have no idea why, but they just do.
Here's my window:
It consists of two panels, each uses a GridBagLayout.
The main frame uses a gridBagLayout as well, which consists of the two panels.
I know it's weird as hell, but I can't figure out what may cause this GUI bug. Any idea?
The pieces of code:
increaseSteps which is called everytime I click a puzzle button
void increaseSteps() {
_steps++;
_lblSteps.setText("Steps: " + _steps);
}
Creation of the puzzle panel (the left panel)
private JPanel puzzlePanel() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
for (int i = 0; i < _splitImage.getSize(); i++)
for (int j = 0; j < _splitImage.getSize(); j++) {
int valueAtPos = _board.getMatrix()[i][j];
if (valueAtPos == 0)
continue;
int imageRow = _board.getImageRowFromValue(valueAtPos);
int imageCol = _board.getImageColFromValue(valueAtPos);
ImageIcon imageIcon = new ImageIcon(_splitImage.getImages()[imageRow][imageCol]);
JButton btn = new JButton(imageIcon);
_tileButtons[i][j] = new TileButton(btn, i, j);
btn.setPreferredSize(new Dimension(_splitImage.getImages()[i][j].getWidth(null),
_splitImage.getImages()[i][j].getHeight(null)));
// add action listener
btn.addActionListener(this);
btn.addKeyListener(this);
gbc.gridx = j;
gbc.gridy = i;
panel.add(_tileButtons[i][j].getButton(), gbc);
}
return panel;
}
actionPerformed:
#Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JButton))
return;
JButton btn = (JButton) e.getSource();
TileButton tile = getTileButtonFromBtn(btn);
if (tile == null)
return;
// check if we can move the tile
String moveDir = _board.canMoveTile(tile.getRow(), tile.getCol());
if (moveDir.equals("no"))
return;
increaseSteps();
int dirx = 0;
int diry = 0;
if (moveDir.equals("left")) {
dirx = -1;
_board.move("left", true);
tile.setCol(tile.getCol() - 1);
} else if (moveDir.equals("right")) {
dirx = 1;
_board.move("right", true);
tile.setCol(tile.getCol() + 1);
} else if (moveDir.equals("up")) {
diry = -1;
_board.move("up", true);
tile.setRow(tile.getRow() - 1);
} else { // down
diry = 1;
_board.move("down", true);
tile.setRow(tile.getRow() + 1);
}
moveButton(btn, dirx, diry, MOVE_SPEED);
if (_board.hasWon())
win();
}
moveButton: (moves the button in a seperate thread, calling btn.setLocation())
private void moveButton(JButton btn, int dirx, int diry, int speed) {
Point loc = btn.getLocation();
// get start ticks, calculate distance etc...
StopWatch stopper = new StopWatch();
int distance;
if (dirx != 0)
distance = _splitImage.getImages()[0][0].getWidth(null) * dirx;
else
distance = _splitImage.getImages()[0][0].getHeight(null) * diry;
if (speed > 0) {
// run the animation in a new thread
Thread thread = new Thread() {
public void run() {
int currentTicks;
int elapsed;
do {
int newX = loc.x;
int newY = loc.y;
elapsed = stopper.getElapsed();
int moved = (int) ((double) distance * (double) (elapsed / (double) speed));
if (dirx != 0)
newX += moved;
else
newY += moved;
btn.setLocation(newX, newY);
} while (elapsed <= MOVE_SPEED);
// make sure the last location is exact
btn.setLocation(loc.x + (dirx == 0 ? 0 : distance), loc.y + (diry == 0 ? 0 : distance));
}
};
thread.start();
}
else
btn.setLocation(loc.x + (dirx == 0 ? 0 : distance), loc.y + (diry == 0 ? 0 : distance));
}

You're trying to set the absolute position of a component via setLocation(...) or setBounds(...), one that is held by a container that uses a layout manager. This may work temporarily, but will fail if the container's layout manager is triggered to re-do the layout of its contained components. When that happens, the GridBagConstraints will take over and the components will move to their gridbag constraints assigned location.
The solution is to not do this, and instead to place the location of your components in concert with the layout managers used.
Another problem is that your current code is not Swing thread-safe since you're making Swing state changes from within a background thread. This won't always cause problems, but since it's a threading issue, risks causing intermittent hard to debug problems (ones that usually only occur when your boss or instructor are trying to run your code).
Possible solutions:
For a grid of images, you could use a grid of JLabels (or JButtons if you must) held in a container that uses GridLayout. When you need to reposition components, remove all components held by that JPanel, and then re-add, using the order of addition to help you position the components.
Easiest though would be to use a grid of non-moving JLabels, give them MouseListeners, and instead of moving the JLabels, remove and add Icons to them, including a blank Icon.
If you need to do Swing animation, use a Swing Timer to drive the animation. This will allow your code to make repetitive calls with delay between the calls, and with these calls being made on the Swing event thread, the EDT (event dispatch thread).
Demo proof of concept example code that shows swapping icons, but without animation, and without test of solution yet:
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class ImageShuffle extends JPanel {
private static final int SIDES = 3;
public static final String IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/"
+ "thumb/5/5a/Hurricane_Kiko_Sep_3_1983_1915Z.jpg/"
+ "600px-Hurricane_Kiko_Sep_3_1983_1915Z.jpg";
private List<Icon> iconList = new ArrayList<>(); // shuffled icons
private List<Icon> solutionList = new ArrayList<>(); // in order
private List<JLabel> labelList = new ArrayList<>(); // holds JLabel grid
private Icon blankIcon;
public ImageShuffle(BufferedImage img) {
setLayout(new GridLayout(SIDES, SIDES, 1, 1));
fillIconList(img); // fill array list with icons and one blank one
Collections.shuffle(iconList);
MyMouseListener myMouse = new MyMouseListener();
for (Icon icon : iconList) {
JLabel label = new JLabel(icon);
label.addMouseListener(myMouse);
add(label);
labelList.add(label);
}
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel selectedLabel = (JLabel) e.getSource();
if (selectedLabel.getIcon() == blankIcon) {
return; // don't want to move the blank icon
}
// index variables to hold selected and blank JLabel's index location
int selectedIndex = -1;
int blankIndex = -1;
for (int i = 0; i < labelList.size(); i++) {
if (selectedLabel == labelList.get(i)) {
selectedIndex = i;
} else if (labelList.get(i).getIcon() == blankIcon) {
blankIndex = i;
}
}
// get row and column of selected JLabel
int row = selectedIndex / SIDES;
int col = selectedIndex % SIDES;
// get row and column of blank JLabel
int blankRow = blankIndex / SIDES;
int blankCol = blankIndex % SIDES;
if (isMoveValid(row, col, blankRow, blankCol)) {
Icon selectedIcon = selectedLabel.getIcon();
labelList.get(selectedIndex).setIcon(blankIcon);
labelList.get(blankIndex).setIcon(selectedIcon);
// test for win here by comparing icons held by labelList
// with the solutionList
}
}
private boolean isMoveValid(int row, int col, int blankRow, int blankCol) {
// has to be on either same row or same column
if (row != blankRow && col != blankCol) {
return false;
}
// if same row
if (row == blankRow) {
// then columns must be off by 1 -- they're next to each other
return Math.abs(col - blankCol) == 1;
} else {
// or else rows off by 1 -- above or below each other
return Math.abs(row - blankRow) == 1;
}
}
public void shuffle() {
Collections.shuffle(iconList);
for (int i = 0; i < labelList.size(); i++) {
labelList.get(i).setIcon(iconList.get(i));
}
}
}
private void fillIconList(BufferedImage img) {
// get the width and height of each individual icon
// which is 1/3 the image width and height
int w = img.getWidth() / SIDES;
int h = img.getHeight() / SIDES;
for (int row = 0; row < SIDES; row++) {
int y = (row * img.getWidth()) / SIDES;
for (int col = 0; col < SIDES; col++) {
int x = (col * img.getHeight()) / SIDES;
// create a sub image
BufferedImage subImg = img.getSubimage(x, y, w, h);
// create icon from the image
Icon icon = new ImageIcon(subImg);
// add to both icon lists
iconList.add(icon);
solutionList.add(icon);
}
}
// create a blank image and corresponding icon as well.
BufferedImage blankImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
blankIcon = new ImageIcon(blankImg);
iconList.remove(iconList.size() - 1); // remove last icon from list
iconList.add(blankIcon); // and swap in the blank one
solutionList.remove(iconList.size() - 1); // same for the solution list
solutionList.add(blankIcon);
}
private static void createAndShowGui(BufferedImage img) {
JFrame frame = new JFrame("ImageShuffle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ImageShuffle(img));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
URL imgUrl = null;
BufferedImage img;
try {
imgUrl = new URL(IMG_PATH);
img = ImageIO.read(imgUrl);
SwingUtilities.invokeLater(() -> createAndShowGui(img));
} catch (IOException e) {
e.printStackTrace();
}
}
}
If I wanted animation, again, I'd raise the icon into the JFrame's glasspane, animate it to the new position using a Swing Timer, and then place the icon into the new JLabel. I'd also disable the MouseListener using a boolean field, a "flag", until the animation had completed its move.

Related

Which Swing layout should i use for moving JButtons

I have a Board 14x14 which has JButtons and every Jbutton has a different color. When you click one of those buttons, it checks the neighbors with the same color and removes them. When it removes them, theres a blank space between the board so the above buttons, should move down to fill the blank space. I tried with GridLayout but I don't know how to move the above buttons.
This actually is a case where you can hardly use a layout manager at all.
A LayoutManager is supposed to compute the layout of all components at once. It is triggered by certain events (e.g. when the parent component is resized). Then it computes the layout and arranges the child components accordingly.
In your case, the situation is quite different. There is no layout manager that can sensibly represent the "intermediate" state that appears while the upper buttons are falling down. While the components are animated, they cannot be part of a proper layout.
The animation itself may also be a bit tricky, but can fortunately be solved generically. But you still have to keep track of the information about where each component (i.e. each button) is currently located in the grid. When one button is removed, you have to compute the buttons that are affected by that (namely, the ones directly above it). These have to be animated. After the animation, you have to assign the new grid coordinates to these buttons.
The following is a MCVE that shows one basic approach. It simply removes the button that was clicked, but it should be easy to generalize it to remove other buttons, based on other conditions.
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class FallingButtons
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int rows = 8;
int cols = 8;
GridPanel gridPanel = new GridPanel(rows, cols);
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
JButton button = new JButton(r+","+c);
gridPanel.addComponentInGrid(r, c, button);
button.addActionListener(e ->
{
Point coordinates = gridPanel.getCoordinatesInGrid(button);
if (coordinates != null)
{
gridPanel.removeComponentInGrid(
coordinates.x, coordinates.y);
}
});
}
}
f.getContentPane().add(gridPanel);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class GridPanel extends JPanel
{
private final int rows;
private final int cols;
private final JComponent components[][];
GridPanel(int rows, int cols)
{
super(null);
this.rows = rows;
this.cols = cols;
this.components = new JComponent[rows][cols];
addComponentListener(new ComponentAdapter()
{
#Override
public void componentResized(ComponentEvent e)
{
layoutGrid();
}
});
}
private void layoutGrid()
{
int cellWidth = getWidth() / cols;
int cellHeight = getHeight() / rows;
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
JComponent component = components[r][c];
if (component != null)
{
component.setBounds(
c * cellWidth, r * cellHeight, cellWidth, cellHeight);
}
}
}
}
Point getCoordinatesInGrid(JComponent component)
{
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
if (components[r][c] == component)
{
return new Point(r, c);
}
}
}
return null;
}
void addComponentInGrid(int row, int col, JComponent component)
{
add(component);
components[row][col] = component;
layoutGrid();
}
JComponent getComponentInGrid(int row, int col)
{
return components[row][col];
}
void removeComponentInGrid(int row, int col)
{
remove(components[row][col]);
components[row][col] = null;
List<Runnable> animations = new ArrayList<Runnable>();
for (int r=row-1; r>=0; r--)
{
JComponent component = components[r][col];
if (component != null)
{
Runnable animation =
createAnimation(component, r, col, r + 1, col);
animations.add(animation);
}
}
for (Runnable animation : animations)
{
Thread t = new Thread(animation);
t.setDaemon(true);
t.start();
}
repaint();
}
private Runnable createAnimation(JComponent component,
int sourceRow, int sourceCol, int targetRow, int targetCol)
{
int cellWidth = getWidth() / cols;
int cellHeight = getHeight() / rows;
Rectangle sourceBounds = new Rectangle(
sourceCol * cellWidth, sourceRow * cellHeight,
cellWidth, cellHeight);
Rectangle targetBounds = new Rectangle(
targetCol * cellWidth, targetRow * cellHeight,
cellWidth, cellHeight);
Runnable movement = createAnimation(
component, sourceBounds, targetBounds);
return () ->
{
components[sourceRow][sourceCol] = null;
movement.run();
components[targetRow][targetCol] = component;
repaint();
};
}
private static Runnable createAnimation(JComponent component,
Rectangle sourceBounds, Rectangle targetBounds)
{
int delayMs = 10;
int steps = 20;
Runnable r = () ->
{
int x0 = sourceBounds.x;
int y0 = sourceBounds.y;
int w0 = sourceBounds.width;
int h0 = sourceBounds.height;
int x1 = targetBounds.x;
int y1 = targetBounds.y;
int w1 = targetBounds.width;
int h1 = targetBounds.height;
int dx = x1 - x0;
int dy = y1 - y0;
int dw = w1 - w0;
int dh = h1 - h0;
for (int i=0; i<steps; i++)
{
double alpha = (double)i / (steps - 1);
int x = (int)(x0 + dx * alpha);
int y = (int)(y0 + dy * alpha);
int w = (int)(w0 + dw * alpha);
int h = (int)(h0 + dh * alpha);
SwingUtilities.invokeLater(() ->
{
component.setBounds(x, y, w, h);
});
try
{
Thread.sleep(delayMs);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
}
SwingUtilities.invokeLater(() ->
{
component.setBounds(x1, y1, w1, h1);
});
};
return r;
}
}
You could try using a 2-dimensional array of JButtons
JButton[][] buttons = new JButton[14][14];
for (int i=0; i < buttons.length; i++) {
for (int j=0; j < buttons[i].length; j++) {
buttons[i][j] = new JButton("Button [" + i + "][" + j + "]");
}
}
// Then do whatever,remove,change color,check next element in array
// and compare colors etc
buttons[2][3].setText("changed text");
If you want the above buttons to take more space to fill the empty space when you remove a component well, this is not possible using GridLayout, but you can add some empty components like JLabels to fill the space.
You can add a component in a container at a specific index for this purpose, by using Container's add (Component comp, int index) method.
This code snippet will replace a button at a specified index (45, just for example) with a blank component in a panel which has a GridLayout set:
JPanel boardPanel = new JPanel (new GridLayout (14, 14));
// ... add your buttons ...
// This code could be invoked inside an ActionListener ...
boardPanel.remove (45);
boardPanel.add (new JLabel (""), 45);
boardPanel.revalidate ();
boardPanel.repaint ();
This way, the rest of the components will not move, and you will just see a blank space replacing your button.
You can achieve more: if you add the empty label at index = 0, all the buttons will move to the right (remember that the number of components should not change, else the components will resize and you could obtain bad behaviour), and so on, you can "move" a single component by simply removing it and adding it at a different index.
Another way to go would be to store a 2-dimensional array of objects representing your model logic (you can store color and all the stuff you need), and painting them on your own by overriding paintComponent method.
For an example of a custom painting approach, take a look at this MadProgrammer's answer, where he shows how to highlight a specific cell in a grid (in this case he uses a List to store objects, but a 2d array will work as well).

Java Game of Life using Swing

I am trying to make a replica of the Game of Life using swing, I admit i have used code from some 1 else as i am trying to get my head around it and then proceed with my own implementation. I have some understanding of their code, yet i wanted to implements 2 additional features to their code. However i am finding that the way it is written is posing problems as i wanted to add a MouseListener(To make a cell come to life when clicked) and WindowListener(To make a start,pause and resume button).
I do understand how they work to some extent, yet i need your help to get my headaround it.
Here is the code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.Transient;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class ConwaysGameOfLife extends JPanel implements MouseListener{
private int[][] cellsGrid; // grid is the size of the 2d array
private static final Random rnd = new Random(); // make a new random generator
private int generationCounter; // counter for the generation
public ConwaysGameOfLife(int width, int height) {
this.cellsGrid = new int[width / 4][height / 4];// divides by 4 whatever the width and height set is
setupGrid();
}// new method for creating the game with input sizes for the size of the game window
/*The grid consists fully of cells, the grid size is divided by 4 to make the cells
* setupGrid makes the grid of cells
*
* */
private void setupGrid() {
for (int[] row : cellsGrid) {
for (int j = 0; j < row.length; j++) {
if (rnd.nextDouble() < 0.92)
continue;
row[j] = rnd.nextInt(2);
//
}
}
}
/*
* applies the rule to the existing cells changing their state depending on the position to neighbors set in the rules
* */
public void updateGrid() {
for (int i = 0; i < cellsGrid.length; i++) {
for (int j = 0; j < cellsGrid[i].length; j++) {
applyRule(i, j);
}
}
}
// Rules of game of life cells iterations
private void applyRule(int i, int j) {
int left = 0, right = 0, up = 0, down = 0;
int dUpperLeft = 0, dUpperRight = 0, dLowerLeft = 0, dLowerRight = 0;
//this shows the 8 possible neighbors in terms of position
if (j < cellsGrid.length - 1) {
right = cellsGrid[i][j + 1];
if(i>0)
dUpperRight = cellsGrid[i - 1][j + 1];
if (i < cellsGrid.length - 1)
dLowerRight = cellsGrid[i + 1][j + 1];
}
if (j > 0) {
left = cellsGrid[i][j - 1];
if (i > 0)
dUpperLeft = cellsGrid[i - 1][j - 1];
if (i< cellsGrid.length-1)
dLowerLeft = cellsGrid[i + 1][j - 1];
}
if (i > 0)
up = cellsGrid[i - 1][j];
if (i < cellsGrid.length - 1)
down = cellsGrid[i + 1][j];
int sum = left + right + up + down + dUpperLeft + dUpperRight
+ dLowerLeft
+ dLowerRight;
if (cellsGrid[i][j] == 1) {
if (sum < 2)
cellsGrid[i][j] = 0;
if (sum > 3)
cellsGrid[i][j] = 0;
}
else {
if (sum == 3)
cellsGrid[i][j] = 1;
}
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(cellsGrid.length * 4, cellsGrid[0].length * 4);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Color gColor = g.getColor();
g.drawString("Generation: " + generationCounter++, 0, 10);
for (int i = 0; i < cellsGrid.length; i++) {
for (int j = 0; j < cellsGrid[i].length; j++) {
if (cellsGrid[i][j] == 1) {
g.setColor(Color.black); // change colour
g.fillRect(j * 8, i * 8, 8, 8); // change size of cells
}
}
}
g.setColor(gColor);
//paint the cells to a colour
}
public static void main(String[] args) {
final ConwaysGameOfLife c = new ConwaysGameOfLife(800, 800);
JFrame frame = new JFrame();
frame.getContentPane().add(c);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
JButton start=new JButton("START");
/* This method specifies the location and size
* of button. In method setBounds(x, y, width, height)
* x,y) are cordinates from the top left
* corner and remaining two arguments are the width
* and height of the button.
*/
start.setBounds(80,0,80,20);
//Adding button onto the frame
//frame.add(start);
new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frame.add(start);
c.updateGrid();
c.repaint();
}
}).start();
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
First of all, The button flickers, and only appears when hovered over with the mouse, still flickering. Then i want the iterations to begin after start button is pressed, and pause button to pause it, and resume(Have a decent idea how it would work, but not how to implement it with the structure of swing that is done in this code.)
Secondly, I wanted the cells to come to life when they are pressed with the mouse, But, i am unsure how to implement the mouseListener to do this.
I tried something like cellsGrid[i][j] = 1; when clicked by mouse but i get errors, which is due to my lack of understanding of the implementation of cellsGrid.
I am not expecting solutions to the problem, I would like some guidance to understand the Listeners better and maybe how to make this simpler to understand for me. Thank You :)
Your simulation has a model that is a grid of cells; it has a view that paints an 8 x 8 square to represent a cell in the grid. As suggested here, you can map model and view coordinates using linear interpolation. In particular, given the following proportions, you can cross-multiply and solve for the missing coordinate.
view.x : panelWidthInPixels :: model.x : modelXRange
view.y : panelHeightInPixels :: model.y : modelYRange
For reference, this complete example maps mouse coordinates to pixel coordinates in an image. A complete example of John Conway’s Game of Life in Java Swing is cited here.

Java checkerboard, odd/even % 2 panel

I have successfully got a checkerboard to be put together using colored panels, but only when the user will input odd numbers for the rows and columns. Otherwise when inputing even numbers it just shows alternating colored columns. I'm struggling to figure out how to write a short segment that checks to see if it's odd or even by using the %2=0, with the result of even changing the color. Below is my code. Thanks, and take it easy on me I'm very new to programming! :-)
Also, I've created a separate ColorPanel class to build the colored panel, and then pull into into my main program. I didn't bother putting that code below.
import javax.swing.*;
import java.awt.*;
public class Checkerboard extends JPanel{
public static void main(String[] args) {
JFrame chBoard = new JFrame();
chBoard.setTitle("Checkerboard");
chBoard.setSize(800,800);
chBoard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String inputStr = JOptionPane.showInputDialog("Number of rows", "5");
if (inputStr == null) return;
int row = Integer.parseInt(inputStr);
inputStr = JOptionPane.showInputDialog("Number of columns", "5");
if (inputStr == null) return;
int col = Integer.parseInt(inputStr);
Container pane = chBoard.getContentPane();
pane.setLayout(new GridLayout(row, col));
Color BoxColor = Color.red;
for ( int counter = 1; counter <= row * col; counter++ )
{
if (BoxColor == Color.red)
BoxColor = Color.black;
else
BoxColor = Color.red;
ColorPanel panel = new ColorPanel(BoxColor);
pane.add(panel);
}
chBoard.setVisible(true);
}
}
Change your loop to:
for ( int x = 0; x < row; x++ ) {
for(int y = 0; y < col; y++) {
if((x + y)%2 == 0) {
BoxColor = Color.red;
} else {
BoxColor = Color.black;
}
...
}
}
Like I said, I'm new to programming but I'm really enjoying the learning experience. I hope this helps other people in their learning experience.
Anyways, I suppose I created more work for myself with the separate ColorPanel class. So instead of creating a separate ColorPanel class to build the colored panel, I just changed it to use the preexisting JPanel class to create the panel inside the main program. So instead of:
ColorPanel panel = new ColorPanel(BoxColor);
+ the ColorPanel class...
I put:
JPanel panel = new JPanel();
panel.setBackground(BoxColor);
within the main program and deleted the additional ColorPanel class.
Sorry for the redundancy, just wanting to explain myself clearly.
Also, thanks to Jason he really helped me figure out the idea of using the two
int x & y
to count the
row & col
and then add them together which enabled me to use the
%2=0
to figure out whether I was on the odd or even panel.
Hope this helps someone! :-)
Final code looks like this:
import javax.swing.*;
import java.awt.*;
public class Checkerboard extends JPanel{
public static void main(String[] args) {
JFrame chBoard = new JFrame();
chBoard.setTitle("Checkerboard");
chBoard.setSize(800,800);
chBoard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String inputStr = JOptionPane.showInputDialog("Number of rows", "5");
if (inputStr == null) return;
int row = Integer.parseInt(inputStr);
inputStr = JOptionPane.showInputDialog("Number of columns", "5");
if (inputStr == null) return;
int col = Integer.parseInt(inputStr);
Container pane = chBoard.getContentPane();
pane.setLayout(new GridLayout(row, col));
Color BoxColor = Color.red;
for ( int x = 0; x < row; x++ ) {
for(int y = 0; y < col; y++) {
if((x + y)%2 == 0) {
BoxColor = Color.red;}
else{
BoxColor = Color.black;}
JPanel panel = new JPanel();
panel.setBackground(BoxColor);
pane.add(panel);
}
chBoard.setVisible(true);
}
}
}

Java keylistener grid game

So I am working on a game where an actor is moved by the user in a grid, and if the space the user is trying to move the actor to of a certain type, then it can be moved to. The grid is made up of a class that I know works called PGrid, so the grid is made of PGrids. The problem is the keyListener does not function at all, not even print out 'hi.' Below is the code for PGame and moveGrid, where PGame handles moveGrid stuff but moveGrid draws out the grid (as it is the JPanel). I tried moving the keylistener from PGame to moveGrid, but it did not work.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
//where all the different classes are put together
public class PGame implements KeyListener{ //may want to move the listener to moveGrid
private static moveGrid panel = new moveGrid(8,8); //taking something from moveGrid
private static PActor pguy = new PActor("Bill", 2, 2);
private boolean shallmove = false;
private int newx, newy;
public static void main(String[] args){
//create a new frame
JFrame frame = new JFrame("PGame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//moveGrid panel = new moveGrid(8,8); //taking something from moveGrid
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
panel.requestFocus();
//creating a "path" of places able to move to
moveGrid.pgrids.get(0).changeType(1);
moveGrid.pgrids.get(1).changeType(1);
moveGrid.pgrids.get(2).changeType(1);
moveGrid.pgrids.get(3).changeType(1);
moveGrid.pgrids.get(10).changeType(1);
moveGrid.pgrids.get(11).changeType(1);
moveGrid.pgrids.get(19).changeType(1);
moveGrid.pgrids.get(27).changeType(1);
//moveGrid.pgrids.get(4).changeType(2);
//start our pguy out in a position
PGrid pguystart = new PGrid(2,0,0);
moveGrid.pgrids.set(0,pguystart);
panel.repaint();
}
public void keyPressed(KeyEvent e) {
//here test if the grid can be updated
//Test:
pguy.canMove(3,3);
pguy.Move(4,3,3);
if(e.getKeyCode() == KeyEvent.VK_UP){
newx = pguy.getx();
newy = pguy.gety() - 1;
shallmove = pguy.canMove(newx,newy);
System.out.println("Hi");
}else if(e.getKeyCode() == KeyEvent.VK_DOWN){
newx = pguy.getx();
newy = pguy.gety() + 1;
shallmove = pguy.canMove(newx,newy);
} else if(e.getKeyCode() == KeyEvent.VK_LEFT){
newx = pguy.getx() - 1;
newy = pguy.gety();
shallmove = pguy.canMove(newx,newy);
}else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
newx = pguy.getx() + 1;
newy = pguy.gety();
shallmove = pguy.canMove(newx,newy);
}
}
public void keyReleased(KeyEvent e) {
System.out.println("Hi");
//update the grid if it can here
//somewhere in here add this:
//moveGrid.repaint(); //tell movegrid to repaint
if(shallmove = true){
//change a certain spot to the actor
PGrid temp = new PGrid(2,newx,newy);
moveGrid.pgrids.set(pguy.getplace(),temp);
//need to also change to old space to be back to what it was....
//*here*
pguy.Move(pguy.newPos,newx, newy);
panel.repaint();
}
}
public void keyTyped(KeyEvent e) { }
}
moveGrid:
//a grid in which stuff can move
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class moveGrid extends JPanel {
private int height;
private int width;
private int newx, newy;
private static PActor pguy = new PActor("Bill", 2, 2);
private boolean shallmove = false;
public static ArrayList<PGrid> pgrids = new ArrayList<PGrid>(); //an array full of grid boxes with type PGrid
public moveGrid(int height, int width){
this.height = height;
this.width = width;
setPreferredSize(new Dimension(800, 800));
//make all the values in pgrids equal to "Water" and give them locations
int i = 0;
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
PGrid pnull = new PGrid(0, x, y);
pgrids.add(i, pnull);
i++;
}
}
//drawGrid();
}
/*public void drawGrid(Graphics g){
g.drawRect(x,y,20,20);
} */
public void addNotify() {
super.addNotify();
requestFocus();
}
public void paintComponent(Graphics g){
//PGrid curLoc = new PGrid(height, height, height);
//go through and draw out the grid
int q = 0;
int midx = 0; //need to make these somehow so the squares get drawn at the center
int midy = 0;
for(int qh = 0; qh < height; qh++){
for(int qw = 0; qw < width; qw++){
PGrid pcur = pgrids.get(q); //
int p = pcur.getType();
if(p == 0){
//may want to import a water looking image
g.setColor(Color.BLUE);
g.fillRect((40*qw)+midx,(40*qh)+midy,40,40);
g.setColor(Color.BLACK);
g.drawRect((40*qw)+midx,(40*qh)+midy,40,40);
}else if(p == 1){
//may want to import a better image
g.setColor(Color.GREEN);
g.fillRect((40*qw)+midx,(40*qh)+midy,40,40);
}else if(p == 2){
//draws the "character"
g.setColor(Color.ORANGE);
g.fillRect((40*qw)+midx,(40*qh)+midy,40,40);
}
q++;
}
}
//here draw the character in the proper position
//so like multiply the x and y by 40
}
}
I also may have an error in the PActor class, which is supposedly the Actor that moves.
public class PActor {
private String name;
private int curx, cury;
int newPos;
public PActor(String name, int curx, int cury){
this.name = name;
this.curx = curx;
this.cury = cury;
}
public boolean canMove(int x, int y){
boolean abletomove = false;
//test if the space that the user is trying to moveto can be moved to
//use indexOf(a temp variable with values x and y also with type 1) to test
PGrid togo = new PGrid(1,x,y);
//now scan through pgrids in moveGrid to see the desired spot can be moved to
for(int s = 0; s <= moveGrid.pgrids.size(); s++){
PGrid temp = moveGrid.pgrids.get(s);
//test if the temp space is equal
if((togo.getType() == temp.getType()) && (togo.getx() == temp.getx()) && (togo.gety() == temp.gety())){
abletomove = true;
newPos = s;
break; //stop scanning, as it is now unnecessary
}
else{ //do nothing
}
}
//now test pgrids to see if there is a spot like such that is moveable
return abletomove;
}
public int getplace(){
return newPos;
}
public int getx(){
return curx;
}
public int gety(){
return cury;
}
public void Move(int pos, int x, int y){
PGrid temp = new PGrid(2,x,y);
moveGrid.pgrids.set(pos,temp);
}
public String toString(){
return name + " ";
}
}
Suggestions:
Again, for any Java Swing listener to work, the listener must be added to a component. For instance for a KeyListener to work, you first need to add it to the component that you wish to have listened to by calling addKeyListener(myKeyListener) on that component.
For a KeyListener to work, the component being listened to must have the keyboard focus. This means that if your'e listening to a JPanel, you first have to make it focusable by calling setFocusable(true) on the JPanel and then you need to request focus, such as by calling requestFocusInWindow() on it.
If something later steals the focus, such as a JButton that has been pushed or a text component, then the KeyListener will no longer work.
To get around this, we usually recommend that you use Key Bindings in place of KeyListeners, but note that the set up of these is completely different from that of KeyListeners, and you would need to throw all assumptions to the side and study the tutorial on use of these first.
If still stuck, then you will want to create and post a minimal example program, a small program that has a lot less code than the program you've posted, yet that compiles for us, runs, and that shows us directly your problem.
Links:
KeyListener Tutorial
Key Bindings tutorial

JPanel inside a JScrollPane

I have a JPanel and I create, dynamically, JCheckBoxes inside.
These have to be added JCheckBoxes always a side by side. In case there is more space to be inserted in the side, a new line of JCheckBoxes is created, as in a simple text editor.
This is happening perfectly. But ...
I set the layout on this JPanel to FlowLayout, exactly what I want.
The obvious problem is that a window has limited space. So a good solution to this is: Insertion of this JPanel in a JScrollPane,l and making that happen only in the vertical scrolling.
But I have problems. Although you can make only a vertical scroll bar to appear, the items are always added "forever" side by side. And the vertical scroll simply does not work, only horizontally.
I've tried many ways to make the scroll only vertically, but nothing worked (if it had worked I would not be here:]).
So, has anyone had any similar problem, and can help me?
I shall be very grateful to those who help me.
No more.
I dealt with the same issue with ScrollPanes and FlowLayouts. I found the best solution is to use a modified version of FlowLayout that takes into account vertical changes. Here is the code for such a layout. You can include it in your project and call it just like a FlowLayout, however it will actually work nice with a scrollpane.
import java.awt.*;
/**
* A modified version of FlowLayout that allows containers using this
* Layout to behave in a reasonable manner when placed inside a
* JScrollPane
* #author Babu Kalakrishnan
* Modifications by greearb and jzd
*/
public class ModifiedFlowLayout extends FlowLayout {
public ModifiedFlowLayout() {
super();
}
public ModifiedFlowLayout(int align) {
super(align);
}
public ModifiedFlowLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
public Dimension minimumLayoutSize(Container target) {
// Size of largest component, so we can resize it in
// either direction with something like a split-pane.
return computeMinSize(target);
}
public Dimension preferredLayoutSize(Container target) {
return computeSize(target);
}
private Dimension computeSize(Container target) {
synchronized (target.getTreeLock()) {
int hgap = getHgap();
int vgap = getVgap();
int w = target.getWidth();
// Let this behave like a regular FlowLayout (single row)
// if the container hasn't been assigned any size yet
if (w == 0) {
w = Integer.MAX_VALUE;
}
Insets insets = target.getInsets();
if (insets == null){
insets = new Insets(0, 0, 0, 0);
}
int reqdWidth = 0;
int maxwidth = w - (insets.left + insets.right + hgap * 2);
int n = target.getComponentCount();
int x = 0;
int y = insets.top + vgap; // FlowLayout starts by adding vgap, so do that here too.
int rowHeight = 0;
for (int i = 0; i < n; i++) {
Component c = target.getComponent(i);
if (c.isVisible()) {
Dimension d = c.getPreferredSize();
if ((x == 0) || ((x + d.width) <= maxwidth)) {
// fits in current row.
if (x > 0) {
x += hgap;
}
x += d.width;
rowHeight = Math.max(rowHeight, d.height);
}
else {
// Start of new row
x = d.width;
y += vgap + rowHeight;
rowHeight = d.height;
}
reqdWidth = Math.max(reqdWidth, x);
}
}
y += rowHeight;
y += insets.bottom;
return new Dimension(reqdWidth+insets.left+insets.right, y);
}
}
private Dimension computeMinSize(Container target) {
synchronized (target.getTreeLock()) {
int minx = Integer.MAX_VALUE;
int miny = Integer.MIN_VALUE;
boolean found_one = false;
int n = target.getComponentCount();
for (int i = 0; i < n; i++) {
Component c = target.getComponent(i);
if (c.isVisible()) {
found_one = true;
Dimension d = c.getPreferredSize();
minx = Math.min(minx, d.width);
miny = Math.min(miny, d.height);
}
}
if (found_one) {
return new Dimension(minx, miny);
}
return new Dimension(0, 0);
}
}
}

Categories

Resources