I have a Java Swing interface with multiple JTextArea's and I am implementing an "Edit" menu with various different functions like "Find", "Copy", "Paste", etc. When I click on the JMenuItem I need to know which JTextArea had the focus which is achievable through a TextAction (I haven't gone down the route of a FocusListener and keeping track of what last had the focus):
JMenuItem miFind = new JMenuItem(new EditHandler("Find"));
class EditHandler extends TextAction {
private String s = null;
public EditHandler(String vs) {
super(vs);
s = vs;
}
#Override
public void actionPerformed(ActionEvent e) {
JTextComponent c = getFocusedComponent();
if (s.equals("Find")) {
showFindDialog(c);
}
}
}
This works well and good but I want to be able to disable the "Find" JMenuItem under certain contexts (i.e. if the specific JTextArea is disabled or is empty. I can implement an ActionListener on a JMenu but I can't use getFocusedComponent() to identify what JTextArea has the focus.
According to the Java docs the JMenu constructor takes an Action (like a JMenuItem) and I have tried the following:
mEdit = new JMenu(new EditHandler("Edit"));
However, although the constructor fires, the actionPerformed() event isn't firing within my EditHandler for the JMenu. If I can get it to fire then I was planning to either enable or disable my "Find" JMenuItem.
The best way for you is using of actions map of the text component to place the corresponding action. In this case you can disable it for some text components.
#Override
public void actionPerformed(ActionEvent e) {
JTextComponent c = getFocusedComponent();
if (s.equals("Find")) {
Action a = c.getActionMap().get("Find");
if (a.isEnabled()) {
// generate new event to modify the source (menu item -> text component)
ActionEvent ae = new ActionEvent(c, e.getID(), e.getCommand());
a.actionPerformed(ae);
}
}
}
For each your text component you must provide an action and register it using the action map of the component.
public class UniversalFindAction extends AbstractAction {
public void actionPerformed(ActionEvent ae) {
JTextComponent c = (JTextComponent) ae.getSource();
showFindDialog(c);
}
}
// registering of action
JTextComponent comp = new JTextArea();
comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK), "Find");
comp.getActionMap().put("Find", new UniversalFindAction());
Thanks to #sergiy-medvynskyy I have implemented a Global Focus Listener to keep track of the last JTextArea to be focused:
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
#Override
public void propertyChange(final PropertyChangeEvent e) {
if (e.getNewValue() instanceof JTextArea) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
tFocused = (JTextArea)e.getNewValue();
}
});
}
}
});
I then check the tFocused object using a MenuListener on my JMenu to verify what JTextArea currently has the focus. I can then call setEnabled() on my respective JMenuItem's depending on the context.
Related
How would I call an Action from another class in Java? I got a CloseTabButton class online that allows a simple close tab button on each JTabbedPane, but when the tab is closed, I would like a dialog to pop up based on information (if file is not saved, ask to save it, etc.). This is the file:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class CloseTabButton extends JPanel implements ActionListener {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, int index) {
this.pane = pane;
setOpaque(false);
// CloseIcon class just had a button with an x painted on it
Icon closeIcon = new CloseIcon();
JButton close = new JButton(closeIcon);
close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
close.addActionListener(this);
add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
add(close);
pane.setTabComponentAt(index, this);
}
#Override
public void actionPerformed(ActionEvent e) {
int i = pane.indexOfTabComponent(this);
String fileName = pane.getToolTipTextAt(i);
// Where I want to ask if user wants to save, etc.
if (fileName == "Untitled") {
// Do stuff
}
pane.remove(i); // Removes the tab
// If tab count < 1, then disable the save and save as buttons on menu
if (pane.getTabCount() < 1) {
JFrame frame = (JFrame) pane.getParent().getParent().getParent().getParent().getParent(); // Yes, there is that many in my code to get the parent JFrame
int menuCount = frame.getJMenuBar().getMenuCount();
for (int a = 0; a < menuCount; a++) {
int itemCount = frame.getJMenuBar().getMenu(a).getItemCount();
for (int b = 0; b < itemCount; b++) {
Component component = frame.getJMenuBar().getMenu(a).getMenuComponent(b);
if (!(component instanceof JSeparator)) {
// Not a seperator
String itemName = frame.getJMenuBar().getMenu(a).getItem(b).getAccessibleContext().getAccessibleName();
if (itemName == "Save As..") {
frame.getJMenuBar().getMenu(a).getItem(b).setEnabled(false);
}
}
}
}
}
}
}
In my main class I have actions listed like this:
static Action Close = new AbstractAction("Close") {
public void actionPerformed(ActionEvent e) {
closeCurrentWindow(); // function that will close tab
}
}
The other menu items are Actions as well, and as you can see, what I'm currently doing in the CloseTabButton class is quite frustrating, and most likely the wrong way to code it. Is there a much simpler way to do what I'm doing?
The first thing I might do is provide ActionListener support to the CloseTabButton, for example...
public class CloseTabButton extends JPanel {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, int index) {
this.pane = pane;
setOpaque(false);
// CloseIcon class just had a button with an x painted on it
Icon closeIcon = new CloseIcon();
JButton close = new JButton(closeIcon);
close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireActionPerformed();
}
});
add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
add(close);
pane.setTabComponentAt(index, this);
}
public void addActionListener(ActionListener listener) {
listenerList.add(ActionListener.class, listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(ActionListener.class, listener);
}
protected void fireActionPerformed() {
ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "You could provide you own action command for each tab here");
for (ActionListener listener : listeners) {
listener.actionPerformed(evt);
}
}
}
Basically, this now allows you to register your own ActionListeners to the CloseTabButton
Next, this, fileName == "Untitled", is not how you compare Strings in Java, you should be using something more like "Untitled".equals(fileName)
If you're menus are based on actual Actions, then you can simply disable the Actions themselves. This would require a little bit of work, but a lot less of "guess" work then you're doing now.
Basically, you would monitor the JTabbedPane itself, monitoring for changes to the selected tab and updating the states of the individual Actions themselves
There a number of ways you could do this, like passing a reference of the JTabbedPane to the Actions so they can perform there own monitoring (but I'd use some kind of management interface which could more easily provide information to the Actions and decouple the code and the reliance on JTabbedPane directly, then you could be free to use JInternalFrames instead).
You could have a "menu manager" which did a similar job, monitoring changes to the document container and changing the state of the menu Actions based on the current state, as an example
Updated
If you're making use of the Action API (which I would recommend), then you could simply do something like...
public class CloseTabButton extends JPanel {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, Action action, int index) {
this.pane = pane;
setOpaque(false);
// CloseIcon class just had a button with an x painted on it
Icon closeIcon = new CloseIcon();
JButton close = new JButton(action);
close.setIcon(closeIcon);
close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
add(close);
pane.setTabComponentAt(index, this);
}
}
Passing in the Action for the close operation, then use the same action for both the JMenuItem and JTabbedPane.
The "core" issue would be how you would identify the "current" tab and document in a uniform manner
I have a JPanel with a bunch of different check boxes and text fields, I have a button that's disabled, and needs to be enabled when specific configurations are setup.
What I need is a listener on the the whole JPanel looking for events, whenever anything changes.
I believe I need an action listener but I can't find anything to bridge the action Listener with the JPanel
JPanel Window = new JPanel();
Window.addActionListener(new ActionListener(){
//Check if configurations is good
}
I figure I can copy and paste my code a bunch of times into every listener in the panel, but that seems like bad coding practice to me.
First off as #Sage mention in his comment a JPanel is rather a container than a component which do action. So you can't attach an ActionListener to a JPanel.
I figure I can copy and paste my code a bunch of times into every
listener in the panel, but that seems like bad coding practice to me.
You're totally right about that, it's not a good practice at all (see DRY principle). Instead of that you can define just a single ActionListener and attach it to your JCheckBoxes like this:
final JCheckBox check1 = new JCheckBox("Check1");
final JCheckBox check2 = new JCheckBox("Check2");
final JCheckBox check3 = new JCheckBox("Check3");
final JButton buttonToBeEnabled = new JButton("Submit");
buttonToBeEnabled.setEnabled(false);
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
boolean enable = check1.isSelected() && check3.isSelected();
buttonToBeEnabled.setEnabled(enable);
}
};
check1.addActionListener(actionListener);
check2.addActionListener(actionListener);
check3.addActionListener(actionListener);
This means: if check1 and check3 are both selected, then the button must be enabled, otherwise must be disabled. Of course only you know what combination of check boxes should be selected in order to set the button enabled.
Take a look to How to Use Buttons, Check Boxes, and Radio Buttons tutorial.
A suggestion could be to derive a class from each of the components you're using and add an ActionListener that bubbles up the Container tree and looks for the first Container that implements a custom interface like this:
public interface MyCommandProcessor {
void execute(String actionCommand);
}
public class MyButton extends JButton {
public MyButton(string actionCommand) {
setActionCommand(actionCommand);
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
Container traverser = MyButton.this;
while (traverser != null && !(traverser instanceof MyCommandProcessor))
traverser = traverser.getParent();
if (traverser != null)
((CommandListener)traverser).execute(ae.getActionCommand());
}
});
}
}
public class MyApp extends JFrame implements MyCommandListener {
public MyApp() {
JPanel panel = new Panel();
panel.add(new MyButton("MyButton got pressed"));
}
public void execute(String actionCommand) {
System.out.println(actionCommand);
}
}
You need to create custom component listener. Look here:
Create a custom event in Java
Creating Custom Listeners In Java
http://www.javaworld.com/article/2077333/core-java/mr-happy-object-teaches-custom-events.html
I do it throw the standard ActionListener
Example
public class MyPanel extends JPanel {
private final JComboBox<String> combo1;
private final JButton btn2;
.......
//catch the actions of inside components
btn2.addActionListener(new MyPanelComponentsActionListener());
........
//assign actionlistener to panel class
public void addActionListener(ActionListener l) {
listenerList.add(ActionListener.class, l);
}
public void removeActionListener(ActionListener l) {
listenerList.remove(ActionListener.class, l);
}
//handle registered listeners from components used MyPanel class
protected void fireActionPerformed(ActionEvent event) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
ActionEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ActionListener.class) {
// Lazily create the event:
if (e == null) {
String actionCommand = event.getActionCommand();
if(actionCommand == null) {
actionCommand = "FontChanged";
}
e = new ActionEvent(FontChooserPanel.this,
ActionEvent.ACTION_PERFORMED,
actionCommand,
event.getWhen(),
event.getModifiers());
}
// here registered listener executing
((ActionListener)listeners[i+1]).actionPerformed(e);
}
}
}
//!!! here your event generator
class MyPanelComponentsActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//do something usefull
//.....
fireActionPerformed(e);
}
}
....
}
I have a number of checkboxes in my Swing Project. For each checkbox select/deselect a particular query is to be executed. I know one way of getting the source of checkbox is
public void itemStateChanged(ItemEvent e) {
if(e.getSource=="checkbox object")
{
some code goes here;
}
}
If i have small number of checkboxes than this solution is best but if i have many checkboxes then i have to write lengthy code. Is there a way to find the object of checkbox that causes the event in a single command?
You can get selected checkbox by like this
JCheckBox checkBox1 = new JCheckBox("Check1");
JCheckBox checkBox2 = new JCheckBox("Check2");
checkBox1.setName("Check1");
checkBox2.setName("Check2");
ItemListener listener = new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
JCheckBox check = (JCheckBox)e.getSource();
String name = check.getName();
System.out.println(name);
}
};
checkBox1.addItemListener(listener);
checkBox2.addItemListener(listener);
If you handle checks in some verry uniform way, it may help to put JChekBoxes into HashMap, mapping them into some structure (maybe data source or some processing object) that helps to process the event easier. Amount of code can be further reduced by having a method that creates, adds and registers a checkbox. The general idea would be along the lines
HashMap<JCheckBox, String> urls = new HashMap<JCheckBox, String>();
// Here I use String but can be any complex data structure.
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String url = urls.get(e.getSource());
// Work with the selected URL now
}
};
void buildCheckBoxes() {
register("http://wikipedia.org");
register("http://stackoverflow.com");
// and 101 others, or load the list from the file.
}
void register(String url) {
JCheckBox box = new JCheckBox("Use "+url);
urls.put(box, url);
box.addActionListener(listener);
// One listener for all, defined above
myPanel.add(box);
// Some panel probably with GridLayout
}
From the other side, if your actions are very different, it may also be better to have a separate listener (probably inner or anonymous class) for each different action:
JCheckBox boxA = new JCheckBox("A");
JCheckBox boxB = new JCheckBox("B");
boxA.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Only code for boxA
}
});
boxB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Only code for boxB
}
});
As soon as there is more code in the listener, you should move it into a method on you main class.
If you want to know which component (either CheckBox or any other Component) has generated an event in java, you can assign name to that component using "setName(name)" method.
// For CheckBox
JCheckBox checkBox1 = new JCheckBox();
checkBox1.setName("CheckBox1");
// Any other Component
JButton button1 = new JButton();
button1.setName("Button1");
Now in listener class you can obtain source object who has generated current event.
// CheckBox Listener
public void itemStateChanged(ItemEvent e)
{
JCheckBox source = (JCheckBox)e.getSource();
if(source.getName().equals("CheckBox1"))
{
//some code goes here;
}
}
I want to show a small popup menu when you right-click a tab, now this is working fine but when you right click it also selects that tab which is unwanted.
So my idea was to make a new class, extend JTabbedPane and recode those mouse events. Problem is that I have no idea where to start, I was browsing its source but I can't find what part is handeling the mouseEvents.
Tabs.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
if(me.getButton()==3){
int tabNr = ((TabbedPaneUI)Tabs.getUI()).tabForCoordinate(Tabs, me.getX(), me.getY());
Component clickedTab = EventsConfig.window.MainTabs.getComponentAt(tabNr);
newMenu(clickedTab, me.getX(), me.getY());
}
}
});
Beware: dirty hack ahead! The only reason I recommend it, is that I consider the behaviour (select on right press) a bug in the BasicTabbedPaneUI's Handler.
The basic idea is to grab the listener installed by the ui, remove it, wrap into a custom listener which delegates everything except a right pressed to the original and add that to the pane:
private void installMouseListenerWrapper(JComponent tabbedPane) {
MouseListener handler = findUIMouseListener(tabbedPane);
tabbedPane.removeMouseListener(handler);
tabbedPane.addMouseListener(new MouseListenerWrapper(handler));
}
private MouseListener findUIMouseListener(JComponent tabbedPane) {
MouseListener[] listeners = tabbedPane.getMouseListeners();
for (MouseListener l : listeners) {
if (l.getClass().getName().contains("$Handler")) {
return l;
}
}
return null;
}
public static class MouseListenerWrapper implements MouseListener {
private MouseListener delegate;
public MouseListenerWrapper(MouseListener delegate) {
this.delegate = delegate;
}
#Override
public void mouseClicked(MouseEvent e) {
delegate.mouseClicked(e);
}
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) return;
delegate.mousePressed(e);
}
#Override
public void mouseReleased(MouseEvent e) {
delegate.mouseReleased(e);
}
#Override
public void mouseEntered(MouseEvent e) {
delegate.mouseEntered(e);
}
#Override
public void mouseExited(MouseEvent e) {
delegate.mouseExited(e);
}
}
then you have to add JPopupMenu (or JToolTip on MouseHoverOver ) to the JTabbedPane
A possible workaround is to set your custom tab component for each tab - see JTabbedPane#setTabComponentAt(...). Add a mouse handler to your custom tab component and redispatch left click events to the tabbedPane as described at http://www.jyloo.com/news/?pubId=1315817317000.
The custom tab component can be a simple JLabel (used for the tab title) or a container for multiple components. Depending on your requirements you can e.g. add an arrow button which will open a popup menu by left clicking the related button.
This article will helpful for removing unwanted tab selection when you click right mouse button.
Stop right click Event on JTabbedPane
I liked to add more about removing Mouse Listeners.
Try to override the method rather than removing it. It's better for future code updates.
The problem is BasicTabbedPaneUI's have inner class called Handler. That handler class override Mouse Listener.
To stop right click tab selection and show pop up menu; we need to override this method in BasicTabbedPaneUI,
protected MouseListener createMouseListener() {
return getHandler();
}
To get better look and feel we should override SynthTabbedPaneUI class.
SynthTabbedPaneUI is extends BasicTabbedPaneUI.
So our inner class is like this,
private class SynthTabbedPaneUIWrapper extends SynthTabbedPaneUI
{
private MouseAdapter menuAdapter;
private MouseAdapter getMenuAdapter()
{
if (menuAdapter == null)
{
menuAdapter =
new MouseAdapter()
{
#Override
public void mouseReleased(final MouseEvent e)
{
//implement to stop right click tab selection
//implement to show pop up menu
}
};
}
}
#Override
protected MouseListener createMouseListener()
{
return getMenuAdapter();
}
}
After that we can set our custom UI object into TabbedPane.
tabbedPane.setUI(new SynthTabbedPaneUIWrapper());
I need your suggestions and guidence on following task.
I have a frame which has two JComboBoxes supposed they are named combo1 and combo2, a JTable and other components.
At initial stage when frame is visible with above component. The combo1 combobox is filled with some values but no value is selected at initial stage, the combo2 combobox is disabled and the table is empty.
I have added an actionListener on combo1 as well as combo2. There are two types of values in combo1 suppose those values are type1 and type2.
Condition 1:
When we selects value type1 from Combo1 the actionListener method is called of combo1 which invokes a method which combo2 remains disabled and adds some rows to table related to selected value type1 from combo1.
Condition 2:
when we selects value type2 from combo1 the actionListener method is called of combo1 which invokes a method who makes combo2 filled with some values related to type2 and gets enabled but no value is selected from combo2 and table also should remain empty until we selects any value from combo2.
table at every addition of value to combo2 the action listener method of combo2 is gets fired. In actionListener method of combo2 which gets combo2 selected value but here there is no selected value of combo2 which leads to a NullPointerException.
So what should I do that the action listner method of combo2 will not be get executed after addition of an values to combo2.
You could remove the action listener before you add the new elements, and add it back once you're done . Swing is single threaded so there is no need to worry about other threads needing to fire the listener.
Your listener could probably also check if something is selected and take appropriate action if not. Better than getting a NPE.
What i do instead of adding and removing action listeners i have a boolean variable in my action listeners that is true if it has to allow the action through or false if it has to block it.
I then set it to false when i do some changes that will fire off the action listener
JComboBox test = new JComboBox();
test.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if(testActionListenerActive)
{
//runn your stuff here
}
}
});
//then when i want to update something where i want to ignore all action evetns:
testActionListenerActive = false;
//do stuff here like add
SwingUtilities.invokeLater(() -> testActionListenerActive = false);
//and now it is back enabled again
//The reason behind the invoke later is so that if any event was popped onto the awt queue
//it will not be processed and only events that where inserted after the enable
//event will get processed.
try this:
indicatorComboBox = new JComboBox() {
/**
* Do not fire if set by program.
*/
protected void fireActionEvent() {
// if the mouse made the selection -> the comboBox has focus
if(this.hasFocus())
super.fireActionEvent();
}
};
although its late, a better alternative would be to disabled the combobox to be modified prior to being modified. by doing so, you prevent firing events of the modified combobox, when for example, you use methods likes removeAllItems() or addItem()
String orderByOptions[] = {"smallest","highest","longest"};
JComboBox<String> jcomboBox_orderByOption1 = new JComboBox<String(orderByOptions);
JComboBox<String> jcomboBox_orderByOption2 = new JComboBox<String(orderByOptions);
JComboBox<String> jcomboBox_orderByOption3 = new JComboBox<String(orderByOptions);
jcomboBox_orderByOption1.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent itemEvent)
{
int eventID = itemEvent.getStateChange();
if (eventID == ItemEvent.SELECTED)
{
Object selectedItem = jcomboBox_orderByOption1.getSelectedItem();
jcomboBox_orderByOption2.setEnabled(false);
jcomboBox_orderByOption2.removeAllItems();
for (String item: string_orderByOptions)
{
if (!item.equals(selectedItem))
{
jcomboBox_orderByOption2.addItem(item);
}
}
jcomboBox_orderByOption2.setEnabled(true);
}
}
});
jcomboBox_orderByOption2.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent itemEvent)
{
int eventID = itemEvent.getStateChange();
if (eventID == ItemEvent.SELECTED)
{
Object selectedItem1 = jcomboBox_orderByOption1.getSelectedItem();
Object selectedItem2 = jcomboBox_orderByOption2.getSelectedItem();
jcomboBox_orderByOption3.setEnabled(false);
jcomboBox_orderByOption3.removeAllItems();
for (String item: string_orderByOptions)
{
if (!item.equals(selectedItem1) && !item.equals(selectedItem2))
{
jcomboBox_orderByOption3.addItem(item);
}
}
jcomboBox_orderByOption3.setEnabled(true);
}
}
});
The cleaner way is to use lambda expressions like this:
do(comboBox, () -> comboBox.setSelectedItem("Item Name"));
For the above to work, you need the following method defined somewhere:
public static void do(final JComboBox<String> component, final Runnable f) {
final ActionListener[] actionListeners = component.getActionListeners();
for (final ActionListener listener : actionListeners)
component.removeActionListener(listener);
try {
f.run();
} finally {
for (final ActionListener listener : actionListeners)
component.addActionListener(listener);
}
}
This works:
/** Implements a Combo Box with special setters to set selected item or
* index without firing action listener. */
public class MyComboBox extends JComboBox {
/** Constructs a ComboBox for the given array of items. */
public MyComboBox(String[] items) {
super(items);
}
/** Flag indicating that item was set by program. */
private boolean isSetByProgram;
/** Do not fire if set by program. */
protected void fireActionEvent() {
if (isSetByProgram)
return;
super.fireActionEvent();
}
/** Sets selected Object item without firing Action Event. */
public void setSelection(Object item) {
isSetByProgram = true;
setSelectedItem(item);
isSetByProgram = false;
}
/** Sets selected index without firing Action Event. */
public void setSelection(int index) {
isSetByProgram = true;
setSelectedIndex(index);
isSetByProgram = false;
}
}
Note: You can't just override setSelectedItem(...) or setSelectedIndex(...) because these are also used internally when items are actually selected by user keyboard or mouse actions, when you do not want to inhibit firing the listeners.
To determine whether or not to perform various methods in actionListener interface methods (actionPerformed() blocks of code) use setActionCommand() on source components (combo1 or combo2).
For your example, before adding elements to combo2, call setActionCommand("doNothing") and guard your comboBoxActionPerformed() method.
Here's a compilable example that uses this principle to have one combo set another combo's selected index while also displaying a String in a JTextField. By using setActionCommand() and guarding the comboActionPerformed() block of code, the JTextField will cycle through each word in the wordBank. If the comboActionPerformed() method was not guarded or if the actionCommand String was not changed, 2 actionEvents will trigger and the textField will skip words.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
/** #author PianoKiddo */
public class CoolCombos extends JPanel {
JComboBox<String> candyCombo;
JComboBox<String> flavorCombo;
JTextField field;
String[] wordBank;
int i = 0;
CoolCombos() {
super();
initComponents();
addComponentsToPanel();
}
private void initComponents() {
initCombos();
initTextField();
}
private void initCombos() {
ActionListener comboListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
comboActionPerformed(e);
}
};
String[] candyList = {"Sourpatch", "Skittles"};
String[] flavorList = {"Watermelon", "Original"};
candyCombo = new JComboBox<>(candyList);
candyCombo.addActionListener(comboListener);
flavorCombo = new JComboBox<>(flavorList);
flavorCombo.addActionListener(comboListener);
}
private void initTextField() {
wordBank = new String[]{"Which", "Do", "You", "Like", "Better?"};
field = new JTextField("xxxxx");
field.setEditable(false);
field.setText(wordBank[i]);
}
private void addComponentsToPanel() {
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(candyCombo);
this.add(flavorCombo);
this.add(field);
}
public void comboActionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (!command.equals("doNothing")) {
JComboBox combo = (JComboBox) e.getSource();
if (combo.equals(candyCombo)) {
setOtherComboIndex(candyCombo, flavorCombo); }
else {
setOtherComboIndex(flavorCombo, candyCombo); }
displayText(); //replace here for toDo() code
}
}
private void setOtherComboIndex(JComboBox combo, JComboBox otherCombo) {
String command = otherCombo.getActionCommand();
otherCombo.setActionCommand("doNothing"); //comment this line to skip words.
otherCombo.setSelectedIndex(combo.getSelectedIndex());
otherCombo.setActionCommand(command);
}
private void displayText() {
i++;
String word;
if (i > 4) { i = 0; }
word = wordBank[i];
field.setText(word);
this.repaint();
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("CoolCombos");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new CoolCombos();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setMinimumSize(frame.getSize());
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I kind of went the stupid simple route with this issue for my program since I am new to programming.
I changed the action listeners to have a counter if statement:
if(stopActionlistenersFromFiringOnLoad != 0){//action performed ;}
Then at the end of the java program creation, I added 1 to the counter:
topActionlistenersFromFiringOnLoad += 1;
To avoid that addItem method fire events is better to use an DefaultComboBoxModel in the JComboBox to add data. Also, if you invoke a model.addElement(), an event is fired, so, you can add all the elements to the model and later use JComboBox.setModel(model). In this way, if you add elements to the model, events are not fired because you have not link the JComboBox with the model. Then, I show you an example.
private void rellenarArrendatarioComboBox(ArrayList<Arrendatario> arrendatarios) {
DefaultComboBoxModel model = new DefaultComboBoxModel();
model.addElement(new Arrendatario(" -- Seleccione un arrendatario --"));
for (Arrendatario arrendatario : arrendatarios) {
model.addElement(arrendatario);
}
ArrendatarioComboBox.setModel(model);
}
First, we create the model, add all elements to the model (events are not fired because you have not link the JComboBox with the model), we link the model with the JComboBox using ArrendatarioComboBox.setModel(model). After linking, events are fired.