Composing Swing Components: How to Add the Ability to Add Actionlisteners

Composing Swing Components: How do I add the ability to add ActionListeners?

I personally don't think you need a custom Swing component. No need for your UI class to extend any Swing class; you aren't likely to provide much custom behavior. (I might concede for a JPanel that composes others.)

I would prefer composition over inheritance. Have a UI class that has Swing data members and give it methods to add and remove Listeners as you need them. You can change the behavior that way without having to rewrite the UI class. It's nothing more than a container.

public class MyUI
{
private Button b = new Button();

public void addButtonListener(ActionListener listener) { this.b.addActionListener(listener); }
}

How to add Swing components in an ActionListener?

When you dynamically add/remove components from a visible GUI then you need to do:

panel.add(...);
panel.revalidate();
panel.repaint();

If you need more help then post your SSCCE that demonstrates the problem.

How to add ActionListener to customize component

If you really can't use a JButton for this, your next best option is extending AbstractButton, which includes the EventListenerList plumbing mentioned here. For example,

image

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.DefaultButtonModel;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.plaf.ButtonUI;

/** @see https://stackoverflow.com/a/14429304/230513 */
public class JCButtonTest {

private void display() {
JFrame f = new JFrame("JCButtonTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JCButton(new AbstractAction("JCButton") {

@Override
public void actionPerformed(ActionEvent e) {
System.out.println(e);
}
}));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}

private static class JCButton extends AbstractButton {

public static final int SIZE = 32;

public JCButton(Action action) {
this.setModel(new DefaultButtonModel());
System.out.println(action.getValue(Action.NAME));
this.init((String) action.getValue(Action.NAME), null);
this.addActionListener(action);
}

@Override
public void updateUI() {
setUI((ButtonUI) UIManager.getUI(this));
}

@Override
public String getUIClassID() {
return "ButtonUI";
}
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
new JCButtonTest().display();
}
});
}
}

How to add an ActionListener while adding an Instantiation of a JComponent to a JContainer?

Three Options:

Option 1: In my opinion the cleanest

    JFrame frame = new JFrame();

JButton button = new JButton("Click Here");
frame.add(button);
button.addActionListener(this);

Option 2 Anonymous class

    JFrame frame = new JFrame();

JButton button = new JButton("Click Here");
frame.add(button);

button.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked");
}
});

Option 3

This is not recommended, ugly and has unintended side effects (imagine calling add again). But you asked for a way to do it directly inside the add.

    JFrame frame = new JFrame();

JButton button = new JButton("Click Here");
frame.add(new JButton("Click Here"){
@Override
public void addActionListener(ActionListener l) {
super.addActionListener(YourClass.this);
}
});

Java Swing; Two classes, where to put if statements and new actionlisteners?

Because ClockListener is a nested class (lower), the enclosing instance (upper) can access the listener's private fields. If you have a reference to an instance of ClockListener,

ClockListener cl = new ClockListener();

you can use it to initialize your timer

Timer t = new Timer(1000, cl);

and you can use it in your test:

if (cl.count == 2) { t.stop(); }

Addendum: For reference, here's a variation of your program that uses a JToggleButton to control the timer. As suggested earlier, you had used Calendar to minimize Timer drift. Like you, I abandoned the approach as irrelevant in a low-resolution application.

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.Timer;

/** @see https://stackoverflow.com/questions/5528939*/
class ClockExample extends JFrame {

private static final int N = 60;
private static final String stop = "Stop";
private static final String start = "Start";
private final ClockListener cl = new ClockListener();
private final Timer t = new Timer(1000, cl);
private final JTextField tf = new JTextField(3);

public ClockExample() {
t.setInitialDelay(0);

JPanel panel = new JPanel();
tf.setHorizontalAlignment(JTextField.RIGHT);
tf.setEditable(false);
panel.add(tf);
final JToggleButton b = new JToggleButton(stop);
b.addItemListener(new ItemListener() {

@Override
public void itemStateChanged(ItemEvent e) {
if (b.isSelected()) {
t.stop();
b.setText(start);
} else {
t.start();
b.setText(stop);
}
}
});
panel.add(b);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.setTitle("Timer");
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}

public void start() {
t.start();
}

private class ClockListener implements ActionListener {

private int count;

@Override
public void actionPerformed(ActionEvent e) {
count %= N;
tf.setText(String.valueOf(count));
count++;
}
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
ClockExample clock = new ClockExample();
clock.start();
}
});
}
}


Related Topics



Leave a reply



Submit