I am trying to do a simple Minesweeper game using JFrame, however I am having troubles with the creation of objects. I am creating 96 buttons, some of which get the property of being wrong ("F") and right ("R"):
public class GUIBase extends JFrame {
private JButton button;
private JButton fButton;
public GUIBase() {
super("Minesweeper");
setLayout(new FlowLayout());
//Fields
int position;
for (int i = 0; i < 96; i++) {
position = (int) (Math.random() * 100);
if (position < 80) {
button = new JButton("R");
button.setToolTipText("Is this the correct one?");
add(button);
} else {
fButton = new JButton("F");
fButton.setToolTipText("Is this the correct one?");
add(fButton);
}
}
I then use ActionListener in order to check whether or not the button is correct. If the button is correct, it will get .setEnabled(false), otherwise the game ends:
//Action
Action action = new Action();
button.addActionListener(action);
fButton.addActionListener(action);
}
private class Action implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("Somethin");
if (event.getSource() == button) {
button.setEnabled(false);
} else if (event.getSource() == fButton) {
JOptionPane.showMessageDialog(null, "You lost!");
System.exit(0);
} else {
JOptionPane.showMessageDialog(null, "An error ocurred");
System.exit(0);
}
}
}
Everything in the game turns out as planned, however only the last correct button ("R") and last wrong one ("F") are connected to the ActionListener. The rest of the buttons do not do anything when pressed.
How can I fix this?
The problem is that you only have two variables (attributes of the class GUIBase, specifically), and your are assigning to it each time you create a new button. Hence, you only have a reference to the last buttons.
You need an array of buttons. Let's see:
public class GUIBase extends JFrame {
public final int MAX_BUTTONS = 96;
private JButton[] buttons;
// ...
}
The next step is to create the array itself at the beginning:
public GUIBase() {
super("Minesweeper");
setLayout(new FlowLayout());
this.buttons = new JButton[MAX_BUTTONS];
//Fields
int position;
for (int i = 0; i < buttons.length; i++) {
position = (int) (Math.random() * 100);
this.buttons[ i ] = new JButton("R");
this.buttons[ i ].setToolTipText("Is this the correct one?");
this.add(this.buttons[ i ]);
Action action = new Action();
this.buttons[ i ].addActionListener(action);
}
}
You'll probably need more depth in arrays in order to completely understand the code. Basically, an array is a continuous collection of variables, which you can index by its position, from 0 to n-1, being n the number of positions.
Then you'll probably be able to fill the gaps yourself.
Hope this helps.
One part of your problems is coming from your action listener.
Of course, one part is that your code probably needs a list/array to keep track of all created buttons; but at least right now, you can rework your code without using arrays/list:
private class Action implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("Somethin");
if (event.getSource() instanceofJButton) {
JBUtton clickedButton = (JButton) event.getSource();
String buttonText = clickedButton.getText();
if (buttonText.equals("R") ...
else if (buttonText.equals("F")
You see, the whole point here is: as of now, you just need to know what kind of button was created. And your ActionListener knows which button it was clicked on ...
Related
I'm creating a TicTacToe program for a Java project. I'm using swing with a 3x3 panel as the GUI and integrating buttons into each box for the user to click. The problem is, I do not know how to run the WinCondition method (meaning when a user gets 3 in a row). I am not able to call it to the actionPerformed method in the button class, and I don't know where else it would be viable to call.
I have two classes, one is a button class and another is the actual game which creates the panel for the user.
I've tried thinking about where else I could implement it, but I cannot because I do not know another way to execute code when a button is pressed other than the actionPerformed method.
public class TicTacGame extends JFrame{
/**
*
*/
private static final long serialVersionUID = 1L;
JPanel p = new JPanel();
XOButton[] buttons = new XOButton[9];
public TicTacGame() {
super("TicTacToe");
setSize(400,400);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
p.setLayout(new GridLayout(3,3));
for(int i = 0; i < 9; i++) {
buttons[i] = new XOButton();
p.add(buttons[i]);
}
add(p);
setVisible(true);
}
public int winCondition() {
//I have left out the win condition method so this box doesn't get unnecessarily long
}
public static void main(String[] args) {
TicTacGame ttg = new TicTacGame();
}
}
public class XOButton extends JButton implements ActionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
ImageIcon X;
ImageIcon O;
//0 is nothing, 1 is X, 2 is O
int value = 0;
public static int turn = 1;
public XOButton() {
X = new ImageIcon(this.getClass().getResource("X.png"));
O = new ImageIcon(this.getClass().getResource("O.png"));
addActionListener(this);
}
public int getValue() {
return this.value;
}
public void actionPerformed(ActionEvent e) {
if(turn >= 5) {
int win = ttg.winCondition();
}
if(turn % 2 == 0) {
value += 2;
turn++;
}
else {
value++;
turn++;
}
value %= 3;
switch(value) {
case 0:
setIcon(null);
break;
case 1:
setIcon(X);
removeActionListener(this);
break;
case 2:
setIcon(O);
removeActionListener(this);
break;
}
}
}
What I expect is that whenever a button is clicked, the winCondition() method is executed to check if a user won or not.
there's a plenty ways to do that one of them
DON'T
create the win method static, so that you can access from any other class. for more info read comments below
1)you can pass a reference from TicTacGame class into XOButton class in the constructor.. so that you've the access to any other function in TicTacGame class
2)you can use interfaces....etc
I've implemented code that will - amongst other things - make a series of JButton's disabled after being clicked. The code for this is below:
ActionListener disableButton = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (!(event.getSource() instanceof JButton)) {
return;
}
theModel.currentWord.append(event.getActionCommand());
wordDisplay.setText(theModel.getCurrentWord());
((JButton) event.getSource()).setEnabled(false);
}
};
theModel.randomLetters();
for (int i = 0; i < 16; i++) {
dice = new JButton(theModel.letters.get(i));
dice.addActionListener(disableButton);
boggleGrid.add(dice);
}
Notice the "((JButton)event.getSource()).setEnabled(false);" line. This, after completing the previous lines, makes any clicks on the button inactionable. I wish to reverse this when a seperate button is clicked. It's code is below:
JButton submitWordButton = new JButton("Submit Word");
submitWordButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent submit) {
wordDisplay.setText("");
theModel.currentWord.delete(0, 16);
((JButton) submit.getSource()).setEnabled(true);
}
});
info.add(submitWordButton, BorderLayout.SOUTH);
My dilemma is I don't know how to reference the JButton's outside of the ActionListener that disables them once clicked and hence enabled them again. The button I want to use to do this (the one with code most recently pasted above) is in another class. Any ideas?
I have 20 loop-generated JToggleButtons and I need to count how many of them are active.
private void generarBotones(){
JToggleButton b;
this.panelCuerpo.setLayout(new GridLayout(4,5));
for(int i = 1; i<=20; i++){
b = new JToggleButton();
b.setText(String.valueOf(i));
this.panelCuerpo.add(b);
b.addActionListener(new ActionListener() {
int clicks = 0;
#Override
public void actionPerformed(ActionEvent ae2){
clicks = clicks + 1;
System.out.println(clicks);
}
public void setCantidadBoletas(int clicks){
cantidadBoletas = clicks;
}
});
}
}
The problem here is that it counts how many times is EACH ONE of them clicked instead of count how many of them are selected.
PS. I tried to use (b.isSelected()) but b needs to be final to access it so it wasn't the solution.
If you declare the JToggleButton inside the loop, you can make it final:
for (int i = 1; i<=20; i++) {
JToggleButton b = new JToggleButton();
Then you can use b.isSelected:
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (b.isSelected())
clicks++;
else
clicks--;
}
});
}
clicks would have to be a class variable.
Suggestions:
Create a field, JToggleButton[] toggleButtons = new JToggleButton[20]
Or use an ArrayList if you so choose
In your for loop create your JToggleButton and assign it to the proper array item.
In the ActionListener simply iterate through the array, counting how many of its JToggleButton items are selected.
You're done.
Create a class attribute that will count the selected toggles:
private int selectedCount;
Initialize the counter to 0 in your constructor:
this.selectedCount = 0;
Increment or decrement the counter every time the state of a toggle changes:
b.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ev) {
if (ev.getStateChange() == ItemEvent.SELECTED){
YourClass.this.selectedCount++;
} else if (ev.getStateChange() == ItemEvent.DESELECTED){
YourClass.this.selectedCount--;
}
System.out.println(YourClass.this.selectedCount);
}
});
There are many ways to get this done and the best way depends on the rest of your code. I tried to keep it as close to yours.
You can just declare the buttons as final inside the loop and keep a global count of the number of buttons selected, which will be modified in the ActionListener:
public class ButtonsCount extends JFrame {
int clicks = 0;
ButtonsCount() {
JLabel label = new JLabel("0");
JPanel buttonsPanel = new JPanel(new GridLayout(4,5));
for(int i = 1; i <= 20; i++) {
final JToggleButton b = new JToggleButton();
b.setText(String.valueOf(i));
buttonsPanel.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae2){
if (b.isSelected())
label.setText(String.valueOf(++clicks));
else
label.setText(String.valueOf(--clicks));
}
});
}
add(buttonsPanel);
add(label, BorderLayout.PAGE_END);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new ButtonsCount();
}
}
I have a class called Screen containing the actionPerformed method.
I want a different outcome for the different menu items: random, aggressive and human.
This outcome effects the main method, however I am unsure how to link the two...
public class Screen extends JFrame
implements ActionListener {
ActionListener listener;
JMenuItem random = new JMenuItem("Random");
JMenuItem aggressive = new JMenuItem("Aggressive");
JMenuItem human = new JMenuItem("Human");
public Screen(Board board){
//menuBar items
menu.add(random);
random.addActionListener(this);
menu.add(aggressive);
aggressive.addActionListener(this);
menu.add(human);
human.addActionListener(this);
....
//sets up board of buttons and adds actionListener to each.
....
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == random){
}
if(e.getSource() == aggressive){
}
if(e.getSource() == human){
}
//code for the board buttons - nothing to do with the menu.
//But thought it might help
if (numClicks == 0){
JButton piece = (JButton) e.getSource();
String xy = piece.getName();
String x = xy.substring(0,1);
String y = xy.substring(2,3);
FromXInt = Integer.parseInt(x);
FromYInt = Integer.parseInt(y);
System.out.println("From" + " " +FromXInt + "," + FromYInt);
}
else{
JButton piece = (JButton) e.getSource();
String xy = piece.getName();
String x = xy.substring(0,1);
String y = xy.substring(2,3);
ToXInt = Integer.parseInt(x);
ToYInt = Integer.parseInt(y);
System.out.println("To" + " " + ToXInt + "," + ToYInt);
}
numClicks++;
if (numClicks >= 2){
numClicks = 0;
}
return;
}
}
My class which contains the main method:
public class Chess{
public static void main(String [ ] args){
Screen s = new Screen(board);
// my attempt but doesn't work
if (s.actionPerformed(e) == random){
.....
note: I am new to Java and still trying to get my head round the linking of multiple classes.
--------------------The ActionPerformed method also contains events if buttons are clicked but I haven't added that code in because it over complicates things.--
This approach uses a public enum and sets the style variable according to the users menu choice:
package chess;
//...
public class Screen extends JFrame
implements ActionListener {
private JMenuItem random = new JMenuItem("Random");
private JMenuItem aggressive = new JMenuItem("Aggressive");
private JMenuItem human = new JMenuItem("Human");
public enum PlayStyle {Random, Aggressive, Human};
private PlayStyle style;
public Screen(Board board) {
//menuBar items
menu.add(random);
random.addActionListener(this);
menu.add(aggressive);
aggressive.addActionListener(this);
menu.add(human);
human.addActionListener(this);
//....
//sets up board of buttons and adds actionListener to each.
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == random) {
style=PlayStyle.Random;
}
if (e.getSource() == aggressive) {
style=PlayStyle.Aggressive;
}
if (e.getSource() == human) {
style=PlayStyle.Human;
}
//code for the board buttons - nothing to do with the menu.
//....
}
public PlayStyle getStyle(){
return style;
}
}
This is the class that contains the main method:
package chess;
import chess.Screen.PlayStyle;
public class Chess{
public static void main(String [ ] args){
Screen s = new Screen(board);
// this attempt will work
if (s.getStyle()==PlayStyle.Random) {
...
} else if (s.getStyle()==PlayStyle.Aggressive){
...
You are calling a method and it seems that you want to use something that comes back from that method but the method itself returns nothing, i.e. "void". I changed your Screen class so that the method returns something now.
public class Screen extends JFrame
implements ActionListener {
public Source actionPerformed(ActionEvent e) {
....
if(e.getSource() == random){
}
if(e.getSource() == aggressive){
}
if(e.getSource() == human){
}
return e.getSource()
}
The main method will now be able to receive a result from the call and use it.
it is possible to have two class, and in one something like
arrayButtons[i][j].addActionListener(actionListner);
and in another
ActionListener actionListner = new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int j = 0; j < arrayButtons.length; j++) {
for (int i = 0; i < arrayButtons[j].length; i++) {
if (arrayButtons[j][i] == e.getSource()) {
if ((gameNumber == 2) && (playHand.getNumberOfCards() == 0)) {
if (player[j].getCard(i).getSuit() == Suit.HEARTS.toString() && player[j].hasSuitBesideHearts())
//second game
messageOnTable("xxx");
else{
arrayButtons[j][i].setVisible(false);
test[j].setIcon(player[j].getCard(i).getImage());
pnCardNumber[j].setText(Integer.toString(player[j].getCard(i).getNumber()));
pnCardName[j].setText(player[j].getCard(i).toString());
pnCardSuit[j].setText(player[j].getCard(i).getSuit());
playHand.addCard(player[j].getCard(i), j);
player[j].removeCard(i);
}
}
}
//and more
the reason of that is because i need to separate the button (swing) to the action listener
how i can do ?
thanks
Not only it is possible to separate these two, it's also recommended (see MVC pattern - it's very much about separating screen controls like buttons, and the logics of your program)
The easiest way that comes to my mind is to do write a named class that implements ActionListener interface, something like this:
public class SomeActionListener implements ActionListener{
private JTextField textField1;
private JComboBox combo1;
private JTextField textField2;
//...
public SomeActionListener(JTextField textField1, JComboBox combo1,
JTextField textField2){
this.textField1=textField1;
this.combo1=combo1;
this.textField2=textField2;
//...
}
public void actionPerformed(ActionEvent e) {
//cmd
}
}
And then add it to your buttons:
ActionListener actionListener = new SomeActionListener(textField1, combo1, textField2);
someButton.addActionListener(actionListener);
To answer: "my problem is that action listener have many variables of swing like buttons for example,so, when i change to another class, i have problems with that"
Your action listener class could have a constructor that takes a parameter of the type of the view class:
public class Listener implements ActionListener {
private final MyViewClass mView;
public Listener(MyViewClass pView) {
mView = pView;
}
public void actionPerformed(ActionEvent e) {
// can use mView to get access to your components.
mView.get...().doStuff...
}
}
Then in your view:
Listener l = new Listener(this);
button.addActionListener(l);
you can do it easily by using nested classes,
but i think the best way is pass the parent object as a parameter to the construct of object and using it as an action handler;
//**parent class - Calculator **//
public class Calculator extends JFrame implements ActionListener{
private DPanel dPanel;
private JTextField resultText;
public Calculator(){
// set calc layout
this.setLayout(new BorderLayout(1,1));
dPanel = new DPanel(this); // here is the trick ;)
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
resultText.setText(command);
// **** your code ****/
}
}
//**inner class - DPanel**//
public class DPanel extends JPanel{
private JButton digitsButton[];
private JButton dotButton,eqButton;
public DPanel(Calculator parent){
//layout
this.setLayout(new GridLayout(4,3,1,1));
// digits buttons
digitsButton = new JButton[10];
for (int i=9;i>=0;i--){
digitsButton[i] = new JButton(i+"");
digitsButton[i].addActionListener(parent); // using parent as action handler ;)
this.add(digitsButton[i]);
}
}
}
It's a bit off topic but you should definately not use the == operator to compare Strings as you appear to be doing on this line:
if (player[j].getCard(i).getSuit() == Suit.HEARTS.toString()
This is because Strings are pointers, not actual values, and you may get unexpected behaviour using the == operator. Use the someString.equals(otherString) method instead. And also
"String to compare".equals(stringVariable)
is alot better than the other way around
stringVariable.equals("String to compare to")
because in the first example you avoid getting a NullPointerException if stringVariable is null. It just returns false.
Yes, it can be done. It's very simple; in one class you have your buttons, in the other class you just need to implement an ActionListener and just make your //cmd
to separate that button's function. To do this, you need to use e.getActionCommand().equals(buttonActionCommand).
Sample code:
public class Click implements ActionListener{
public Click(
//input params if needed
){
}
public void actionPerformed(ActionEvent e) {
if( e.getActionCommand().equals(buttonActionCommand){
//cmd
}
}
}
To add that listener on your button just do:
buttonTest.addActionListener(new Click());