The title is a bit ambiguous and I will explain in codes. Suppose I have
Class A extends JFrame implements ActionListener{
B b;
Class B extends JPanel{
public JButton button;
public B(A a){
button = new JButton();
button.addActionListener(a);// I want to process all actionEvents in A
this.add(button);
}
}
public A(){
b = new B(this);
//irrelevant codes omitted for brevity
}
public void actionPerformed(ActionEvent e){
//Here's the question:
//Suppose I have a lot of Bs in A,
//how can I determine which B the button
//that triggers this callback belongs to?
}
}
So is there any way to to that? Or my idea is wrong? Any thought is welcomed.
EDIT:
What I finally do is to add a function has(JComponent component) to B to compare against every clickable B has. The getParent() becomes awkward when you have multiple layers of JPanel as it's hard to figure out which layer of panel it's referring to and it's against the idea of encapsulation.
Use e.getSource() to get a reference to the exact component that triggered the event. In your case, it will be a JButton. To get the panel it sits on, use e.getSource().getParent().
Say you have B[] bs = new B[n];
Then you could set action command for each button, such as:
for (B b : bs) {
b.setActionCommand("some identifiable command"); // use different command for different buttons
}
Then in the actionPerformed method, switch on the commands:
public void actionPerformed (ActionEvent e) {
switch (e.getActionCommand()) {
case "cmd1":
// do something
break;
case "cmd2":
// do something
break;
default:
}
}
You can also use Action objects, which is more flexible but a little more complicated.
For more information, please read Java tutorial:
How to Use Buttons, Check Boxes, and Radio Buttons
How to Use Actions
Related
I have an A class and a B class
In the A class I have multiple B instances. From A class, after defining B, I write a mouseListener event for the B class, and it works. Now I realiced that I have to write the same mouseListener for every B class that I instantiate, and all they do is the same: Opening a DialogBox asking for a number, so I decided to write that mouseListener in the B class constructor.
The problem comes when that mouseListener has to access to an A's private attribute. In this point, I thought about using a functional interface / callback to send values from B to A when the mouseEvent is fired, but I feel like the code will be a little bit messy, like I'm using the wrong tools to reach the functionality I want, maybe I'm wrong...
Any advice or recomendation ? I'll post a little bit of the code so you can understand it better. This is my A class: This code works but now think that I have to write the same code lines again and again
PowerConfigPanel is the A class.
PTextField is the B class.
connectionListener is the A private attribute I was talking about.
public class PowerConfigPanel extends JPanel {
private ConnectionListener connectionListener;
public PowerConfigPanel(){
PTextField SUSPEND_CHARGER_BF= new PTextField(7);
SUSPEND_CHARGER_BF.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
JPanel insertValuePanel = new JPanel();
insertValuePanel.setLayout(new GridLayout(3,2,1,1));
PTextField field = new PTextField(7);
insertValuePanel.add(UtilsService.createComponentPanel(field,"Insert Value "));
int result = JOptionPane.showConfirmDialog(null, insertValuePanel,
"Please Enter a new Value", JOptionPane.OK_CANCEL_OPTION);
if(result == JOptionPane.OK_OPTION){
JOptionPane.showMessageDialog(null, "Invalid input for seconds", "Error", JOptionPane.ERROR_MESSAGE);
connectionListener.connect("a","b",Integer.valueOf(field.getText()));
}
}
});
}
}
What I mean is: mouseClick returns void... is there any possibility to make it return an integer and collect that integer in the A class when the B class is clicked?
I feel that I'm missing something when it comes to statically typed languages. When I pretty much only used perl way back, there were many ways I could tell an object which function to call.
Now that I'm in Java, I fail to see how I can do something similar in an easy fasion
I have a generic Button class. This is subclassed by all of the actual buttons that will be used: Each with a different method to call when clicked.
Is there really no way of passing a reference to a method to call when clicked, so that I can use one class for all of the buttons?
At present, I create buttons like this:
// Specifically using the subclass that sets "firemode" to "close"
FiremodeClose fc = new FiremodeClose(Settings.ui_panel_start, Settings.ui_panel_row_firemode, game);
painter.addSelectionButton(fc);
clickTracker.addSelectionButton(fc);
This ofcourse couses a myriad of subclasses, each one differing only in placement, label/graphics, and method call. It makes more sense to do something similar to this:
// Generic button, the method that sets "firemode" is somehow passed as arguement to the contsructor.
Button fc = new Button(&referenceToFunctionToCallWhenClicked, otherArguementsEtc);
painter.addSelectionButton(fc);
clickTracker.addSelectionButton(fc);
Like I said, I feel I must be missing something, because it makes sense that there should be a way of achieving this, thus letting me getting away with just one Button class without any subclasses.
If that's what interfaces are for, then I must've been using them for something else than their intended purpose. I'd love to see an answer involving some code examples for this.
Have your Buttons implement the observer pattern, just like Swing does. Then you can even just use Swing's ActionListener interface, or even Runnable is not a bad choice, or e.g. roll your own:
// Your interface.
public interface MyButtonListener {
public void buttonClicked ();
}
// Somewhere else:
Button fc = ...;
fc.addButtonListener(new MyButtonListener () {
#Override public void buttonClicked () {
// do stuff here
}
});
// And in your Button have it simply iterate through all of its registered
// MyButtonListeners and call their buttonClicked() methods.
There are myriads of other ways to implement this. For example, you could even do something like:
public interface ThingThatCaresAboutButtons {
public void buttonClicked (Button button);
}
Then have your higher level UI logic be something like:
public class MyUI implements ThingThatCaresAboutButtons {
#Override public void buttonClicked (Button button) {
if (button == theOneButton) {
// do whatever
} else if (button == theOtherButton) {
// do whatever
}
}
}
And when creating buttons:
theOneButton = new Button(theUI, ...);
theOtherButton = new Button(theUI, ...);
Or have them maintain a list instead of a single object passed in the constructor. Or whatever.
Endless ways to skin this cat but hopefully you get some inspiration here. Check out how Swing works.
You could for instance use Runnable:
class MyButton {
private final Runnable action;
public MyButton(Runnable action) {
this.action = action;
}
...
}
And then call action.run() when the button is clicked.
Then when creating a button, you can pass a reference to a method, as long as it has the void return type, and takes no arguments.
Button fc = new Button(EnclosingClass::methodToCall, otherArguementsEtc);
Other interfaces can be used for different method signatures.
In Java 8 you can use both method references and lambdas:
class Button {
Button(Runnable function) {
}
}
Button b1 = new Button(() -> System.out.println("works!"));
Button b2 = new Button(System::gc);
You can do similar thing in Java <8, but it's more verbose with anonymous classes:
Button b3 = new Button(new Runnable() {
#Override
public void run() {
System.out.println("works!");
}
});
I was asking about the right way to make a component that holds some state. Like a Jbutton that saves a color in it, or a list item that saves a certain object. So when those GUI components fire an event I can use the saved states to do something with it.
My way was like that:
1- Make a subclass of the required component, like a subclass from Jbutton.
2- Make a Listener for this new subclass : in the listener check if the event source is the subclass, convert it then use the stored data.
Example:
class ColorButton extends JButton
{
static class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent actionEvent) {
Object source = actionEvent.getSource();
if( source.getClass() == ColorButton.class)
{
ColorButton t = (ColorButton) source;
t.getComponent().setBackground(t.getColor());
}
}
}
//states i want to be saved
private Color c;
private Component comp;
ColorButton(Component comp, Color c) {
setColorChanger(comp, c);
}
/* ......
......
rest of constructors added with those additions
......
*/
private void setColorChanger(Component comp, Color c)
{
this.comp = comp;
this.c = c;
}
Color getColor() {
return c;
}
Component getComponent() {
return comp;
}
}
And I use it this way:
JPanel panel = new JPanel();
ColorButton.Listener l = new ColorButton.Listener();
JButton b = new ColorButton("Blue", panel, Color.BLUE);
JButton r = new ColorButton("Red", panel, Color.RED);
r.addActionListener(l);
b.addActionListener(l);
panel.add(b);
panel.add(r);
add(panel);
So I was wondering if this way is okay or what, I feel it is very boring to make this for every component that should hold a certain states, is there a better way?
Yes, there is a better way. Every single component object should have its own separate ActionListener, so that you don't have to check if( source.getClass() == ColorButton.class), and you can directly access the fields of the component by name, without having to go through the source at all. For that to work, you have to use a non-static inner class, or an anonymous inner class. That if statement is a very old-fashioned and non-OOP way of doing things.
In fact, the component object itself can be its own ActionListener - but that style only allows you to have one ActionListener, and is a bit less well-organised.
The better way is dependent on what kind of state you want to hold and what use you want to make of it. Without thinking that through so that you can state it, it isn't possible to make an overall plan for a better way to do it. Is setting color the only thing you want to do? Do you need to mix regular JButtons with ColorButtons in your application?
This is a lab for school that I'm struggling with, the code is making a game of hangman, and when the "brain" program says the game is over, all of the letter buttons are supposed to be disabled.
relevant code sections:
the buttons:
class ActionButton extends JButton implements ActionListener{
private String name;
private char t;
public ActionButton(String s){
super(s);
name = s;
t = name.charAt(0);
}
#Override
public void actionPerformed(ActionEvent e) {
ido.newLetter(t);
this.setEnabled(false);
LovesMePanel.this.update();
}
}
the update method:
public void update(){
answers = ido.getAnswer();
flower.setTriesLeft(ido.getTriesLeft());
progress.setText(answers);
if(ido.gameOver()){
// This is where I need to deactivate the buttons
if(ido.hasWon()){
}
}
else if(triesLeft == 0){
}
}
the buttons are all created in a loop in the LoveMePanel that holds all of the other panels. Is there a way to reference them all or disable them all when the game is over?
If not, how should I change my code so that it would be possible to do that?
If you put your buttons in a Collection, you can iterate through them and disable them all that way. I.e.,
for (JButton b : myButtons) {
b.setEnabled(false)
}
If not, you have 26 disable statements to write.
See the setEnabled() method for JButton. You can:
Add your Buttons to an ArrayList while creating them and then iterate over it and disable one by one
Get children of a JPanel, iterate over them, check if it's a button and disable it
Put a Glass Pane on top of your Burrons to intercept the incoming events
Feel free to choose the one you like best.
how about getting the children of the root panel by calling getComponents and iterating over them recursively and finding JButtons and disabling them as you find them?
I can't get my head round this one. I've tried to adhere to the MVC pattern for the first time and now have difficulties accessing the source of an ActionEvent because the ActionListener is located in a different class. But let the code do the talking...
In the "view":
// ControlForms.java
...
private JPanel createSearchPanel() throws SQLException {
...
comboBoxCode = new JComboBox(); // Field comboBoxCode -> JComboBox()
SwingUtilities.invokeLater(new Runnable() {
public void run() {
AutoCompleteSupport<Object> support = AutoCompleteSupport.install(
comboBoxCode, GlazedLists.eventListOf(jnlCodeArray));
}
}); // Auto-Complete comboBox from GlazedLists
...
public void setComboListener(ComboListener comboListener) {
comboBoxCode.addActionListener(comboListener);
}
...
}
Then, in what I term the controller, I have two different classes:
// Controller.java
public MyController() throws SQLException {
...
addListeners();
}
...
private void addListeners(){
View view = getView();
getView().getControlForm().setComboListener(new ComboListener());
}
and
public class ComboListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("ComboBox listened to! e = " + e.toString());
}
}
Now, e obviously doesn't give the name of the variable (which at the moment I wish it would), so I cannot if test for e.getSource().
My question is thus: is there either a) a way to query (via if for example) the source of e, or b) a less complicated way to get to the variable name?
Many, many thanks in advance for your insights and tips!
Why do you need the name of the variable? Why can't you do the event handling like this
public class ComboListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
JComboBox source = (JComboBox)e.getSource();
//do processing here
}
}
I'd think that if you need to do processing according the variable name, obviously you need different listeners for different combo boxes.
Generally, there are only two situations in which you should use a listener like that: a) you're going to handle a certain event the same way for a bunch of objects, or b) you're only going to use the listener for one object. In the latter case, I'd prefer handling the event locally anyway.
That said, the direct answer to your question is: you shouldn't have to check inside your ActionListener implementation to see whether the appropriate object is the source of the event; you should simply only add the ActionListener to that one object.
One final note: without knowing the specifics of your architecture... generally, MVC will treat all event handling as part of the View (it reduces coupling) and the View will pass commands or method calls or your own events (i.e., not Swing's) to the Controller.