I cannot pass the parameters of the ActionListener interface, method actionPerformed().
I have been trying to use the Runnable interface to overcome this problem, however it's method run() does not have the e.getSource() function which is key in my program!
My program creates nine buttons simultaneously in one class, than using dependency injection and getters the second class gets the information and to uses it to make the buttons functional.
The problem comes from the fact that if I want to activate this functionality I need to use the method actionPerformed(). Which parameters actionPerformed( ActionEvent e ).
I need to pass it into the constructor and than pass it's parameter which is at least for me impossible.
The next thing that I that I tried using was the Runnable interface, however it's method run() does not have the e.Source() function, which I have tried to recreate without any luck.
Class where buttons are made:
public class MakeButtons {
private Parameters p;
...
//constructor
public MakeButtons(Parameters p){
this.p = p;
...
makeNineButtons();
}
//getter
public JButton[] getButtons() {
return buttons;
}
//making the nine buttons buttons.length = 9
private void makeNineButtons() {
for (int i = 0; i < buttons.length; i++) {
panel.getGamePanel().add(buttons[i] = new JButton());
buttons[i].setFont(font);
}
}
}
Class where buttons get functions:
public class ButtonsFunctionality implements ActionListener /* OR Runnable */{
private final MakeButtons makeButtons;
...
//constructor
public ButtonsFunctionality(MakeButtons makeButtons){
this.makeButtons = makeButtons;
...
/*
actionPerformed(???);
or
run()
*/
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("yes");
for (JButton n : makeButtons.getButtons()) {
if (e.getSource() == n) {
//Code
}
}
}
// OR
#Override
public void run() {
System.out.println("yes");
for (JButton n : makeButtons.getButtons()) {
if ( ??? == n) {
//Code
}
}
}
}
Related
I've been trying to make a listener (I'm not really sure whether I should be using an ItemListener or ActionListener) respond to changes in a JComboBox by changing a JLabel image next to the box.
I tried defining the actionPerformed method in the constructor of the class under the addActionListener call on the combo box, as well as outside the constructor, and the actionPerformed never seems to execute. I've added a println to each one to test whether the method is actually working when I select an item in the box, but neither one appears to output anything, leading me to believe the actionPerformed method is not executing for some reason. A lot of different answers elsewhere have defined actionListeners and actionPerformed in multiple different places, such as a separate class or in an instance variable declaration.
public class MainBattle
{
//instance variables
public MainBattle() throws FileNotFoundException,IOException
{
//creation of ArrayLists used later
for(JComboBox<String> j : party1)
{
j.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Listener active");
if(e.getSource() instanceof JComboBox)
{
JComboBox<String> cb = (JComboBox<String>)e.getSource();
String content = (String)cb.getSelectedItem();
if(party1.indexOf(cb) != -1)
{
party1Image.get(party1.indexOf(cb)).setIcon(new ImageIcon(".\\res\\sprites_small\\"
+ content.substring(0,content.indexOf(" ")) + ".png"));
}
}
selectFrame.revalidate();
selectFrame.repaint();
}
});
}
createUI();
}
public void createUI()
{
//building GUI elements and displaying
for(int i = 0; i < 6; i++)
{
party1.add(new JComboBox<String>());
party2.add(new JComboBox<String>());
}
for(int i = 0; i < 6; i++)
{
party1Image.add(new JLabel(new ImageIcon(".\\res\\sprites_small\\0.png")));
party2Image.add(new JLabel(new ImageIcon(".\\res\\sprites_small\\0.png")));
}
//building GUI elements and displaying
}
// Commented out to make sure existence of multiple methods is not problematic
/*
public void actionPerformed(ActionEvent e)
{
System.out.println("Action");
}
*/
public static void main(String[] args) throws IOException
{
new MainBattle();
}
}
I am trying to make a chess program where I have an 8x8 array of JPanels which all require an addMouseListener but in this addMouseListener I need to make use of the index of that array for it to work, like this:
panels[0][0].addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
panels[0][0].setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
}
public void mouseReleased(MouseEvent e) {
}
});
Since I have 64 JPanels that means I need to copy this 63 times and possible changes need to be copied as well. Is there any better, more efficient way to achieve this?
Since I have 64 JPanels that means I need to copy this 63 times
You can write a generic listener
MouseListener ml = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
JPanel panel = (JPanel)e.getSource();
panel.setBorder(...);
}
};
Then in your looping code you just do:
panels[?][?].addMouseListener( ml );
You should always attempt to write generic listeners so the code can be reused.
You should use a loop for this:
for (int r = 0; r < panels.length; ++r) {
for (int c = 0; c < panels[r].length; ++c) {
// Do this to fix the "must be final" error:
final int row = r;
final int col = c;
panels[row][col].addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
panels[row][col].setBorder(.....);
}
// ..... more
});
}
}
However, there are a few other additional ways to go about this. One is that you could write a class which saves the location of the panel:
class MyMouseListener extends MouseAdapter {
int panelRow;
int panelCol;
MyMouseListener(int panelRow, int panelCol) {
this.panelRow = panelRow;
this.panelCol = panelCol;
}
//.....
}
That is basically what the example using an anonymous class does behind the scenes. You could also save a reference to the panel itself.
Or you can use the getSource() method on the MouseEvent:
#Override
public void mousePressed(MouseEvent e) {
JPanel panelWhichWasClicked = (JPanel) e.getSource();
// .....
}
In that case, you only need 1 mouse listener which you can add to every panel.
When you have an array you must take different approach.
First your class should implement MouseListener which has 5 abstract methods but you are probably interested in mouseClicked:
public class Example implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
JPanel panel = (JPanel) e.getSource(); // finding which panel is clicked on
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
}
Then somewhere inside your class you will do:
for(int i = 0; i < panels.length; i++){
for(int j = 0; j < panels[0].length; j++){
panels[0][0].addMouseListener(this);
}
}
I have two classes in same package. i have declared a static variable in one class and want to access that variable in another class.
Here is my code in which i have declared the static variable
public class wampusGUI extends javax.swing.JFrame {
static String userCommand;
public wampusGUI() {
initComponents();
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
userCommand = commandText.getText();
}
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
wampusGUI w = new wampusGUI();
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
Here is the code in which i want to access variable
public class Game {
private wampusGUI gui;
public Game(wampusGUI w) {
world = new World();
world.start();
gui = w;
}
public void play() {
gui.setTextArea(welcome());
gui.setTextArea(describe());
for (;;) {
String s = userCommand; // here value should come should
System.out.println(userCommand);
Command c = Command.create(s);
String r = c.perform(world);
// is game over?
if (r == null) {
break;
}
System.out.println(r);
}
System.out.println("Game over");
}
}
However, i can pass the variable from first class as a argument. but the problem is that, when i will run program the value is going null first time, which i dont want. i want when i enter value in textfield then it should go to another class.
Thank you.
Looking at your code, it seems you want to show dialogs to your user with a certain text
gui.setTextArea(welcome());
gui.setTextArea(describe());
and sometimes, that dialog should capture user input which is handled afterwards.
Those setTextArea calls are not what you want to use. The user will never see the welcome message as it will immediately be replaced by the describe message.
Make sure you do not block the Event Dispatch Thread (EDT) or nothing will be shown at all. I do not know what your Command class will do, but I see an infinite loop on the Event Dispatch Thread which is never a good thing. Take a look at the Concurrency in Swing tutorial for more information
Thanks to that for loop, the user will simply not be capable to input any command as the EDT is busy handling your loop. What you need is a blocking call allowing the user to provide input (not blocking the EDT, but just blocking the execution of your code). The static methods in the JOptionPane class are perfectly suited for this (e.g. the JOptionPane#showInputDialog). These methods also have a mechanism to pass the user input back to the calling code without any static variables, which solves your problem.
I suggest that you use a listener of one sort or another to allow the Game object to listen for and respond to changes in the state of the GUI object. There are several ways to do this, but one of the most elegant and useful I've found is to use Swing's own innate PropertyChangeSupport to allow you to use PropertyChangeListeners. All Swing components will allow you to add a PropertyChangeListener to it. And so I suggest that you do this, that you have Game add one to your WampusGUI class (which should be capitalized) object like so:
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
// ....
}
This will allow Game to listen for changes in the gui's state.
You'll then want to make the gui's userCommand String a "bound property" which means giving it a setter method that will fire the property change support notifying all listeners of change. I would do this like so:
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
// ....
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
Then you would only change this String's value via this setter method:
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
The Game's property change listener would then respond like so:
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
One of the beauties of this technique is that the observed class, the GUI, doesn't have to have any knowledge about the observer class (the Game). A small runnable example of this is like so:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
private String userCommand;
private JTextArea displayTextArea = new JTextArea(10, 30);
private JTextField commandText = new JTextField(10);
public WampusGUI() {
initComponents();
}
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
private void initComponents() {
displayTextArea.setEditable(false);
displayTextArea.setFocusable(false);
JButton enterButton = new JButton("Enter Command");
enterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
enterButtonActionPerformed(evt);
}
});
JPanel commandPanel = new JPanel();
commandPanel.add(commandText);
commandPanel.add(Box.createHorizontalStrut(15));
commandPanel.add(enterButton);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(new JScrollPane(displayTextArea));
mainPanel.add(commandPanel, BorderLayout.SOUTH);
add(mainPanel);
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
WampusGUI w = new WampusGUI();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.pack();
w.setLocationRelativeTo(null);
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
class Game {
private WampusGUI gui;
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
}
public void play() {
gui.setTextArea("Welcome!\n");
gui.setTextArea("Please enjoy the game!\n");
}
public void play(String userCommand) {
// here we can do what we want with the String. For instance we can display it in the gui:
gui.setTextArea("User entered: " + userCommand + "\n");
}
}
I agree with Jon Skeet that this is not a good solution...
But in case u want an dirty solution to ur problem then u can try this:
public class wampusGUI extends javax.swing.JFrame
{
private static wampusGUI myInstance;
public wampusGUI( )
{
myInstance = this;
initComponents();
}
public static void getUserCommand()
{
if(myInstance!=null)
{
return myInstance.commandText.getText();
}
else
{
return null;
}
}
......
......
}
in the other class use:
public void play()
{
.....
//String s = userCommand; // here value should come should
String s = wampusGUI.getUserCommand();
.....
}
This kind of code is there in some of our legacy projects... and I hate this.
I got a JLabel Scoresp1 which I want to change using Scoresp1.setText(mijnScore + "");. But the text on the JLabel stays the same.
I got a class Dobbelsteen which looks like this:
public class Dobbelsteen extends Spel {
...
public void aantalOgen(int aantalogen) {
oudepositie = huidigepositie;
nieuwepositie = (huidigepositie + aantalOgen);
if (nieuwepositie == eindronde) {
System.out.println("Speler Piet heeft de ronde gewonnen!");
updateUI();
}
}
}
Which calls updateUI which is in the class Spel
public class Spel {
...
public void updateUI() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ikWin = true;
while(ikWin){
mijnScore = mijnScore+1;
Scoresp1.setText(mijnScore + "");
System.out.println("mijnScore" + mijnScore);
ikWin = false;
positie = 0;
}
}
});
}
...
}
Scoresp1 is declared as public JLabel Scoresp1;. If I use String l = Scoresp1.getText(); I get the right value, but the JLabel doesn't get updated visually.
I've looked at some of you code, and my first concern (other than an over-use of static variables) is that you're using inheritance inappropriately and because of this are calling methods on the wrong reference.
Many classes inherit from Spel but don't appear that they should be doing this. For instance, your Dobbelsteen class inherits from Spel, and yet it also has a separate Spel instance -- why? What Spel object is currently visible at the time this code is run? I doubt it is the one that Dobbelsteen extends. Because of this, I think that you're trying to changing the JLabel that is held by the the Dobbelsteen class, but it is not the "Spel" object that is currently visualized. To properly change the visualized JLabel, you'll need a valid reference to the currently visualized Spel object that holds it, and call the appropriate public method on that class.
In all, you might want to re-write this project from the ground up, with a goal of separating out your model (the data) from the view (the GUI), and with an eye towards good OOP principles.
Edit 1:
This may only be a bandaid, but what if you got your Spel reference passed to you in Dobbelsteen's constructor, something like this (changes noted with !! comments: //!!):
//!! public class Dobbelsteen extends Spel {
public class Dobbelsteen { //!!
int dobbelsteen;
int nieuwepositie;
int nieuwepositie2;
public static String newPos;
public static String newPos2;
int oudepositie;
int oudepositie2;
int huidigepositie = Spel.positie;
// int huidigepositie2 = Spel.positie2;
int aantalOgen = Spel.aantalogen;
int aantalOgen2 = Spel.aantalogen2;
static boolean heeftgewonnen = false;
// !! Spel spiel = new Spel();
Spel spiel; // !!
// !!
public Dobbelsteen(Spel spiel) {
this.spiel = spiel;
}
public void aantalOgen(int aantalogen) {
oudepositie = huidigepositie;
nieuwepositie = (huidigepositie + aantalOgen);
if (nieuwepositie == Spel.eindronde) { //!!
System.out.println("Speler Piet heeft de ronde gewonnen!");
spiel.updateUI(); //!! ****** here in particular ******
} else if (nieuwepositie > Spel.eindronde) {
Spel.positie = huidigepositie; //!!
spiel.output.setText("Je hebt teveel gegooid"); //!!
spiel.output.setForeground(Color.red); //!!
} else {
Spel.oudpositie = oudepositie; //!!
Spel.positie = nieuwepositie; //!!
newPos = String.valueOf(nieuwepositie);
if (SpelHost.host) {
SpelHost.verstuurPositie("Positie" + newPos);
} else if (SpelClient.client) {
SpelClient.verstuurPositie("Positie" + newPos);
}
}
}
}
And call it like so:
class GooiDobbelsteen extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
aanBeurt = false;
dobbelsteen = new Random();
aantalogen = dobbelsteen.nextInt(6) + 1;
aantalOog = String.valueOf(aantalogen);
Dobbelsteen dobbel = new Dobbelsteen(Spel.this); // !!
dobbel.aantalOgen(aantalogen);
use
public void updateUI() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ikWin = true;
while(ikWin){
mijnScore = mijnScore+1;
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
Scoresp1.setText(mijnScore + "");
}
});
System.out.println("mijnScore" + mijnScore);
ikWin = false;
positie = 0;
}
}
});
}
to have a test
Add a Scoresp1.repaint() to the end of your while loop.
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());