Editable JCombobox avoid multiple DocumentEvents when the selection change from the popup - java

I have an editable JComboBox with a single listener on it.
It is a documentListener that execute some code when the user insert or remove some text inside the combobox textfield:
((JTextComponent)combobox.getEditor().getEditorComponent()).getDocument().addDocumentListener(..)
My problem is that when the user select an element from the popup and the content of the combobox textfield changes there are two events executed into the documentListener, one is a removeUpdate() corresponding to the deletion of the previous content and the other is a insertUpdate() corresponding to the insertion of the new value.
I want that only one execution of my code is done and not two. How can I avoid that the code is executed two times when the user select an entry from the popup?
I tried various combination of different listener but for now without result.
What I want in the end is that my code is execute only one time when:
- The user change the text into the combobox textfield.
- The user select an element from the combobox popup
Thanks in advance.
[EDIT 1]
As requested I updated adding SSCCE
myCombobox = new javax.swing.JComboBox<String>();
myCombobox.setEditable(true);
((JTextComponent)myCombobox.getEditor().getEditorComponent()).getDocument().addDocumentListener(
new DocumentListener(){
#Override
public void insertUpdate(DocumentEvent e) {
System.out.println("insert performed");
}
#Override
public void removeUpdate(DocumentEvent e) {
System.out.println("remove performed");
}
#Override
public void changedUpdate(DocumentEvent e) {
System.out.println("change performed");
}
});
myCombobox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
System.out.println("Action performed");
}
}
});
Note that in this case I have an ItemEvent instead of an ActionEvent because I'm continuing to modify my code searching for a solution in any case the behavior should not be influenced by this.

You can check ((JTextComponent)combobox.getEditor().getEditorComponent()).hasFocus() to be sure user types in the editor.

Related

Listener on JTextField to detect completion of data entry

We are developing an application in Java Swing.
We have a text field and a combo box. The combo box values should be populated from DB based on the value entered in the text field.
The text field length is 4 characters. So, user can enter any value between 1 to 9999.
Which listener would be used to identify that the user has completed their entry in the text field so that I can populate the combo box?
..the user has completed his entry in the text field so that i can populate the combobox.
An ActionListener. When the user is done, they hit the enter key and an action event will be fired. But..
..user can enter any value between 1 to 9999.
This sounds better suited to a JSpinner using a SpinnerNumberModel and a ChangeListener to register selecting a different number.
Use the textField.getDocument().addDocumentListener(new DocumentListener()
textField.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
//TODO
}
public void removeUpdate(DocumentEvent e) {
//TODO
}
public void insertUpdate(DocumentEvent e) {
//TODO
}

Event handling in Java (JTree + JButton)

private void createEvents()
{
menuFileExit.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
System.exit(0);
}
});
////// Events on tree selection
jtStoryViewer.addTreeSelectionListener(new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent arg0)
{
DefaultMutableTreeNode selection = (DefaultMutableTreeNode) jtStoryViewer.getLastSelectedPathComponent();
Object nodeObject = selection.getUserObject();
////// Checks if selected node is a String (only story title is a string)
if(selection.getUserObject().getClass().getName() == "java.lang.String" )
{
tfTitle.setText(nodeObject.toString());
////// Action listener for Change Button
btnChange.addActionListener(new ActionListener()
{
////// Title text swap
public void actionPerformed(ActionEvent arg0)
{
selection.setUserObject(tfTitle.getText());
((DefaultTreeModel)jtStoryViewer.getModel()).nodeChanged(selection);
}
});
}
///// checks if the object is a chapter object
if(selection.getUserObject().getClass().getName() == "ISW.common.Chapter")
{
Chapter chapter = (Chapter) selection.getUserObject();
tfTitle.setText(chapter.toString());
////// Action listener for Change Button
btnChange.addActionListener(new ActionListener()
{
////// Title text swap
public void actionPerformed(ActionEvent arg0)
{
chapter.setTitle(tfTitle.getText());
((DefaultTreeModel)jtStoryViewer.getModel()).nodeChanged(selection);
}
});
}
}
});
}
I am using JTree to display and modify some objects. I added a TreeSelectionListener to get the object data on selection. For now I want to be able to change the title of an object, it works fine on first selection on the tree , I change the value in the text box and the "Change" button works just fine, but when I move on to next objects, the change button also modifies the value of all previously selected objects.
I guess it is caused due to my improper usage of the ActionListeners but I can't tell for sure and at this point I'm stuck.
Will be grateful for any hints.
Don't keep adding an ActionListener to the btnChange JButton within the TreeSelectionListener#valueChanged method.
This will cause the button to call EVERY ActionListener you have previously
Instead, give the btnChange a single ActionListener, when clicked, can act on the currently selected node (by checking the JTree it self). You could have the TreeSelectionListener#valueChanged method enable or disable the btnChange based on the validity of the selection
Also, if(selection.getUserObject().getClass().getName() == "ISW.common.Chapter") isn't how String comparison is done in Java, instead you should use something more like if("ISW.common.Chapter".equals(selection.getUserObject().getClass().getName()))

Editable JComboBox with dynamic entries

I made my JComboBox editable using:
myCombo.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
I encounter multible problems with the following task:
When writing into the combobox, the new content of the box should be taken and compared with a list and all entries starting with this text should be shown in the popupmenu.
So if i have a list with: "Aban" "Aben" "Aber" "Acen" "Aden"
and enter "Ab" into the box, then the pop-up should display the first 3 entries.
When clicking one of the entries (Either by keyboard selecting and pressing enter/tab or by clicking with a mouse) The ComboBox should get that value and the Popup should hide. I need to find this action as some of the elements have a note at the end (In backets which I require) but only when one of the entries is selected
Here are the most imporant parts of my code:
final JTextComponent tcA = (JTextComponent) myCombo.getEditor().getEditorComponent();
tcA.getDocument().addDocumentListener(new DocumentListener() {
public void methodUsedByinsertUpdateAndremoveUpdate(DocumentEvent e) {
String item = ((JTextComponent) myCombo.getEditor().getEditorComponent()).getText();
//Routine to get the new list in a vector, not pasted for readability
DefaultComboBoxModel newMyComboModel = new DefaultComboBoxModel(myVectorList);
myCombo.setModel(newMyComboModel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myCombo.showPopup();
}
});
}
}
myCombo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(myCombo.getModel().getSize() == 1) {
//Special logic to find out if the selected item has a note
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myCombo.hidePopup();
}
});
}
}
With this, i have:
Trouble with the first character (Caret position not working correctly)
Popup not automatically shown and hides when entering new character into the field
Problems with Swing GUI not being actualised
If you require more information just ask
The Glazed List recommended by peeskillet did exactly what I wanted

JComboBox itemStateChanged event called twice at a time

resultCombo = new JComboBox();
resultCombo.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent ie) {
sampleText=resultCombo.getSelectedItem().toString();
System.out.println("SampleText : "+sampleText);
}
});
output:
SampleText : selectedword
SampleText : selectedword
Why this event is called twice when selecting item in combobox.?
JComoboBox ItemListener does get called twice for a single change. Once for SELECTED event and once for DESELECTED event.
See this tutorial page on how to write an ItemListener.
Basically what you have to do is
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
//Do any operations you need to do when an item is selected.
} else if(e.getStateChange() == ItemEvent.DESELECTED){
//Do any operations you need to do when an item is de-selected.
}
}
Based on the documentation of ItemListener
Invoked when an item has been selected or deselected by the user. The
code written for this method performs the operations that need to
occur when an item is selected (or deselected).
This suggested that you will receive an event when an item is deselected or selected. Seen as a change in the selected item in the combo box requires that the currently selected item has to be deselected first, it stands to reason that you will receive a ItemEvent.DESELECTED and ItemEvent.SELECTED event

Why is itemStateChanged on JComboBox is called twice when changed?

I'm using a JComboBox with an ItemListener on it. When the value is changed, the itemStateChanged event is called twice. The first call, the ItemEvent is showing the original item selected. On the second time, it is showing the item that has been just selected by the user. Here's some tester code:
public Tester(){
JComboBox box = new JComboBox();
box.addItem("One");
box.addItem("Two");
box.addItem("Three");
box.addItem("Four");
box.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
System.out.println(e.getItem());
}
});
JFrame frame = new JFrame();
frame.getContentPane().add(box);
frame.pack();
frame.setVisible(true);
}
So when I changed the Combo box once from "One" to "Three" the console shows:
One
Three
Is there a way I can tell using the ItemEvent maybe, that it's the second item (ie. the user selected item)? And if someone can explain why it gets called twice, that would be nice too!
Thanks
Have a look at this source:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Tester {
public Tester(){
JComboBox box = new JComboBox();
box.addItem("One");
box.addItem("Two");
box.addItem("Three");
box.addItem("Four");
box.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
System.out.println(e.getItem() + " " + e.getStateChange() );
}
});
JFrame frame = new JFrame();
frame.getContentPane().add(box);
frame.pack();
frame.setVisible(true);
}
public static void main(String [] args) {
Tester tester = new Tester();
}
}
Use the getStateChange to determine if an item is selected or deselected
According to this thread,
It gets tripped when you leave one result and then called again when set to another result
Don't listen for itemStateChanged. Use an ActionListener instead, which is good for handling events of the combo.
You need a ItemStateListener if you need to separately handle deselection / selection depending on the item involved.
Changing the state of the item within itemStateChanged causes itemStateChanged to be fired... this called "reentrance".
I wanted to get the index string after selected and set in combobox
comboBox1.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() == ItemEvent.SELECTED) {
comboBox1ItemStateChanged();
}
}
});
Yo can do it like this:
import java.awt.event.*;
jComboBox1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello");
}
});
private void dropDown_nameItemStateChanged(java.awt.event.ItemEvent evt) {
if(evt.getStateChange() == ItemEvent.SELECTED)
{
String item = (String) evt.getItem();
System.out.println(item);
}
}
Good Luck!
The code is:
public class Tester {
private JComboBox box;
public Tester() {
box = new JComboBox();
box.addItem("One");
box.addItem("Two");
box.addItem("Three");
box.addItem("Four");
box.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == 1) {
JOptionPane.showMessageDialog(box, e.getItem());
System.out.println(e.getItem());
}
}
});
JFrame frame = new JFrame();
frame.getContentPane().add(box);
frame.pack();
frame.setVisible(true);
}
}
Have a look here,
box.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
if(e.getStateChange()== ItemEvent.SELECTED) {
//this will trigger once only when actually the state is changed
JOptionPane.showMessageDialog(null, "Changed");
}
}
});
When you select a new option, it will only once call the JOptionPane, indicating that the code there will be called once only.
Quote from Java Tutorial:
"Only one item at a time can be selected in a combo box, so when the user makes a new selection the previously selected item becomes unselected. Thus two item events are fired each time the user selects a different item from the menu. If the user chooses the same item, no item events are fired."
When the anyitem is selected from the combo box, it internally triggers selection change, i.e. it will call the function setSelectedItem.
If an explicit itemStateChanged event listener is implemented, the setSelectedItem will call itemStateChanged. So, when an item is selected it calls setSelectedItem then it calls itemStateChanged.
As the value of the combo box changes, even that too triggers itemStateChanged and hence itemStateChanged gets called.
I had written listener for item change to handle change in value of combo box when set internally from the code and that caused the function getting called twice.
Here are the 2 back traces, which gets invoked when a value is selected from combo box.
1st time on actual value change:
dataMgr.MainInterface.jComboBoxPaymentStatusValueChangeHandle(MainInterface.java:1431),
dataMgr.MainInterface.jComboBoxPaymentStatusItemStateChanged(MainInterface.java:1676),
dataMgr.MainInterface.access$600(MainInterface.java:28),
dataMgr.MainInterface$7.itemStateChanged(MainInterface.java:437),
javax.swing.JComboBox.fireItemStateChanged(JComboBox.java:1223),
javax.swing.JComboBox.selectedItemChanged(JComboBox.java:1271),
javax.swing.JComboBox.contentsChanged(JComboBox.java:1330),
javax.swing.AbstractListModel.fireContentsChanged(AbstractListModel.java:118),
javax.swing.DefaultComboBoxModel.setSelectedItem(DefaultComboBoxModel.java:93),
javax.swing.JComboBox.setSelectedItem(JComboBox.java:576), javax.swing.JComboBox.setSelectedIndex(JComboBox.java:622), javax.swing.plaf.basic.BasicComboPopup$Handler.mouseReleased(BasicComboPopup.java:852), java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:290), java.awt.Component.processMouseEvent(Component.java:6533), javax.swing.JComponent.processMouseEvent(JComponent.java:3324), javax.swing.plaf.basic.BasicComboPopup$1.processMouseEvent(BasicComboPopup.java:501), java.awt.Component.processEvent(Component.java:6298), java.awt.Container.processEvent(Container.java:2236), java.awt.Component.dispatchEventImpl(Component.java:4889), java.awt.Container.dispatchEventImpl(Container.java:2294), java.awt.Component.dispatchEvent(Component.java:4711), java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888), java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525), java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466), java.awt.Container.dispatchEventImpl(Container.java:2280), java.awt.Window.dispatchEventImpl(Window.java:2746), java.awt.Component.dispatchEvent(Component.java:4711), java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758), java.awt.EventQueue.access$500(EventQueue.java:97), java.awt.EventQueue$3.run(EventQueue.java:709), java.awt.EventQueue$3.run(EventQueue.java:703), java.security.AccessController.doPrivileged(Native Method), java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76), java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86), java.awt.EventQueue$4.run(EventQueue.java:731), java.awt.EventQueue$4.run(EventQueue.java:729), java.security.AccessController.doPrivileged(Native Method), java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76), java.awt.EventQueue.dispatchEvent(EventQueue.java:728), java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201), java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116), java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105), java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101), java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93), java.awt.EventDispatchThread.run(EventDispatchThread.java:82)]
2nd time from the due to operation on combobox
dataMgr.MainInterface.jComboBoxPaymentStatusValueChangeHandle(MainInterface.java:1431),
dataMgr.MainInterface.jComboBoxPaymentStatusItemStateChanged(MainInterface.java:1676),
dataMgr.MainInterface.access$600(MainInterface.java:28),
dataMgr.MainInterface$7.itemStateChanged(MainInterface.java:437),
javax.swing.JComboBox.fireItemStateChanged(JComboBox.java:1223),
javax.swing.JComboBox.selectedItemChanged(JComboBox.java:1280),
javax.swing.JComboBox.contentsChanged(JComboBox.java:1330),
javax.swing.AbstractListModel.fireContentsChanged(AbstractListModel.java:118),
javax.swing.DefaultComboBoxModel.setSelectedItem(DefaultComboBoxModel.java:93),
javax.swing.JComboBox.setSelectedItem(JComboBox.java:576),
javax.swing.JComboBox.setSelectedIndex(JComboBox.java:622),
javax.swing.plaf.basic.BasicComboPopup$Handler.mouseReleased(BasicComboPopup.java:852),
java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:290),
java.awt.Component.processMouseEvent(Component.java:6533),
javax.swing.JComponent.processMouseEvent(JComponent.java:3324),
javax.swing.plaf.basic.BasicComboPopup$1.processMouseEvent(BasicComboPopup.java:501),
java.awt.Component.processEvent(Component.java:6298), java.awt.Container.processEvent(Container.java:2236),
java.awt.Component.dispatchEventImpl(Component.java:4889), java.awt.Container.dispatchEventImpl(Container.java:2294),
java.awt.Component.dispatchEvent(Component.java:4711), java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888),
java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525), java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466),
java.awt.Container.dispatchEventImpl(Container.java:2280), java.awt.Window.dispatchEventImpl(Window.java:2746),
java.awt.Component.dispatchEvent(Component.java:4711), java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758),
java.awt.EventQueue.access$500(EventQueue.java:97), java.awt.EventQueue$3.run(EventQueue.java:709),
java.awt.EventQueue$3.run(EventQueue.java:703), java.security.AccessController.doPrivileged(Native Method),
java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76),
java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86),
java.awt.EventQueue$4.run(EventQueue.java:731), java.awt.EventQueue$4.run(EventQueue.java:729),
java.security.AccessController.doPrivileged(Native Method), java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76), java.awt.EventQueue.dispatchEvent(EventQueue.java:728), java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201), java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116), java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105), java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101), java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93), java.awt.EventDispatchThread.run(EventDispatchThread.java:82)]
JComboBox.setFocusable(false) will do the trick.

Categories