My application has a module which allows the user to add jButtons on the jLayeredpane during runtime. I want to add action listeners to this dynamically added contents and also i have to provide access to delete the dynamically added buttons during runtime. Is there any way to do this ?
private Map<String, JButton> dynamicButtons;
public void addButton(String name) {
JButton b = new JButton(name);
b.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jLayeredPane2.add(b);
dynamicButtons.put(name, b);
jLayeredPane2.invalidate();
}
public void removeButton(String name) {
JButton b = dynamicButtons.remove(name);
jLayeredPane2.remove(b);
jLayeredPane2.invalidate();
}
Original Answer Good in general, but done differently in this case
In order to keep track of an arbitrary number of added JButtons, you will need to keep them in a list.
So, after you create a new button, add the listeners to it, and add it to the pane, you then need to save that new button in a list.
That way you can keep track of all of the buttons you have added.
You could also use a Map<String, JButton> that maps a button name to the button.
Example:
private Map<String, JButton> dynamicButtons;
public void addButton(String name) {
JButton b = new JButton(name);
b.addActionListener(someAction);
yourPanel.add(b);
dynamicButtons.put(name, b);
yourPanel.invalidate();
}
public void removeButton(String name) {
Button b = dynamicButtons.remove(name);
yourPanel.remove(b);
yourPanel.invalidate();
}
The following is a full class that lets you add and remove buttons dynamically. It's not exactly what you want, but it should get you really close.
Code for your specific case:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class ExampleFrame extends JFrame {
private JButton add, remove;
private JPanel dynamicButtonPane, addRemovePane;
private boolean waitingForLocationClick;
public ExampleFrame() {
super("Dynamic button example");
waitingForLocationClick = false;
add = new JButton("Add Button");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
addButton(JOptionPane
.showInputDialog("Name of the new button:"));
}
});
remove = new JButton("Remove Button");
remove.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lookingToRemove = true;
}
});
JPanel mainPane = new JPanel(new BorderLayout());
dynamicButtonPane = new JPanel();
dynamicButtonPane.setLayout(null);
dynamicButtonPane.setPreferredSize(new Dimension(300, 300));
addRemovePane = new JPanel();
addRemovePane.add(add);
addRemovePane.add(remove);
mainPane.add(dynamicButtonPane, BorderLayout.NORTH);
mainPane.add(addRemovePane, BorderLayout.SOUTH);
add(mainPane);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
dynamicButtonPane.addMouseListener(pointSelectorListener);
}
private JButton buttonToPlace;
public void addButton(String name) {
JButton b = new JButton(name);
b.setActionCommand(name);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (lookingToRemove) {
if (e.getSource() instanceof JButton) {
dynamicButtonPane.remove((Component) e.getSource());
dynamicButtonPane.validate();
dynamicButtonPane.repaint();
}
} else
JOptionPane.showMessageDialog(ExampleFrame.this, "This is " + e.getActionCommand());
}
});
waitingForLocationClick = true;
lookingToRemove = false;
buttonToPlace = b;
}
public void putButtonAtPoint(Point p) {
System.out.println("Placing a button at: " + p.toString());
dynamicButtonPane.add(buttonToPlace);
buttonToPlace.setBounds(new Rectangle(p, buttonToPlace
.getPreferredSize()));
dynamicButtonPane.validate();
buttonToPlace = null;
waitingForLocationClick = false;
}
private boolean lookingToRemove = false;
private final MouseListener pointSelectorListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (waitingForLocationClick) {
putButtonAtPoint(e.getPoint());
} else {
System.out.println("Not in waiting state");
}
}
};
public static void main(String[] args) {
new ExampleFrame();
}
}
Absolutely. All of this stuff can be done programatically at any time. Here are a couple of hints to avoid problems and pitfalls:
When you add components to any panel, make sure this is done on the Event Dispatch Thread through SwingUtilities.invokeLater(Runnable). Inside the Runnable, you want to add the component to the panel, hook up the listeners, and re-layout the panel.
Use SwingUtilities.isEventDispatchThread() to check to see if you are already on the event dispatch thread. If you are, then you can just run the Runnable immediately instead of calling invokeLater.
Once you've modified the layout of a panel, be sure to call Component.invalidate() on the panel to make sure it gets laid out again.
Maintain your own list of listeners. Overwrite the add and remove methods on the panel to add or remove them from your list and also from all existing buttons. When you add new buttons, add all listeners on the list.
This is a very common task, and it is fully supported by Java. You should be able to get it done without too much trouble.
Related
I am developing a Swing application, in which I want enter key work as tab key for all component of JFrame except JButton components and dialog boxes. For that I have set ENTER and TAB as default focus traversal keys.
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
Set<KeyStroke> keys = new HashSet<>();
keys.add(enter);
keys.add(tab);
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);
This is working well,but I want that ENTER Key work as Action on JButton and Dialog Boxes.
It's possible, but using another way: global event listener. To register a global event listener you should use the Toolkit class:
Toolkit.getDefaultToolkit().addAWTEventListener(listener, mask);
Here is an example for your case:
import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
public class FocusTransferTest {
public static void main(String[] args) {
JFrame frm = new JFrame("Test focus transfer");
JPanel panel = new JPanel();
panel.add(new JTextField(10));
panel.add(new JTextField(10));
panel.add(new JTextField(10));
JButton btn = new JButton("Press me");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frm, "It's a message", "Info",
JOptionPane.INFORMATION_MESSAGE);
}
});
panel.add(btn);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEnterKeyListener(),
AWTEvent.KEY_EVENT_MASK);
frm.add(panel);
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
private static class AWTEnterKeyListener implements AWTEventListener {
#Override
public void eventDispatched(AWTEvent event) {
if (event instanceof KeyEvent) {
KeyEvent key = (KeyEvent) event;
if (key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiersEx() == 0
&& key.getID() == KeyEvent.KEY_PRESSED) {
if (key.getComponent() instanceof AbstractButton) {
((AbstractButton) key.getComponent()).doClick();
} else {
key.getComponent().transferFocus();
}
}
}
}
}
}
I think even the solution using AWTEventListener would work, I would suggest avoid using AWTEventListener if another solution is available. It is because it's so powerful that it intercepts all kinds of events globally before they reach their real targets, so if anything went wrong (such as a NullPointerException) in the middle, the whole application would stop working.
My proposed solution makes use of input map & action map which adds handling of Enter key to any focused component in a particular container.
The advantage:
Safer because it affects only components in a container instead of all components.
The disadvantage:
The same handling code has to apply to all containers that need this behavior, but this could be easily accomplished by a static utility method.
Here is the sample program:
public MainFrame() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(500, 500);
setLayout(new GridLayout(2, 2));
addAllComponents();
addEnterKeyAsFocusTraversal();
}
private void addAllComponents() {
add(new JTextField());
add(new JTextField());
add(new JButton("OK"));
add(new JButton("Cancel"));
}
private void addEnterKeyAsFocusTraversal() {
final String ENTER_KEY_ACTION = "EnterKeyAction";
// Here uses the content pane of type Container so a cast is required,
// in other case it could be the root container which may already be an instance of JComponent.
JComponent contentPane = (JComponent) getContentPane();
contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ENTER_KEY_ACTION);
contentPane.getActionMap().put(ENTER_KEY_ACTION, createEnterKeyAction());
}
private AbstractAction createEnterKeyAction() {
return new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (focusOwner != null) {
if (focusOwner instanceof AbstractButton) {
((AbstractButton) focusOwner).doClick();
} else {
focusOwner.transferFocus();
}
}
}
};
}
The approved answer is actually bit wrong.
It add Enter as focus travel key, however it DOES NOT modify the basic button behavior of .doClick();
So this code spams .doClick(); second time. I came across this issue when on button action I was writing data from bunch of text Fields to table, and it added the same record two times. solution for this is pretty simple, on instanceof JButton just make return;
repaired code upgraded to java 14 instanceof
private static class AWTListener implements AWTEventListener{
#Override
public void eventDispatched(AWTEvent event) {
if (event instanceof KeyEvent key && key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiersEx() == 0 && key.getID() == KeyEvent.KEY_PRESSED) {
if (key.getComponent() instanceof JButton) {
return;
}
key.getComponent().transferFocus();
}
}
}
I have a jframe that includes JButton.I have six buttons in this frame, but I don't know how to define action listener for this buttons.please help to solve this problem.
First you have to import the package java.awt.event.* to enable events. After the class name you have to add implements ActionListener so that the class can handle events. When you have created the buttons you have to add an actionlistener to each button. Since you haven't showed which code you use I make an example with a simple program that counts votes, if the user clicks the yesButton the votes are increased with 1 and if the user clicks the noButton the votes are decreased with 1.
Here is the code to add an ActionListener to each button:
yesButton.addActionListener(this);
noButton.addActionListener(this);
Then write the following code to handle the events:
public void actionPerformed(ActionEvent e) {
JButton src = (JButton) e.getSource();
if(src.getActionCommand().equals("Yes")) {
yesCount++;
} else {
noCount++;
}
label.setText("Difference: " + (yesCount - noCount));
}
If you have 6 buttons you need to have an if statement and then 5 "else if" statements instead of only an if and an else statement.
Have a look at the Java tutorials on how to use ActionListeners:
https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
Here's a simple example:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Hello extends JPanel implements ActionListener {
JButton button;
public Hello() {
super(new BorderLayout());
button = new JButton("Say Hello");
button.setPreferredSize(new Dimension(180, 80));
add(button, BorderLayout.CENTER);
button.addActionListener(this); // This is how you add the listener
}
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e) {
System.out.println("Hello world!");
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Hello");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Hello();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
buttons have a method called addActionListener, use that for adding the action listener that you can implement for the click...
Example:
dummyButton = new JButton("Click Me!"); // construct a JButton
add(dummyButton); // add the button to the JFrame
dummyButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(" TODO Auto-generated method stub");
}
});
It's really simple.
I suppose you have an instance of your button, right? Let's say that instance is called myButton.
You can just add an action listener by calling addActionListener:
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Do whatever you like here
}
});
Protip: next time you don't know what method to call, just type the instance name and .. Then, your IDE will show you all the methods you can call, unless you are not using an IDE. If that is the case, download one.
I have 20 JLabels and all of them have to change their background color when mouse enters and change back to original color when mouse outs.
Do I have to individually bind 2 Event-listeners of MouseEntered and MouseExited with all JLabels separately, or is there any work around so I can make just 2 events kind of monitoring all JLabels?
Like in the image below: there are about 6 JLabels and I want each one to change its background color whenever the mouse enters the scene and change back to original color when the mouse outs.
So, do I have to individually set event listeners on all JLabels, or there can be a single event listener for all JLabels?
You can register all 20 JLabels with the same mouse listener. You would do something like this:
MouseListener m = new MouseAdapter() // create our own mouse listener
{
#Override
public void mouseEntered(MouseEvent e)
{
e.getComponent().setBackground(Color.RED);; // this method changes the colours of all the labels
}
#Override
public void mouseExited(MouseEvent e)
{
e.getComponent().setBackground(Color.GREEN); // this method changes the colours back to normal
}
};
for (JLabel label: labels) // iterate over all the labels
{
label.addMouseListener(m); // give them all our mouse listener
}
Where "labels" is some collection (List, Set, array...) of your JLabels, and changeLabelColours() and changeLabelColoursBack() are two methods that you define to change the colours.
hope this helps!
EDIT: reading your edited question, I think I should point out that this code will cause ALL labels to change colour when ANY of the labels is moused-over. I think that's what you mean.
You don't "make events". You make eventListeners. And yes, you can do just 2 event listeners and bound them to all JLabels.
Or, if you prefer, you can extends Jlabel into MyJlabel, that has the event listener built in, and save yourself from the repeated binding, if it bothers you.
You can use one MouseListener reference.
You should differentiate event sources based on references or on component name.
Here is an example
final JLabel label1 = new JLabel("label1");
label1.setName("label1");
final JLabel label2 = new JLabel("label2");
label2.setName("label2");
final JLabel label3 = new JLabel("label3");
label3.setName("label3");
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
// you can check references
if(e.getSource() == label1) {
System.out.println("exited " + label1.getName());
} else if (e.getSource() == label2) {
System.out.println("exited " + label2.getName());
} else if (e.getSource() == label3) {
System.out.println("exited " + label3.getName());
}
}
#Override
public void mouseEntered(MouseEvent e) {
String name = ((JComponent)e.getSource()).getName();
// or you can check name of component
switch (name) {
case "label1":
case "label2":
case "label3":
System.out.println("entered " + name);
break;
}
}
};
for (JLabel l : Arrays.asList(label1, label2, label3)) {
l.addMouseListener(mouseListener);
}
Note that labels need to be opaque in order to change their bg color.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class CrazyLabels extends JFrame {
public CrazyLabels() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JPanel content = new JPanel();
add(content);
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
MouseAdapter listener = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
JLabel label = (JLabel)e.getSource();
label.setBackground(Color.red);
}
#Override
public void mouseExited(MouseEvent e) {
JLabel label = (JLabel)e.getSource();
label.setBackground(UIManager.getColor("Label.background"));
}
};
for (int i = 0; i < 5; i++) {
JLabel label = new MyLabel(listener);
label.setText("---- label ----");
content.add(label);
}
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CrazyLabels().setVisible(true);
}
});
}
private static class MyLabel extends JLabel {
public MyLabel(MouseListener listener) {
setOpaque(true);
addMouseListener(listener);
}
}
}
In this example a single listener is used to enforce behavior in all labels.
package com.spiralfive.inventory;
import java.awt.Font;
import java.awt.Panel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.BorderLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
public class MainFile extends JApplet implements MouseListener {
// Declare the initial components
private JButton btnNewInventory;
private JButton btnMarkDown;
private JButton btnProcessSales;
private JButton btnActive;
private JButton btnStats;
private Panel mainPanel;
private Panel menuPanel;
private Panel inventoryPanel;
private Panel setactivePanel;
// Call the Initializer...
public void init() {
initComponents();
}
// Initialize the Main Components - Mostly Menu Items
private void initComponents() {
// Main Form Components
mainPanel = new Panel();
menuPanel = new Panel();
inventoryPanel = new Panel();
setactivePanel = new Panel();
btnNewInventory = new JButton("New Inventory");
btnActive = new JButton("Set Active");
btnMarkDown = new JButton("Mark Down");
btnProcessSales = new JButton("Process Sale");
btnStats = new JButton("Statistics");
// Enable Mouse Interactivity
btnNewInventory.addMouseListener(this);
btnMarkDown.addMouseListener(this);
btnProcessSales.addMouseListener(this);
btnActive.addMouseListener(this);
btnStats.addMouseListener(this);
// Set the Fonts
Font buttonFont = new Font("Arial", Font.PLAIN, 20);
// Apply Fonts To Menu
btnNewInventory.setFont(buttonFont);
btnMarkDown.setFont(buttonFont);
btnProcessSales.setFont(buttonFont);
btnActive.setFont(buttonFont);
btnStats.setFont(buttonFont);
// Set Panel Layout For Menu Items
menuPanel.setLayout(new java.awt.GridLayout(1,6));
// Add the Components
menuPanel.add(btnNewInventory);
menuPanel.add(btnActive);
menuPanel.add(btnMarkDown);
menuPanel.add(btnProcessSales);
menuPanel.add(btnStats);
// Add The Menu To the Main Panel
mainPanel.add(BorderLayout.NORTH, menuPanel);
// Make Inventory Load by Default
createPanels();
makeInvVisible();
// Set It All Visible
add(mainPanel);
}
// Create the Panels Used in this applet
public void createPanels() {
NewInventory inventoryPanel = new NewInventory();
inventoryPanel.setLayout(new java.awt.GridLayout(6,2));
mainPanel.add(BorderLayout.CENTER, inventoryPanel);
SetActive setactivePanel = new SetActive();
setactivePanel.setLayout(new java.awt.GridLayout(6,2));
mainPanel.add(BorderLayout.CENTER, setactivePanel);
}
public void makeInvVisible() {
inventoryPanel.setVisible(true);
mainPanel.repaint();
}
public void makeInvDisappear() {
inventoryPanel.setVisible(false);
mainPanel.repaint();
}
public void makeActiveVisible() {
setactivePanel.setVisible(true);
mainPanel.repaint();
}
public void makeActiveDisppear() {
setactivePanel.setVisible(false);
mainPanel.repaint();
}
public void mouseClicked(MouseEvent e) {
// Determine Which Button Has Been Clicked
JButton currentButton = (JButton)e.getComponent();
// New Inventory Button
if(currentButton == btnNewInventory) {
makeInvVisible();
makeActiveDisppear();
}
// Set Active Button
else if(currentButton == btnActive) {
makeActiveVisible();
makeInvDisappear();
}
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
I am using the code above attempting to make a new Panel() appear and the existing panel disappear when a button is clicked by using setVisible. However, I cannot get the Panel() to change. Nothing happens when the buttons are clicked, which are set to change setVisible to false on the current Panel and true on the requested Panel.
I am fairly certain it is because the panels are set a private in a different class (initComponents). How do I access the setVisible property on the panels? Or, is this even what's wrong?
Add
this.validate();
in the makeActiveVisible() method.
Swing components have a default state of being invalid and won't be painted to the screen unless validated (by calling the .validate() method on either the component itself or on one of the parent containers).
See also
repaint() in Java
Alright, I have a simple java applet with two buttons and a screen. Both the buttons do the same thing. I want to change this. I can't find what it is that changes the action that is performed when either one of the buttons is pressed. They both to the same thing and I don't want this. So my question is how would I change the Inventory button to display "Hello world" instead of a line count?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class projectApplet extends JApplet implements ActionListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
private JTextArea textArea;
private int lineNumber = 0; // this is just to test
public void init() {
JPanel panel = new JPanel();
textArea = new JTextArea();
textArea.setBackground(Color.BLACK);
textArea.setForeground(Color.WHITE);
JScrollPane sp = new JScrollPane(textArea);
panel.add(sp);
Container window = getContentPane();
window.setLayout(new BorderLayout());
window.add(sp,BorderLayout.CENTER);
// this is just to test------------------
JButton b = new JButton("Clik to add a line");
b.addActionListener(this);
window.add(b, BorderLayout.SOUTH);
JButton inventory = new JButton("Inventory");
inventory.addActionListener(this);
window.add(inventory, BorderLayout.NORTH);
//---------------------------------------
}
public void actionPerformed(ActionEvent arg0) {
lineNumber++;
textArea.append("\nLine number: " + lineNumber);
}
public void actionPerformed1(ActionEvent arg0) {
lineNumber++;
textArea.append("RPFL");
}
}
Add a new action listener to it. Typically you can use an anonymous inner class:
inventory.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
textArea.append("Hello, world");
}
});
Just have the one actionPerformed method and then find out which button triggered it.
For example:
public void actionPerformed(ActionEvent arg0) {
if(arg0.getLabel()=="Inventory") // Do the following
if(arg0.getLabel()=="Click to add a new line") // Do the following
}
Note, getLabel() method is deprecated so you'll have to use another... can't remember off the top of my head which you should though... maybe getName(). But this is a simple way to test which button was clicked ;)
You can't do arg0.getSOurce() inside the action performed method to checkout which button has generated this event.