I hope this question is not really a repetition of an existing one! I searched and found no good answer to my question. Here it is:
I have a class MyGame that contains a Button member object. Whenever that button is clicked, MyGame should do something.
class MyGame extends Application {
MyBoard board = new MyBoard();
MyButton btn = new MyButton();
public MyGame() {
board.add(btn);
}
// this method should be called whenever the button is clicked!
public void doSomething() {
doingSomething();
}
}
class MyButton extends Button {
int someData;
// some code here
public MyButton() {
this.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
MyGame.doSomething(); // ==> NOT POSSIBLE!!!
}
});
}
}
Would an interface be the best way to make the communication between the MyButton and MyGame go? If so, how would you do it?
I don't want to hand over a reference of MyGame object to the MyButton object! I think this is not a good way to resolve this problem.
I appreciate any suggestions and help!
Cheers
In the constructor of MyGame you can add an ActionListener to btn that will be called when an action is performed on the button.
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Insert thing to do within MyGame here
}
});
One way is to do it the other way around (game should be a member of button). I know that you think "it is not a good way to resolve this problem", but you think wrong, there is really nothing wrong with it.
Alternatively, move the registering of the listener out of the button's constructor into the game itself:
public MyGame() {
btn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent arg0) { doSomething(); }
});
}
Make the "button action" an interface that can be passed to the constructor when creating MyButton. Example:
public MyButton(final Runnable action) {
this.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
action.run(); // POSSIBLE!!!
}
});
}
or save the action in a MyButton instance field:
public MyButton(Runnable action) {
buttonAction = action; // make buttonAction an instance field
this.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
buttonAction.run();
}
});
}
Then when MyGame creates a new MyButton, the MyGame gets to tell the button what to do when the button is clicked:
MyButton btn = new MyButton(new Runnable() {
#Override
public void run() {
MyGame.this.doSomething();
}
}
or in Java 8:
MyButton btn = new MyButton(this::doSomething);
NOTE: I haven't yet tested this. I think I got the syntax right, but I may have made a mistake. You may need to assign btn in the MyGame constructor rather than in an initializer expression.
This is a generalized approach (dependency injection?) that works in lots of situations. There may be other solutions specific to Button (or other Swing components for which a listener mechanism is already defined). But this mechanism reduces coupling, because the MyButton doesn't really need to know anything about who created it, and the MyGame doesn't need to know what the MyButton plans to do with the Runnable action. Some other possible solutions involve having MyGame doing some of MyButton's work, which increases coupling.
Related
I am trying to connect a button to say "Hi" when the mouse enters it and "Bye" when the mouse leaves. I have been using mouse events with a MouseListener but to no avail.
I'm new to Java and this question has been plaguing me for the last 2 days and I just have not been able to figure it out. Any help would be greatly appreciated.
private abstract class HandlerClass implements MouseListener {
}
private abstract class Handlerclass implements MouseListener {
#Override
public void mouseEntered(java.awt.event.MouseEvent e) {
mousebutton.setText("Hi");
}
#Override
public void mouseExited(java.awt.event.MouseEvent e) {
mousebutton.setText("Bye");
}
}
Try like this. It is working for me.
public class ChangeTextMouseEvent extends Frame
{
static JButton btn;
public ChangeTextMouseEvent()
{
setTitle("ChangeText");
btn = new JButton("SSS");
add(btn);
setVisible(true);
setBounds(0, 0, 100, 100);
}
public static void main(String[] args)
{
ChangeTextMouseEvent frame = new ChangeTextMouseEvent();
btn.addMouseListener(new MouseAdapter(){
#Override
public void mouseExited(MouseEvent e)
{
btn.setText("Bye");
}
#Override
public void mouseEntered(MouseEvent e)
{
btn.setText("Hi");
}
});
}
}
Updating the UI component alone is often not enough; you also have to trigger a repaint action.
In other words: there are two "layers" here. One is the "data model" (where some button knows about its text); the other is the actual "graphical content". The later one comes into existence by somehow displaying the first parts. Therefore both layers need to be addressed in order to make your chances visible to the user.
See here for some examples around that.
Ok, so I'm doing a Java window (a JFrame, doesn't really matter what it is for) and I have a button on it. What I want to approach is when the button is clicked, it changes its text, then the app does something, and when it's finished the button gets its initial text back.
Something like this...
JButton myButton = new JButton("Initial");
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
((JButton) e.getSource()).setText("New");
//Do other things
//Do more other things
((JButton) e.getSource()).setText("Initial");
}
});
That's what I've tried so far, but I know it doesn't work as expected (all the code executes. I'm not really an expert and I'm doing this things to learn, so I have no clue if there's a way to do it or not.
I've already looked for a solution to this in the web but I've not found anything (maybe I didn't search properly), so I hope there's someone who can help me with this!
PS: Sorry if my English is not perfect, I know about it. Ask me if something isn't clear about the question. Thanks to all!
Kevin.
EDIT: The app is a sudoku solver, so it takes a while //doing other things. Thats why I'm trying to change the solve button text (so it sais it is solving and when it finished it says solved).
Your logic is not wrong! Take a look at my example below:
public class MainFrame extends JFrame {
public MainFrame() {
setMinimumSize(new Dimension(200, 100));
JButton myButton = new JButton("Initial");
add(myButton);
myButton.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
final JButton triggerBtn = (JButton) e.getSource();
final String originalValue = triggerBtn.getText();
triggerBtn.setText("New");
JOptionPane.showMessageDialog(null, "Speak softly and carry a big stick and you will go far.");
triggerBtn.setText(originalValue);
}
});
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MainFrame mainFrame = new MainFrame();
mainFrame.setVisible(true);
}
});
}
}
If you run this you will see that the button is changed. If you were to change the showMessageDialog line to Thread.sleep(10*1000), you would not see a change! This is because you're running the event on the dispatcher thread and the text, even though it is changed, will not allow the change event to be triggered until your method finishes.
Consider the following alternative if the work you're doing is on the same thread:
myButton.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
final JButton triggerBtn = (JButton) e.getSource();
final String originalValue = triggerBtn.getText();
triggerBtn.setText("New");
SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
Thread.sleep(10*1000);
return null;
}
#Override
protected void done() {
triggerBtn.setText(originalValue);
}
};
sw.execute();
}
});
This sets the text, and launches a SwingWorker to run the job asynchronously. Once finished, the dispatcher thread will update the text without requiring the dispatcher thread to be tied up waiting for it to finish (and so events are therefore handled properly).
Let me know if that works for you!
Have you tried with:
JButton myButton = new JButton("Initial");
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
myButton.setText("New");
//Do other things
//Do more other things
myButton.setText("Initial");
}
});
In your example you're missing actionPerformed and you're not accessing button directly (I can't say what's e in your example)
Just save your current text in a local variable and set it back after you've performed your other actions.
You should also make sure it's really the button that you clicked or at least check instanceof JButton before casting.
final JButton myButton = new JButton("Initial");
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == myButton) {
String initialText = myButton.getText();
myButton.setText("New");
// Do other things
// Do more other things
myButton.setText(initialText);
}
}
});
You were also missing out on your actionPerformed method, the code in the question won't compile - I guess you just wrote it in the editor.
I need some help, as I am quite the noob.
The program im trying to make here, used to work for my intentions, but as I tried to make my code more readable, I ran into a problem regarding ActionListener.
Before I made a new class to have all the methods in, I used button.addActionListener(this); and it worked just fine. Now that I wanted to put things in a separate class, I have absolutely no idea what to do.
So I guess my question is, how can I make ActionListener work in a situation like this, or am I just doing everything wrong here?
Here's the part of my code that I think is relevant(edited out most of it):
//Class with frame, panels, labels, buttons, etc.
class FemTreEnPlus {
FemTreEnPlus() {
//Components here!
//Then to the part where I try to add these listeners
cfg.addActionListener();
Exit.addActionListener();
New.addActionListener();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run(){
//Start the Program in the FemTreEnPlus Class
new FemTreEnPlus();
}
});
}
That was the class with the frame, here's the other class, with the methods
public class FemTreEnMethods extends FemTreEnPlus implements ActionListener {
//Perform Actions!
public void actionPerformed(ActionEvent ae){
if(ae.getSource() == cfgButton){
configureSettings();
}
if(ae.getSource() == newButton){
newProject();
}
if(ae.getSource() == exitButton){
exitProgram();
}
}
//All methods are down here
Thanks in advance for any help.
Despite the tutorials' examples show the use of listeners implemented in the way you do, IMHO is more useful use anonymous inner classes to implement listeners. For instance:
cfgButton.addActionListener(new ActionListener() {
#Override
public void actionPerfomed(ActionEvent e) {
// do the stuff related to cfgButton here
}
};
newButton.addActionListener(new ActionListener() {
#Override
public void actionPerfomed(ActionEvent e) {
// do the stuff related to newButton here
}
};
exitButton.addActionListener(new ActionListener() {
#Override
public void actionPerfomed(ActionEvent e) {
// do the stuff related to exitButton here
}
};
This approach has these advantages:
Listeners logic is well separated.
You don't need those nested if blocks asking who is the source of the event.
If you add a new button you don't have to modify your listener. Just add a new one.
Of course it depends on the case. If the behaviour will be the same for a set of components (for instance radio buttons or check boxes) then it makes sense have only one listener and use EventObject.getSource() to work with the event's source. This approach is suggested here and exemplified here. Note the examples also make use of anonymous inner classes in this way:
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// do something here
}
};
Perhaps I am going about this the wrong way. Let me know
Using Swing and AWT, I have several buttons set up on a frame and they each have an ActionListener corresponding to their specific function I.E.
JButton foo_button = new JButton("Foo-Me");
foo_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//Fancy schmancy code work
}
})
JButton bar_button = new JButton("Bar None");
bar_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//Fancy schmancy code work
}
})
So each of these buttons do their own thing. However, what if I want all the buttons to do a certain thing (the same exact method for each), in my case, clear a label, before they do their own thing.
Obviously I could add whatever_label.setText("") to each actionPerformed() but that entails a lot of duplication, something I'm not so much a fan of.
Oh Java and Swing gurus come to my aid.
You can subclass your own implementation of ActionListener:
private static abstract class MyListener implements ActionListener {
#Override
final public void actionPerformed(ActionEvent evt) {
theSameTask();
uniqueTask(evt);
}
private void theSameTask() {
// the identical task
}
public abstract void uniqueTask(ActionEvent evt);
}
And then, the new listeners will look like this:
JButton bar_button = new JButton("Bar None");
bar_button.addActionListener(new MyListener() {
#Override public void uniqueTask(ActionEvent evt) {
//Fancy schmancy code work
}
});
Another possibility is to use the 'Decorater' pattern, and write an ActionListener decorator for the common behavior. Your code would then be of the form
bar_button.addActionListener(new MyActionListenerDecorator( new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//Fancy schmancy code work
} }) );
I think the best way to do this is to use Action. That way all the listeners always do the same thing.
Is it possible to pass a String to an ActionListener? I'm creating a number guessing game, and I need to pass the difficulty selected to the the ActionListener, because it's passed from the first GUI opened. How would I do this as it's passed directly to the Game() constructor?
Have your ActionListener access a variable that contains the result of the guess.
I would do something like:
final JTextArea textArea = new JTextArea();
JButton button =new JButton("press");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
String text = textArea.getText();
if (text.equals("something")) {
doSomething();
} else {
doSomethingElse();
}
}
});
Where does the string come from? You can pass that component to the ActionListener when constructed. For example, your string comes from a JLabel object. Then
class GuessNumberActionListener implements ActionListener {
private JLabel difficulty;
public GuessNumberActionListener(JLabel difficulty) {
this.difficulty = difficulty;
}
// implement other methods
}
Then inside your action listener, you can access/update what you want.
ActionListener is an interface. try extending the functionality of the interface using inheritance. this will do the trick
Sure you can. There are lots of ways but one of them is to pass it in to the the ActionListener's constructor:
public class MyClass implements ActionListener {
private int difficultyLevel;
public MyClass(int difficultyLevel) {
this.difficultyLevel = difficultyLevel;
}
public void actionPerformed(ActionEvent e) {
...//code that reacts to action and does something based on difficultyLevel
}
}
UPDATE:
Looks like the design patterns police are out in full force today. You may want to quickly rewrite your app in MVC before you get shot in the foot :)
http://java.sun.com/products/jfc/tsc/articles/architecture/