I have a JList and want to change the tooltips, depending on the entry the mouse hovers over. I tried searching my problem on google, but had no success.
Basically i need to get the object i am currently hovering over.
Every help is appreciated
In order to do that, you have to extend JList and expose the tooltip text method. Here is an example program I found sometime ago using Google:
import java.awt.EventQueue;
import java.awt.event.*;
import javax.swing.*;
// Custom class to extend our JList and expose tooltip functionality.
class MyList extends JList {
public MyList() {
super();
// Attach a mouse motion adapter to let us know the mouse is over an item and to show the tip.
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
MyList theList = (MyList) e.getSource();
ListModel model = theList.getModel();
int index = theList.locationToIndex(e.getPoint());
if (index > -1) {
theList.setToolTipText(null);
String text = (String) model.getElementAt(index);
theList.setToolTipText(text);
}
}
});
}
// Expose the getToolTipText event of our JList
public String getToolTipText(MouseEvent e) {
return super.getToolTipText();
}
}
public class TestJList extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TestJList myTest = new TestJList();
myTest.setTitle("Example JList");
myTest.setSize(300, 300);
myTest.setDefaultCloseOperation(EXIT_ON_CLOSE);
MyList list = new MyList();
// Create our model and add some items.
DefaultListModel model = new DefaultListModel();
model.addElement("one");
model.addElement("two");
model.addElement("three");
model.addElement("four");
// Set the model for our list
list.setModel(model);
ToolTipManager.sharedInstance().registerComponent(list);
// Add our custom list and show the form.
MyTest.add(list);
MyTest.setVisible(true);
}
});
}
}
Hope this helps.
1) Assign a MouseListener to all elements in question.
2) Implement the mouseEntered method
3) Inside that method, use event.getSource() to get the element that was hovered.
Related
I have a main frame which has an array list containing a list of items for an order. I then have a button which creates a new window that has a form allowing the user to pick multiple options for an item, this information is then put into an object.
I want to return this object back to the original frame so that I can add it to the order array list. However I'm not sure how to go about this as my frames have their code auto generated as I'm using netbeans.
You should use a listener (interface) system. Create an interface and implement it in your main frame, when you create your second frame you pass the first one as parameter. By this way you will be able to call a method, let's say, onItemSelected in the second frame whenever you want in an elegant way.
Using interfaces is more convenient, you can have multiple listener.
There is an example:
class MyFrame extends JFrame implements ItemSelectedListener {
void onButtonClick() {
new SecondFrame(this);
}
#Override
public void onItemSelected(List<String> items) {
// do your stuff with the selected items here
}
}
interface ItemSelectedListener {
void onItemSelected(List<String> items);
}
class SecondFrame extends JFrame {
private ItemSelectedListener itemSelectedListener;
private JTextField name;
private JButton buttonOk;
SecondFrame(ItemSelectedListener listener) {
itemSelectedListener = listener;
name = new JTextField();
buttonOk = new JButton("OK");
getContentPane().add(name);
getContentPane().add(buttonOk);
buttonOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<String> myFormItems = new ArrayList<>();
// fulfill your list with all informations that you need
myFormItems.add(name.getText());
// notify your main frame that the user finished to complete the form
itemSelectedListener.onItemSelected(myFormItems);
}
});
}
}
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Within actionPerformed/ItemStateChange");
}
});
System.out.println("Added items to combo");
for (int i = 0; i < 5; i++) {
combo.addItem(i);
}
Calling an addItem calls these methods internally
1) addElement
2)fireIntervalAdded
3)((ListDataListener)listeners[i+1]).intervalAdded(e);
4)intervalAdded calls ->SelectedItemChanged()
5) and finally stateChange is done.
So my question is while adding every item in combobox , Listener should be invoked. But listener is invoked only once. In internal implementation , they have used ListDataListener. How this listener knows that all elemnts have been added or "x" element is the last element...???
Code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
public class test {
private static JComboBox combo;
public static void main(String[] args) {
combo = new JComboBox<>();
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Within actionPerformed/ItemStateChange");
}
});
System.out.println("Added items to combo");
for (int i = 0; i < 5; i++) {
combo.addItem(i);
}
System.out.println("Removed Items from Combo");
invokeRemove();
}
private static void invokeRemove() {
combo.removeAllItems();
}
}
For me the behaviour is correct. Adding something in the combobox does not change selection. So first time you added element selection becomes from nothing to the first element (0). After that you add multiple elements but 0 remains selected. And action is not triggered.
After removing all elements selection moves from 0 to nothing and the action is invoked.
Thanks for sharing your idea..I just now found the part where ItemState Change occurs when addItem is added.
JCombo.class
protected void selectedItemChanged() {
if (selectedItemReminder != null ) {
fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
selectedItemReminder,
ItemEvent.DESELECTED));
}
// set the new selected item.
selectedItemReminder = dataModel.getSelectedItem();
if (selectedItemReminder != null ) {
fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
selectedItemReminder,
ItemEvent.SELECTED));
}
}
They have used ListDataListener . Adding an ActionListener to the jcombo adds an ActionListener to the ListDatalistener's list. Whenever there occurs an item state change , listeners that are registered to ListDataListener gets notified. So i get the sysout in addActionListener.
This method is called in JCOMBOX constructor. This registers addActionListener to the ListDataListener.And Notification comes when item state changes.
public void setModel(ComboBoxModel<E> aModel) {
ComboBoxModel<E> oldModel = dataModel;
if (oldModel != null) {
oldModel.removeListDataListener(this);
}
dataModel = aModel;
dataModel.addListDataListener(this);
// set the current selected item.
selectedItemReminder = dataModel.getSelectedItem();
firePropertyChange( "model", oldModel, dataModel);
}
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.
I have a JList connected to a collection.
When collection change, I need the JList to be automatically refreshed. But I can see that my JList will refresh only after I minimized (or maximized) the JFrame.
Why?
I'm new with Java and I'm trying to learn.
I have this collection for the list model:
public class UserCollection extends Vector<User> implements ListModel{
private static final long serialVersionUID = 2668410577023194442L;
#Override
public void addListDataListener(ListDataListener arg0) {
System.out.println("add list! --- " +arg0.toString());
}
#Override
public Object getElementAt(int index) {
return this.get(index).getName();
}
#Override
public int getSize() {
return(this.elementCount);
}
#Override
public void removeListDataListener(ListDataListener arg0) {}
}
In another class, I populate the collection with userList.add(u1); or userList.remove(u1);
In the JFrame class I have:
JList list = new JList();
list.setModel(xmppManager.userList);
I can see on console the collection changing, and if I minimize the JFrame and/or maximize, the JList is refreshed properly...
If you can, I suggest you use a DefaultListModel as your JList's model. This model will automate the change of the view (the JList) as the model changes and will make your life much easier. If you absolutely must use a collection of your own making, then see if you can have the class that holds it extend an AbstractListModel. If you do this, be sure to call the appropriate fireXXXX() method whenever you change data in the model.
Once your collection has changed, call a refresh on your JList:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jlist.revalidate(); // triggers a repaint of all the items in the JList.
jlistContainer.repaint(); // Not sure if this one is needed
}
});
How your JList model is updated?
[EDIT] Now that we have your code, you should look at AbstractListModel and implements your model in the same way, or better, extends AbstractListModel.
Actually, you add data to your collection but the model is not notified of this change!
When you added or removed items using the add and remove methods, you were calling methods in Vector. This updated your model but the actual JList had no way to know that the model was updated and that it needed to refresh itself.
(I think others have adequately explained how to fix the problem)
Finally I have resolved this thanks to your help!
This is what I did:
1) In the class doing the work I have
public DefaultListModel userList;
and I populate it with userList.addElement(user) and userList.removeElement(user)
2) in the JFrame class, I have:
JList list = new JList();
list.setCellRenderer(new UsersRenderer());
list.setModel(xmppManager.userList);
3) and this is my UsersRenderer:
import java.awt.Color;
import java.awt.Component;
import javax.swing.*;
public class UsersRenderer extends javax.swing.JPanel implements ListCellRenderer {
/**
*
*/
private static final long serialVersionUID = -9210143012283239644L;
public UsersRenderer() {
initComponents();
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
User person = (User) value;
labelUser.setText(person.getName() + " " + person.getAddress());
if (isSelected) {
setBackground(Color.red);
setForeground(Color.white);
} else {
setBackground(Color.white);
setForeground(Color.black);
}
return this;
}
private void initComponents() {
labelUser = new javax.swing.JLabel();
labelUser.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
labelUser.setText("testo");
add(labelUser);
}
private javax.swing.JLabel labelUser;
}
I'm trying to display a list of items and, when the user clicks on an item, to clear the list and display another list.
If I run this and click on the first entry on the displayed list, the program dies with a long trail of runtime exceptions. If I remove the clear() line (commented below), it runs fine. Adding try/catch didn't reveal any information useful to me. Apologies for the long code, but I couldn't figure out how to shorten and still generate the errors.
What at I doing wrong?
import java.util.*;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class ListGui extends JPanel implements ListSelectionListener {
private static JList list;
private static DefaultListModel listModel = new DefaultListModel();
public ListGui() {
super(new BorderLayout());
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addListSelectionListener(this);
JScrollPane listScrollPane = new JScrollPane(list);
add(listScrollPane, BorderLayout.CENTER);
}
public static void Populate(List<String> lines) {
listModel.clear();
for(String line : lines) {
listModel.addElement(line);
}
}
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
List<String> out = new ArrayList<String>();
out.add("three");
out.add("four");
Populate(out);
}
}
}
public class TestClear {
static JComponent newContentPane = new ListGui();
private static void createAndShowGUI() {
JFrame frame = new JFrame("toast");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
List<String> out = new ArrayList<String>();
createAndShowGUI();
out.add("one");
out.add("two");
ListGui.Populate(out);
}
}
I'm trying to display a list of items and, when the user clicks on an item, to clear the list and display another list.
That doesn't sound like the best design to me. The selection will change whenever you click on an item or when you use the arrow keys to move up or down the list. I'm sure for users that like to use the keyboard you don't want the list to change every time you use an arrow key.
The normal design would be to invoke an Action on the list on a "double click" or when the user users "Enter" from the keboard. This is easily implemente using the List Action concept.
However, if you really do want to update the list on every selection then I would use code like:
list.removeListSelectionListener( this );
populate(...);
list.addListSelectionListener(this);
The problem is that you are calling Populate() in valueChanged() which triggers valueChanged() and hence the stackoverflow.
The simplest solution is to have a flag to prevent reentry.
boolean busy = false;
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false && !busy) {
busy = true;
List<String> out = new ArrayList<String>();
out.add("three");
out.add("four");
Populate(out);
busy = false;
}
}
If your code could be accessed by multiple threads, you should be looking into ReentrantLock