I'm trying to make a somewhat slider puzzle kind of game where you click 1 image, then click another and they swap positions. For some reason this works correctly the first time you do it, but when you go to swap an image for the second time and every time after that it selects a different element than the one that you clicked on. Any help would be appreachated, thank you.
public class test extends Application {
int click1 = -1, click2 = -1;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
//Create a GridPane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
//create ArrayList and add imageList to ArrayList
ArrayList<ImageView>imageList = new ArrayList<ImageView>();
for (int i = 0; i < 9; i++) {
imageList.add(new ImageView ((i) +".jpg"));
}
addImages(imageList, pane);
//add onClick listeners to each image
imageList.get(0).setOnMouseClicked(e->{
swap(0, imageList, pane);
});
imageList.get(1).setOnMouseClicked(e->{
swap(1, imageList, pane);
});
imageList.get(2).setOnMouseClicked(e->{
swap(2, imageList, pane);
});
imageList.get(3).setOnMouseClicked(e->{
swap(3, imageList, pane);
});
imageList.get(4).setOnMouseClicked(e->{
swap(4, imageList, pane);
});
imageList.get(5).setOnMouseClicked(e->{
swap(5, imageList, pane);
});
imageList.get(6).setOnMouseClicked(e->{
swap(6, imageList, pane);
});
imageList.get(7).setOnMouseClicked(e->{
swap(7, imageList, pane);
});
imageList.get(8).setOnMouseClicked(e->{
swap(8, imageList, pane);
});
//display the scene
Scene scene = new Scene(pane, 650, 650);
primaryStage.setScene(scene);
primaryStage.setTitle("Test");
primaryStage.show();
}
private void swap(int lastClick, ArrayList<ImageView> imageList, GridPane pane) {
if (click1 == -1) {
click1 = lastClick;
System.out.println(imageList.get(click1).getImage().impl_getUrl()+ " ");
imageList.get(click1).setScaleX(1.02);
imageList.get(click1).setScaleY(1.02);
} else {
click2 = lastClick;
System.out.println(imageList.get(click2).getImage().impl_getUrl()+ " ");
//swap indexes in ArrayList
Collections.swap(imageList, click2, click1);
pane.getChildren().removeAll(imageList);
addImages(imageList, pane);
//reset everything for next swap
imageList.get(click1).setScaleX(1.0);
imageList.get(click1).setScaleY(1.0);
imageList.get(click2).setScaleX(1.0);
imageList.get(click2).setScaleY(1.0);
click1 = -1;
click2 = -1;
}
}
private void addImages(ArrayList<ImageView> imageList, GridPane pane) {
//add imageList to the GridPane
int i = 0;
while (i < 9) {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
pane.add(imageList.get(i), k, j);
i++;
}
}
}
}
}
You need to update the mouseclick listener once you update the new element. What ends up happening is a stale state. An item that has moved reports its initial position rather than its actual position, resulting in your items moving around to where they shouldn't be.
Basic State.
0, 1, 2, 3, 4, 5, 6, 7, 8
Assume we click 3 and then 5. The request is to swap image 5 to image 3's slot, and image 3 to image 5's slot.
0, 1, 2, 5, 4, 3, 6, 7, 8
This looks right for now, but let's try another swap. Let's swap image 1 with 5.
0, 3, 2, 5, 4, 1, 6, 7, 8
What happened?! 1 and 5 were clicked, but 3 and 1 swapped places instead!
The move on the back-end worked actually worked exactly as designed. Image 5 and Image 1 were clicked, resulting in the operation "swap slot 5 with slot 1, instead of the intended slot 3 with slot 1. The issue with that is that the image in slot 5 is actually image 3.
The rectification for this is to update the mouse click listeners to know where they are afterwards, so they can report their new, updated positions.
Fix would be:
imageList.get(click1).setOnMouseClicked(e->{
swap(click1, imageList, pane);
});
imageList.get(click2).setOnMouseClicked(e->{
swap(click2, imageList, pane);
});
This tells the swapped items that "This is where you are moved, and from now on you should report that this is your new moved position."
Related
I made a GUI TicTacToe game years ago and wanted to redo it since I now have more programming skills. I was able to shrink the code from 600 lines to around 150 lines.
While I used the same scheme, I ran into some problems that I couldn't solve myself, so please help me out.
The program consists of two classes, the main class TTTMain:
public class TTTMain {
public static void main(String[] args) {
TTTFrame tttf = new TTTFrame(0,0);
/*Tic Tac Toe Field:
* 0 1 2
* 3 4 5
* 6 7 8
*/
}}
And TTTFrame:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TTTFrame extends JFrame implements ActionListener {
private Button[] btnPlayButton;
private Button btnRestart;
private int buttonCounter;
private int xScore;
private int oScore;
private Label Olabel, Xlabel;
TTTFrame(int xScore, int oScore) {
this.xScore = xScore;
this.oScore = oScore;
btnPlayButton = new Button[9];
for (int i = 0; i < 9; i++) {
btnPlayButton[i] = new Button("" + i);
btnPlayButton[i].setBackground(Color.white);
btnPlayButton[i].setForeground(Color.white);
btnPlayButton[i].addActionListener(this);
this.add(btnPlayButton[i]);
}
Xlabel = new Label("X: " + this.xScore);
Xlabel.setFont(new Font("Arial", Font.BOLD, 24));
Xlabel.setForeground(Color.white);
Xlabel.setBackground(Color.black);
this.add(Xlabel);
btnRestart = new Button("Restart");
btnRestart.setActionCommand("Restart");
btnRestart.setFont(new Font("Arial", Font.PLAIN, 18));
btnRestart.addActionListener(this);
this.add(btnRestart);
Olabel = new Label("O: " + this.oScore);
Olabel.setFont(new Font("Arial", Font.BOLD, 24));
Olabel.setForeground(Color.white);
Olabel.setBackground(Color.black);
this.add(Olabel);
this.setLayout(new GridLayout(4, 3));
this.pack();
this.setResizable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Tic Tac Toe");
this.setSize(300, 400);
this.getContentPane().setBackground(Color.black);
this.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Restart")) {
System.out.println("Restarted");
for (int i = 0; i < 9; i++) {
btnPlayButton[i].setLabel("" + i);
btnPlayButton[i].setForeground(Color.white);
btnPlayButton[i].setBackground(Color.white);
btnPlayButton[i].addActionListener(this);
this.buttonCounter = 0;
}
} else {
((Button) e.getSource()).setFont(new Font("Arial", Font.BOLD, 48));
((Button) e.getSource()).setForeground(Color.black);
System.out.println(buttonCounter);
if (buttonCounter % 2 == 0) {
((Button) e.getSource()).setLabel("X");
((Button) e.getSource()).removeActionListener(this);
} else {
((Button) e.getSource()).setLabel("O");
((Button) e.getSource()).removeActionListener(this);
}
buttonCounter++;
CheckField();
}
}
private void CheckField() {
if (ButtonsWithIdenticalLabels(0, 1, 2)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(3, 4, 5)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(6, 7, 8)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(0, 3, 6)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(1, 4, 7)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(2, 5, 8)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(0, 4, 8)) {
Deactivatebuttons();
}
if (ButtonsWithIdenticalLabels(2, 4, 6)) {
Deactivatebuttons();
}
}
private boolean ButtonsWithIdenticalLabels(int i, int j, int k) {
if (btnPlayButton[i].getLabel() == btnPlayButton[j].getLabel()
&& btnPlayButton[j].getLabel() == btnPlayButton[k].getLabel()) {
btnPlayButton[i].setBackground(Color.red);
btnPlayButton[j].setBackground(Color.red);
btnPlayButton[k].setBackground(Color.red);
if (btnPlayButton[i].getLabel().equals("X")) {
xScore++;
Xlabel.setText("X: " + xScore);
} else {
oScore++;
Olabel.setText("O: " + oScore);
}
return true;
} else {
return false;
}
}
private void Deactivatebuttons() {
for (int i = 0; i < 9; i++) {
btnPlayButton[i].removeActionListener(this);
}
}
}
Now let me explain how the program works. The 3x3 playing field is made of the ButtonArray btnPlayButton. The buttons are compared by their Labels, so to not have matching labels at the start of the game the buttons are labeled from 1 to 9 right when they are created. Here:
for (int i = 0; i < 9; i++) {
btnPlayButton[i] = new Button("" + i); // Right here
btnPlayButton[i].setBackground(Color.white);
btnPlayButton[i].setForeground(Color.white);
btnPlayButton[i].addActionListener(this);
this.add(btnPlayButton[i]);
}
Whenever you click a btnPlayButton , the program jumps into the actionPerformed method. Since the btnPlayButtons don't have an ActionCommand , it jumps right into the else section of the method. Here, int buttonCounter gets greater by 1. Wether buttonCounter is even or odd, the btnPlayButton that got clicked gets relabeled with "X" or "O". Since buttonCounter gets +1 with every click, the X and Os are alternating.
Here is said section:
else {
((Button) e.getSource()).setFont(new Font("Arial", Font.BOLD, 48));
((Button) e.getSource()).setForeground(Color.black);
System.out.println(buttonCounter);
if (buttonCounter % 2 == 0) {
((Button) e.getSource()).setLabel("X");
((Button) e.getSource()).removeActionListener(this);
} else {
((Button) e.getSource()).setLabel("O");
((Button) e.getSource()).removeActionListener(this);
}
buttonCounter++;
CheckField();
}
The ActionListener of the clicked Button are removed to prevent cheating. With every buttonpress, the playing field is checked for a winning combination. This happens in CheckField().
In CheckField(), or to be more precisely, ButtonsWithIdenticalLabels(x, y, z) the labels of btnPlayButtons[x], btnPlayButtons[y], btnPlayButtons[z] are taken and compared, if they are identical it returns true.
Since the btnPlayButton are ordered like this:
0 1 2
3 4 5
6 7 8
the winning combinations are: 012,345,678,036,147,258,045 and 246
so, for example, when btnPlayButton[0], btnPlayButton[1] and btnPlayButton[2] all have the same label. ButtonsWithIdenticalLabels is true and the program jumps into Deactivatebuttons() where all the btnPlayButton are getting disabled meaning a winning combination was found and the game is over. If the label of btnPlayButton[1] is "X" then int xScore gets 1 added to it. Also btnPlayButton[0], btnPlayButton[1] and btnPlayButton[2] get painted red for aesthetics.
With the Restart button you jump into a for loop that relabels the btnPlayButton again and adds them the ActionListener that is implemented into TTTFrame. buttonCounter is getting resetted to 0 as well. The relabeling is the same as the one in the beginning of the class:
if (e.getActionCommand().equals("Restart")) {
System.out.println("Restarted");
for (int i = 0; i < 9; i++) {
btnPlayButton[i].setLabel("" + i);
btnPlayButton[i].setForeground(Color.white);
btnPlayButton[i].setBackground(Color.white);
btnPlayButton[i].addActionListener(this);
this.buttonCounter = 0;
}
Now the problem is that I have is, that after a few restarts, the labeling of X and O isn't alternating anymore. Sometimes there are 3 Os in a row and sometimes even Fields like this are getting recognized as a win
Picture
If someone knows how to fix this bug I'd be really happy.
Thanks in advance,
Fihdi
The problem here is: When you restart the game, a new ActionListener is added to every button. However, it's only removed when you either click it or when someone wins the game. That means when you restart a game before anyone won, every non-clicked button gets a second ActionListener, so the click will be registered twice and this bug appears. Try to call DeactivateButtons() before you reset the board.
The solution is probably staring at me in the face. But here's my problem.
Take a look at this method:
public void showUserData() {
int numUsers = usersModel.numberOfUsers(); // number of users(rows) in the database
ColumnConstraints column1 = new ColumnConstraints();
column1.setPercentWidth(40);
ColumnConstraints column2 = new ColumnConstraints();
column2.setPercentWidth(60);
int counter = 0;
for(int i = 0; i <= numUsers - 1; i++) {
subGrid = new GridPane();
subGrid.getColumnConstraints().addAll(column1, column2);
userImage = new ImageView();
subGrid.setStyle("-fx-background-color: #dddddd;");
subGrid.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("mouse click detected! " + mouseEvent.getSource());
subGrid.setStyle("-fx-background-color: blue;");
}
});
vbox = new VBox();
vbox.getChildren().add(new Label("Name: " + getUserData().get(counter)));
vbox.getChildren().add(new Label("Username: " + getUserData().get(counter + 1)));
vbox.getChildren().add(new Label("Position: " + getUserData().get(counter + 2)));
subGrid.add(vbox, 1, 0);
user = new Image("file:///" + getUserImage().get(i));
userImage.setFitWidth(150);
userImage.setFitHeight(150);
userImage.setSmooth(true);
userImage.setPreserveRatio(true);
userImage.setImage(user);
subGrid.add(userImage, 0, 0);
if(i % 2 == 0 && i == 0) {
mainGrid.add(subGrid, 0, 0);
} else if (i % 2 == 0 && i != 0){
mainGrid.add(subGrid, 0, i - 1);
} else {
mainGrid.add(subGrid, 1, i - 1);
}
scroll.setContent(mainGrid);
counter = counter + 3;
}
}
I know its a mess but hear me out. I have a ScrollPane, and inside is a GridPane that spans the entire area of the ScrollPane. In every cell of the GridPane, there is another smaller GridPane. In each of these smaller GridPanes, the left column is an ImageView and the right is a VBox with labels in it.
The long and the short of this method is that:
a. It takes the number of rows in the database.
b. It creates a subGrid for every row in the database (corresponding to the number of users in the table).
c. It creates the ImageViews and VBoxes for each of the subGrids and adds them in.
d. It then takes the subGrids and adds it to the mainGrid.
This process works. But the listener only works on the latest subGrid that was added. i.e. second subGrid lights up, first does not, third subGrid lights up, second does not, etc.
I'm not sure if TableView can work with this. That's why I tried it with GridPane first. Why does my listener not work?
I ama beginner at this. I know the method is a mess. I know I need to refactor some things. But as long, as I can get this to work, I will happily clean it up afterwards. Any help appreciated, thank you.
I am currently working on a project, and I cannot find just any solution. I want a Gridpane row height to be dynamically counted from the width of one specific column inside that row. The whole Gridpane must be resizable and the rest of the available space should take another row below so the aspect ratio of that one cell is preserved according to the element inside that cell. Any ideas boys?
click for a pic
public class GridPaneControl extends GridPane {
private int index;
private Label startLabel;
private Label endLabel;
private HBox inputCellsContainer;
private HBox outputCellsContainer;
private Button inputOutputNextButton;
private Button inputOutputPreviousButton;
public GridPaneControl() {
setGridLinesVisible(true);
initialization();
ColumnConstraints cc0 = new ColumnConstraints();
cc0.setMaxWidth(Double.MAX_VALUE);
cc0.setHgrow(Priority.NEVER);
ColumnConstraints cc1 = new ColumnConstraints();
cc1.setMaxWidth(Double.MAX_VALUE);
ColumnConstraints cc2 = new ColumnConstraints();
cc2.setMaxWidth(Double.MAX_VALUE);
cc2.setHgrow(Priority.NEVER);
RowConstraints rc0 = new RowConstraints();
rc0.setVgrow(Priority.ALWAYS);
rc0.setFillHeight(false);
rc0.setMaxHeight(Double.MAX_VALUE);
RowConstraints rc1 = new RowConstraints();
rc1.setVgrow(Priority.ALWAYS);
rc1.setFillHeight(false);
rc1.setMaxHeight(Double.MAX_VALUE);
RowConstraints rc2 = new RowConstraints();
rc2.setVgrow(Priority.NEVER);
RowConstraints rc3 = new RowConstraints();
rc3.setVgrow(Priority.ALWAYS);
getColumnConstraints().addAll(cc0, cc1, cc2);
getRowConstraints().addAll(rc0, rc1, rc2, rc3);
}
private void initialization() {
inputCellsContainer = new HBox(0);
outputCellsContainer = new HBox(0);
GridPane.setValignment(inputCellsContainer, VPos.BOTTOM);
GridPane.setValignment(outputCellsContainer, VPos.TOP);
inputOutputPreviousButton = new Button("<<");
inputOutputPreviousButton.maxWidth(Double.MAX_VALUE);
GridPane.setHgrow(inputOutputPreviousButton, Priority.NEVER);
GridPane.setVgrow(inputOutputPreviousButton, Priority.NEVER);
GridPane.setMargin(inputOutputPreviousButton, new Insets(5));
inputOutputNextButton = new Button(">>");
inputOutputNextButton.maxWidth(Double.MAX_VALUE);
GridPane.setHgrow(inputOutputNextButton, Priority.NEVER);
GridPane.setVgrow(inputOutputNextButton, Priority.NEVER);
GridPane.setMargin(inputOutputNextButton, new Insets(5));
for (int i = 0; i < 32; i++) {
InputOutputCell cellIn = new InputOutputCell(String.format("%02X", i), Color.AQUA, 0);
InputOutputCell cellOut = new InputOutputCell(String.format("%02X", i), Color.BEIGE, 1);
HBox.setHgrow(cellIn, Priority.ALWAYS);
HBox.setHgrow(cellOut, Priority.ALWAYS);
inputCellsContainer.getChildren().add(cellIn);
outputCellsContainer.getChildren().add(cellOut);
}
GridPane.setHgrow(inputCellsContainer, Priority.ALWAYS);
GridPane.setHgrow(outputCellsContainer, Priority.ALWAYS);
GridPane.setVgrow(inputCellsContainer, Priority.ALWAYS);
GridPane.setVgrow(outputCellsContainer, Priority.ALWAYS);
startLabel = new Label("0");
endLabel = new Label("31");
GridPane.setHalignment(startLabel, HPos.LEFT);
GridPane.setHalignment(endLabel, HPos.RIGHT);
this.add(inputOutputPreviousButton, 0, 0, 1, 2);
this.add(inputCellsContainer, 1, 0);
this.add(outputCellsContainer, 1, 1);
this.add(inputOutputNextButton, 2, 0, 1, 2);
this.add(startLabel, 1, 2);
this.add(endLabel, 1, 2);
}
public int getIndex() {
return index;
}
private class InputOutputCell extends StackPane {
#FXML
Text text;
#FXML
Rectangle rectangle;
public InputOutputCell(String text, Color color, int type) {
setMinSize(0, 0);
this.text = new Text(text);
rectangle = new Rectangle();
if (type == 0) {
rectangle.widthProperty().bind(inputCellsContainer.widthProperty().divide(32));
rectangle.heightProperty().bind(inputCellsContainer.widthProperty().divide(32));
} else {
rectangle.widthProperty().bind(outputCellsContainer.widthProperty().divide(32));
rectangle.heightProperty().bind(outputCellsContainer.widthProperty().divide(32));
}
rectangle.maxWidth(Double.MAX_VALUE);
rectangle.maxHeight(Double.MAX_VALUE);
rectangle.setArcHeight(10);
rectangle.setArcWidth(10);
rectangle.setFill(color);
rectangle.setStroke(Color.BLACK);
getChildren().addAll(rectangle, this.text);
}
public void setText(String text) {
this.text.setText(text);
}
public String getText() {
return this.text.getText();
}
}
}
I want cells 1,0 and 1,1 to be resizable and clearly by increasing their width the height of row 0 and 1 should not be increasing equally. If there should be any height left I want the row 3 to take it because row 2 should not grow to height at all.
You did a fair bit of explanation (which is good) but it is complex enough to make me read 10 times, and I think I understood 70% of it. The most puzzling part is this line:
the rest of the available space should take another row below so the aspect ratio of that one cell is preserved according to the element inside that cell.
Not sure which "cell" you are referrring to, and what you mean by keeping aspect ratio.
Now for the actual answer, I think the most obvious one that I can see is that you have given rows 0 and 1 ALWAYS priority for VGrow.
You have 3 rows that has ALWAYS VGrow, and what the GridPane will do is to give all children to whatever space that they preferred to have, then distribute all the "leftover" spaces to rows with ALWAYS. That is why the 3 gaps in your image has the same height.
I am looking for a way to add plus and minus buttons to table cells in LibGDX.
I'm trying to do something like this image:
I noticed that the uiskin files I'm using contain plus and minus buttons (right next to the checkbox) which will do just fine (uiskin.png).
Any tips on how I can add those to my table and make it so they increase/decrease the integer value in that table cell?
I've added a rudimentary sample code with an indication of what I want to do:
#Override
public void create() {
Stage stage = new Stage();
Skin skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
Table table = new Table(skin);
table.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
for(int i = 0; i < 10; i++){
table.add(Integer.toString(i)); //Somehow add plus/minus buttons left
//and right that increase/decrease the integer value
table.row();
}
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
This is just some example code, but if it works here I will certainly be able to figure it out.
Thanks
First, you need an array to store your number values. It's not good practice to use your View (table cells) to store your Model (number values), and besides, Label cells store a String, not an integer, so it would be inconvenient.
private int[] tableData = new int[10];
Now you need a button that can be associated with a row in your data. Here's one way Create a subclass of ImageButton that can take a row number and mode (subtract or add).
public class IncrementButton extends ImageButton {
public final int rowNumber;
public final boolean decrement;
public IncrementButton (Skin skin, String styleName, int rowNumber, boolean decrement){
super(skin, styleName);
this.rowNumber = rowNumber;
this.decrement = decrement;
}
}
The plus and minus images in the uiskin atlas you're using are called "tree-minus" and "tree-plus". You need an ImageButton style for each of these that uses one of these as the imageUp property. (imageUp is used as a default if you don't define images for other states such as imageDown.) You can add these to uiskin.json:
com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle: {
plus: { down: default-round-down, up: default-round, imageUp: tree-plus },
minus: { down: default-round-down, up: default-round, imageUp: tree-minus }
},
Now you can create a ChangeListener for these buttons that will modify the numbers in the data and update the table accordingly. And then set it all up:
Stage stage = new Stage();
Skin skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
final Table table = new Table(skin);
final Label[] labels = new Label[tableData.length]; //keep references to the labels for updating them.
final ChangeListener incrementListner = new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
IncrementButton incrementButton = (IncrementButton)actor;
int row = incrementButton.rowNumber;
tableData[row] += incrementButton.decrement ? -1 : 1;
labels[row].setText(Integer.toString(tableData[row]));
}
};
table.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
for(int i = 0; i < tableData.length; i++){
IncrementButton decrementButton = new IncrementButton(skin, "minus", i, true);
decrementButton.addListener(incrementListner);
IncrementButton incrementButton = new IncrementButton(skin, "plus", i, false);
incrementButton.addListener(incrementListner);
table.add(decrementButton);
labels[i] = table.add(Integer.toString(i)).getActor();//Add number label and keep reference to it in labels array for the change listener to look up
table.add(incrementButton);
table.row();
}
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
If you want to set min and max values for the data, then you need some arrays for the plus buttons and minus buttons (similar to the labels array) so the change listener can look up and disable/enable the buttons accordingly when a limit is hit.
I have a chessboard with 64 JPanels representing each square on the board. The pieces are represented using JLabels which are placed on the JPanels. I am trying to remove all the JLabels off the board. I am confused why this doesn't work:
private void removePieces()
{
for(int i = 0; i < 64; i ++)
{
Component c = chessBoard.getComponent(i);
if(c instanceof JLabel)
{
Container parent = c.getParent();
parent.remove((JLabel)c);
parent.revalidate();
parent.repaint();
}
}
}
chessboard is the big JPanel with the 64 JPanels inside it. After some debugging it looks like the if loop is never being entered. I don't understand why it wouldn't enter the if loop if one of the components is a JLabel?
Looks like your trying to remove your JPanels from your chessboard if they are JLabels (which obviously makes no sense, and is why the if code is never firing). Instead you want to remove the chessBoard's components' JLabel component. Example below.
private void removePieces() {
for(int i = 0; i < 64; i ++) {
if(chessBoard.getComponent(i) instanceof JPanel) {
JPanel c = (JPanel)chessBoard.getComponent(i);
c.removeAll();
c.revalidate();
c.repaint();
}
}
}
I am using removeAll() because I am presuming your JPanels have no other components in them other than the potential JLabels.
Why remove the labels, rather than simply set the icon to null or text to ""?
E.G. using text for the pieces.
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
import javax.swing.border.LineBorder;
class ChessBoard2 {
static ChessMoveMouseListener cmml = new ChessMoveMouseListener();
/** Unicode strings for chess pieces & empty string for blank squares. */
static String[][] pieces = {
{"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"},
{"\u265A", "\u265B", "\u265C", "\u265D", "\u265E", "\u265F"},
{""}
};
static int[] order = new int[]{2, 4, 3, 0, 1, 3, 4, 2};
static int[] pawns = new int[]{5, 5, 5, 5, 5, 5, 5, 5};
static int[] blank = new int[]{0, 0, 0, 0, 0, 0, 0, 0};
static int white = 0;
static int black = 1;
static int space = 2;
public static JLabel getColoredLabel(String string, int color) {
JLabel l = new JLabel(string);
l.setFont(l.getFont().deriveFont(50f));
Color c = (color % 2 == 0 ? Color.WHITE : Color.LIGHT_GRAY);
l.setBackground(c);
l.setOpaque(true);
l.addMouseListener(cmml);
return l;
}
public static void addRowToContainer(
Container c,
int[] order,
int row,
int count) {
for (int ii : order) {
c.add(getColoredLabel(pieces[row][ii], count++));
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel chessboard = new JPanel(new GridLayout(0, 8, 1, 1));
chessboard.setBackground(Color.BLACK);
chessboard.setBorder(new LineBorder(Color.BLACK));
int count = 0;
// black pieces..
addRowToContainer(chessboard, order, black, count);
addRowToContainer(chessboard, pawns, black, ++count);
// middle squares..
addRowToContainer(chessboard, blank, space, ++count);
addRowToContainer(chessboard, blank, space, ++count);
addRowToContainer(chessboard, blank, space, ++count);
addRowToContainer(chessboard, blank, space, ++count);
// white pieces..
addRowToContainer(chessboard, pawns, white, ++count);
addRowToContainer(chessboard, order, white, ++count);
JOptionPane.showMessageDialog(null, chessboard,
"Click two squares to move from/to",
JOptionPane.INFORMATION_MESSAGE);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
class ChessMoveMouseListener extends MouseAdapter {
String s = null;
#Override
public void mouseClicked(MouseEvent e) {
JLabel l = (JLabel) e.getSource();
if (s == null) {
if (l.getText().trim().length() > 0) {
s = l.getText();
l.setText("");
}
} else {
l.setText(s);
s = null;
}
}
}
Think, when you're doing:
Component c = chessBoard.getComponent(i);
you're getting one of the JPanels, that contains your JLabels. And of course they are not instances of JLabel.
So you need to get JLabel from that JPanel and then remove it.