actionlistener callback to another class - java

I have a class LabelTextField which does as it indicates, it create a label followed by text field. I want this class to act as JTextField does, ie responding to actionPerformed when an action listener is added. I am adding an action listener with LabelTextField so the JTextField in this class will need to receive the callback and forward on the LabelTextField object name and not the JTextField object name.
I have show a simple JTextField and how it works, the LabelTextField is also there and how I expect to use it.
I have provided a code snippet of what I want to do.
I am having trouble with this type of callback and detail information would be useful.
//
class C_one extends JPanel
{
private JTextField mjtfOne;
private LabelTextField m_nameFind;
public C_one ()
{
setLayout(new FlowLayout());
mjtfOne = new JTextField(20);
mjtfOne.addActionListener(this);
m_nameFind = new LabelTextField("Name", 20);
m_nameFind.addActionListener(this);
add(mjtfOne);
add(m_nameFind);
}
// This is called when return is pressed in either object.
public void actionPerformed(ActionEvent ae)
{
// This works.
if (ae.getSource() == mjbnOne) {
System.out.println("Text field one!");
// ****** This doesn't work. *****
} else if (ae.getSource() == m_nameFind) {
System.out.println("Label Text Field name");
}
}
}
//
// This create a label and textfield as a widget.
class LabelTextField extends JPanel
{
private JTextField mjtf;
private ActionListener mal;
public LabelTextField(String label, int textfieldWidth)
{
setLayout(new FlowLayout());
JLabel lbl = new JLabel(label);
add(lbl);
mjtf = new JTextField(textfieldWidth);
add(mjtf);
}
// The caller of this class will call this as he would for
// JTextField.
public void addActionListener(ActionListener al)
{
mjtf.addActionListener(this);
mal = al;
}
// This will receive the return from JTextField and needs
// to forward this on to calling class with caller object
// not mjtf.
//
// Problem: I can not figure out how to save caller object
// name so when this is called I can forward it on just like
// what JTextField would have done, ie caller can use
// ae.getSource() == m_nameFind.
public void actionPerformed(ActionEvent ae)
{
mal.actionPerformed(ae); // This does call the caller.
}
}

the value of ae.getSource() will be mjtf, because that is the component that generated the original ActionEvent.
If you want to have the source of the event to be m_nameFind, i.e. your custom LabelTextField object, you need to set it manually:
public void actionPerformed(ActionEvent ae)
{
ae.source=this; // this makes it look like the LabelTextField generated the event
mal.actionPerformed(ae);
}
Is this what you want to do?
--
edit
Thanks #Madprogrammer.. Perhaps you should recreate the ActionEvent like this
public void actionPerformed(ActionEvent ae)
{
mal.actionPerformed(new ActionEvent(this, ae.getID(),
ae.getCommand(), ae.getWhen(), ae.getModifiers() ));
}

Since you can't change the source of the ActionEvent, you need to create a new ActionEvent:
public void actionPerformed(ActionEvent e)
{
e = new ActionEvent(
this,
ActionEvent.ACTION_PERFORMED,
e.getActionCommand(),
e.getWhen(),
e.getModifiers());
mal.actionPerformed(e);
}

Related

How to properly format an ActionEvent so JButtons will work

I have set up some ActionListeners, however only 'Takeoff' works. The other buttons do not work when they are clicked. When they are clicked, nothing happens.
I have tried to create a new ButtonHandler, but that did not work.
ButtonListener l = new ButtonListener();
JButton takeoff = new JButton("Takeoff");
takeoff.addActionListener(new ButtonHandler());
takeoff.addActionListener();
grid[0][2].add(takeoff);
JButton land = new JButton("Land");
land.addActionListener(new ButtonHandler());
grid[1][2].add(land);
JButton forward = new JButton("Forward");
forward.addMouseListener(new MouseHandler(l));
forward.addActionListener();
grid[2][1].add(forward);
JButton left = new JButton("Left");
left.addMouseListener(new MouseHandler());
left.addActionListener(new ButtonHandler());
left.addActionListener();
grid[3][0].add(left);
takeoff.addActionListener(l);
land.addActionListener(l);
forward.addActionListener(l);
backward.addActionListener();
left.addActionListener(l);
right.addActionListener(l);
turnLeft.addActionListener(l);
turnRight.addActionListener(l);
up.addActionListener(l);
down.addActionListener(l);
stop.addActionListener(l);
What I want to do is move the robot drone in the correct direction, rather than just letting it sit still.
I am not sure if this part will help, but I have where my ButtonHandler implements ActionListener.
private class ButtonHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String button = e.getActionCommand();
if (button.equals("Takeoff")) {
RobotModel.takeoff();
}
else if (button.equals("Land")) {
RobotModel.land();
}
else if(button.equals("Left")) {
RobotModel.left();
}
}
}
You could use the actionCommand to invoke a method via reflection, e.g.
private void init() {
JButton left = new JButton("Go left");
// This
left.setActionCommand("left");
left.addActionListener(new MethodCallActionHandler());
// OR that
left.addActionListener(new MethodCallActionHandler("left"));
}
private void left() {
// go left...
}
private class MethodCallActionHandler implements ActionListener {
private String methodName;
private MethodCallActionHandler() {
super();
}
private MethodCallActionHandler(String methodName) {
this.methodName = methodName;
}
public void actionPerformed(ActionEvent e)
{
String button = methodName != null ? methodName : e.getActionCommand();
SurroundingClass.this.getClass().getMethod(button, new Class[] {}).invoke(SurroundingClass.this);
}
}
You could also pass the action command as String to the MethodCallActionHandler.
You can inherit the Action Listener class into your current class and then add the required methods. Then you can do takeoff.add(this)... etc.
Also nowhere are you setting the action command, that is done in the button settings.
When you are setting
String button = e.getActionCommand();
That is not what is being set when you do
JButton button = new JButton("Takeoff"); <-- This is the text associated with the button
button.setActionCommand("Takeoff");
and then it should work.

why doesnt my actionListener work with my jcomboBox

I know I'm missing something very simplem but for the life of me I can't see it. All I want to do is get "Paris" from the combo box, and when the button is pressed, show that "Paris" is selected.
public class assignment2try2 implements ActionListener {
private JComboBox HolidayLocation;
private JComboBox HolidayDuration;
private JButton PriceCheck;
public static void main(String[] args) {
JLabel Location = new JLabel(" Where do you want to go ? ");
String[] HolidayLocations = {" ","Paris", "Crete", "Croatia"};
JComboBox<String> LocationBox = new JComboBox<String>(HolidayLocations);
LocationBox.setEditable(false);
LocationBox.setPreferredSize(new Dimension( 160, 20 ));
//LocationBox.setSelectedIndex(4);
LocationBox.addActionListener(LocationBox);
JButton PriceCheck = new JButton("Check Availability");
PriceCheck.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("button works");
//if(LocationBox.getSelectedItem().equals(HolidayLocations))
{
//System.out.println("paris selected");
}
}
});
}
}
EDIT: I just now noticed that your class implements ActionListener. With the below solution, you can remove the implements-statement from your code.
To fix your issues with the String having to be final, make a private class:
private class MyListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println(locationBox.getSelectedItem() + " selected.");
}
}
Then, replace
PriceCheck.addActionListener(new ActionListener() { ... });
with
PriceCheck.addActionListener(new MyListener());
This should be able to print out the selected value after the button is pressed.
Note: I changed your variable name from LocationBox to locationBox to comply with naming conventions.

I can't get the string from ActionListener to different class in Java

I created a JComboBox and JButton to submit information. I need the information to be sent to a different class to sort it out with a switch method. But it looks like the string created inside the ActionListener is not recognized by a different class.
public Main() {
final JComboBox comboB = new JComboBox(b); //add int b in here for array
comboB.setBounds(50, 30, 123, 20);
contentPane.add(comboB);
JButton btnTest = new JButton("Test");
btnTest.setBounds(300, 350, 89, 23);
contentPane.add(btnTest);
btnTest.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String s = (String)comboB.getSelectedItem();
}
});
}
How do I make it so that String s can be recognized by other classes? I have a separate class that will change action depending on what is selected from ComboBox, but I just can't seem to get this information out. Thank you.
Firstly, other objects need some way to register an ActionListener to the combo box. I would suggest providing a addActionListener method to your class, this would act as a proxy method and simple pass the call onto comboB
Secondly, this means comboB is going to need to be a class instance variable
Thirdly, the other classes are going to need to determine if the action originiated from the combo box or not, for example.
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JComboBox) {
JComboBox cb = (JComboBox)e.getSource();
String s = (String)cb.getSelectedItem();
}
}
Now, there's not a lot of context available to the question, but, personally, I would normally either use a model of some kind that your UI class would update and/or a PropertyChangeListener that other classes could register against and monitor for changes to the "properties" of the your main class.
You just need to create a private method and have the combo call that. Then you just navigate to your component/class, and perform the action.
public Main() {
final JComboBox comboB = new JComboBox(b); //add int b in here for array
comboB.setBounds(50, 30, 123, 20);
contentPane.add(comboB);
JButton btnTest = new JButton("Test");
btnTest.setBounds(300, 350, 89, 23);
contentPane.add(btnTest);
btnTest.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String s = (String)comboB.getSelectedItem();
myMethodThatProcessesS(s);
}
});
}
private void myMethodThatProcessesS(String s) {
contentPane.getSomeOtherComponent().doSOmething(s);
}
Since java swing implements the MVC pattern you can pass the JComboBox's model reference to other objects.
Models implement the observer pattern and therefore the other objects can register themself if they need to get notified immediatly when the model changes.
public class Main {
public initializeComponent(OtherClass otherClass) {
...
JComboBox comboBox = ...;
ComboBoxModel comboBoxModel = comboBox.getModel();
otherClass.setComboBoxModel(comboBoxModel);
}
}
public class OtherClass {
private ComboBoxModel comboBoxModel;
public void setComboBoxModel(ComboBoxModel comboBoxModel) {
this.comboBoxModel = comboBoxModel;
ListDataListener listener = ...;
comboBoxModel.addListDataListener(listener);
}
public String getSelectedItem(){
Object selectedItem = comboBoxModel.getSelectedItem();
...
}
}

How to "ignore/discard" Swing UI events when doing data validations on UI?

There's a text field and when lost focus it will validate the inputs, if not passed, print out the error message (to be simple here just has an empty check). And there's a button next to the text field, it will print out the text once click on it.
As I tried, when input some text and then click the button it will trigger both the focus lost event of text field and the event of button. In a other word, it will do the validation first and then print out the input text.
Here comes my question, what is the good approach to prevent printing out the text if the validation not passed? Or is there a way to "ignore" the click event on button if validation not passed?
I tried to use a boolean flag which indicate the validation result and check the flag when perform the action for button, but I do not think it is a good approach. As I know there's an event dispatcher thread in Swing which deal with the events, is it possible I can cancel the events from here?
Below is a piece of code which explain the question:
public class SimpleDemo
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = new JPanel(new FlowLayout());
frame.setContentPane(content);
final JTextField textField = new JTextField(10);
textField.addFocusListener(new FocusAdapter()
{
#Override
public void focusLost(FocusEvent e)
{
String text = textField.getText();
// do some validation here, if not validated
// do not trigger the event on button.
if ("".equals(text))
{
System.out.print("please input a text!");
}
}
});
content.add(textField);
JButton button = new JButton("Print Text");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// action performed for button
String text = textField.getText();
System.out.println(text);
}
});
content.add(button);
frame.setVisible(true);
frame.pack();
}
}
I faces similar issue while working on an application. I solved it like below
I created a abstract class ApplicationFrame which every frame in the application extends
public abstract class ApplicationFrame extends JFrame implements ActionListener {
#Override
final public void actionPerformed(ActionEvent event) {
if(validateInput()){
performAction(event);
}
}
/*
* Sub class should override this method to receive any action
*/
protected void performAction(ActionEvent event) {};
/*
* Sub class should override this method to perform validation
*/
abstract protected boolean validateInput();
}
All Frames will now extend this base frame, as below:
public class Frame1 extends ApplicationFrame{
#Override
protected void performAction(ActionEvent event) {
// perform action
}
#Override
protected boolean validateInput() {
// return true or false depending upon the validation results
}
// if you want to add Action Listener, you need to add like this:
btnSomeButton.addActionListener(this);
}
If you need to handle Focus events, you can make ApplicationFrame or the base frame implement FocusListener.
This is my custom implementation to solve the problem, hope this helps.
Make the button disabled on start-up
Upon lost focus, validate the text & enable button only when the input passes validation.
Upon start of text change, disable the button
It's always makes sense to make ui to communicate with user. So you can show "please input a text" as the default text of the textField when nothing is entered by user.
Here is the code for such custom textField:
public class TextFieldWithDefaultText extends JTextField implements FocusListener{
private final String hint;
public TextFieldWithDefaultText (String $text)
{
super($text);
this.hint = $text;
addFocusListener(this);
}
#Override
public void focusGained (FocusEvent $e)
{
if (this.getText().isEmpty())
{
super.setText("");
}
}
#Override
public void focusLost (FocusEvent $e)
{
if (this.getText().isEmpty())
{
super.setText(hint);
}
}
#Override
public String getText ()
{
String typed = super.getText();
return typed.equals(hint) ? "" : typed;
}
}
Write the acttionListerner for your button like this:
JButton button = new JButton("Print Text");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if(!textField.getText().isEmpty())
System.out.println(textField.getText());
}
});
And ur textField implementation should be :
final TextFieldWithDefaultText textField = new TextFieldWithDefaultText ("please input a text");
Hope this helps :)

A simple question regarding ActionListener and 'enter' key

I am working on an assignment, and I need to enter an SQL Query in a textfield. The user can either press the custom 'execute query' button, or they can press the enter key. When either of these are used, it is to trigger an ActionListener (no other listener is allowed). Is it as simple as writing:
if (e.getSource()=='querybutton' || e.getSource=='enter')
Or is there more to it than this?
As I said, it is a simple question (I know).
edit:
I would write this bit in my ActionPerformed as:
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==gui.executeQueryButton || e.getSource()==gui.enter)
{
String query = gui.queryText.getText();
//more code to follow
}
}
e.getSource() actually returns the object responsible for firing the event (not the name of the variable you used when creating the control). In this case, your button. You could in principle compare e.getSource() with the actual button instances. However, are you actually adding this action listener to buttons other than those two? Presumably you'd only have to add this listener to the two buttons for which you want this behavior -- in which case you wouldn't have to have this if check.
" Is it as simple as writing:
if (e.getSource()=='querybutton' || e.getSource=='enter')"
It's not simple to write this, but rather it is wrong to write it.
For one you don't want to compare Strings with ==, for another, you don't declare Strings with single quotes, and for a third, the enter key is not obtained in this way, but rather by adding the appropriate ActionListener object to the JTextField itself, and finally there should be in a single ActionListener class that handles this action, so the if block is completely unnecessary. This can probably be best done with a small inner private ActionListener class. You'd then create one object of this class and add it as an ActionListener for the querybutton and for the JTextField.
edit 1:
A more complete example of what I mean is shown below, a demo class that has a private inner handler class:
import java.awt.event.*;
import javax.swing.*;
public class ActionListenerEg extends JPanel {
private JButton queryButton = new JButton("Query");
private JTextField textField = new JTextField("hello", 20);
public ActionListenerEg() {
QueryListener qListener = new QueryListener();
queryButton.addActionListener(qListener);
textField.addActionListener(qListener);
add(queryButton);
add(textField);
}
private class QueryListener implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
String textInField = textField.getText();
System.out.println("Use text in field, \"" + textInField + "\" to call SQL query in a background SwingWorker thread.");
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("ActionListenerEg");
frame.getContentPane().add(new ActionListenerEg());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
The ActionListener is fired either by pressing the button or by pressing enter from within the JTextField. I'd then have in my control class, code that is called inside of the actinoPerformed method.
edit 2: Having most handler or "control" code in its own Handler or Control class can be a good idea, but it doesn't have to implement ActionListener interface itself, but rather just have the code that will be called from within the ActionListener codes. For example, here I try to put all the handler code in its own class. It will have different methods that are called for various situations. e.g.,
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
public class ActionListenerEg extends JPanel {
private ActionListenerHandler handler;
private JButton queryButton = new JButton("Query");
private JButton displayButton = new JButton("Display");
private JTextField textField = new JTextField("hello", 20);
// pass in handler or handler
public ActionListenerEg(final ActionListenerHandler handler) {
this.handler = handler;
QueryListener qListener = new QueryListener();
queryButton.addActionListener(qListener);
textField.addActionListener(qListener);
displayButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (handler != null) {
handler.displayActionPerformed(e);
}
}
});
add(queryButton);
add(textField);
add(displayButton);
}
private class QueryListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (handler != null) {
String textInField = textField.getText();
handler.doQueryAction(e, textInField);
}
}
}
private static void createAndShowUI() {
ActionListenerHandler handler = new ActionListenerHandler();
JFrame frame = new JFrame("ActionListenerEg");
frame.getContentPane().add(new ActionListenerEg(handler));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class ActionListenerHandler {
public void displayActionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog((Component) e.getSource(), "Display things!");
}
public void doQueryAction(ActionEvent e, String textInField) {
String text = "We will use \"" + textInField + "\" to help create and run the SQL Query";
JOptionPane.showMessageDialog((Component) e.getSource(), text);
}
}
Please ask questions if it's clear as mudd, or if anything is wrong.

Categories