2

Using Java Swing I have 20 JLabels. Each JLabel has a MouseListener and a KeyListener. I'm been trying to come up with a way (with no luck) to be able to know which label the mouse has entered/hovering over and when the delete key is pressed.

For instance, when the delete key is pressed and the mouse is in JLabel 5. I want an action on JLabel 5 to be performed.

I know how to use MouseListener and KeyListener independently, but I don't know how to use them together like that.

Here is what I'm attempting to do.

    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_DELETE){
            //Get the JLabel that the mouse has entered/hovering over
            //Perform action on that JLabel

        }
    }

In case it matters, I'm using a List for all of the JLabel's.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
Eric
  • 2,008
  • 3
  • 18
  • 31

2 Answers2

6

Suggestions that might surprise you:

  • Surprise number 1: Don't use a MouseListener
  • Surprise number 2: Don't use a MouseMotionListener.
  • Surprise number 3: Don't use KeyListeners.
  • Instead use Key Bindings (tutorial here: Key Bindings Tutorial). I suggest that you bind the delete key press on the JPanel that holds your grid of JLabel. Then when the delete key has been pressed, find out which JLabel the cursor is over (if any) by using the MouseInfo class to get a PointerInfo instance, and with that get the location of the cursor on the screen. With that information and some trivial math, it should be easy for you to figure out which label has the cursor (again, if any), and do your appropriate action. Note that the Key Bindings tutorial will tell you why its better to use these rather than KeyListeners, but most importantly, you don't have to futz so much with the application's focus.

Edit For example:

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

@SuppressWarnings("serial")
public class BindingExample extends JPanel {
   private static final int ROWS = 10;
   private static final int COLS = 8;
   JLabel[][] labels = new JLabel[ROWS][COLS];

   public BindingExample() {
      setLayout(new GridLayout(ROWS, COLS));
      for (int r = 0; r < labels.length; r++) {
         for (int c = 0; c < labels[r].length; c++) {
            String labelText = String.format("[%d,  %d]", r, c);
            labels[r][c] = new JLabel(labelText, SwingConstants.CENTER);
            int eb = 4;
            labels[r][c].setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
            add(labels[r][c]);
         }
      }

      int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
      InputMap inputMap = getInputMap(condition);
      ActionMap actionMap = getActionMap();

      KeyStroke delKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
      String delete = "delete";

      inputMap.put(delKeyStroke, delete);
      actionMap.put(delete, new DeleteAction());
   }

   private class DeleteAction extends AbstractAction {
      @Override
      public void actionPerformed(ActionEvent evt) {
         PointerInfo pInfo = MouseInfo.getPointerInfo();
         Point ptOnScrn = pInfo.getLocation();
         int xPanel = getLocationOnScreen().x;
         int yPanel = getLocationOnScreen().y;
         int x = ptOnScrn.x - xPanel;
         int y = ptOnScrn.y - yPanel;

         Component component = getComponentAt(x, y);
         if (component != null) {
            JLabel selectedLabel = (JLabel) component;
            System.out.println("Selected Label: " + selectedLabel.getText());
         }
      }
   }

   private static void createAndShowGui() {
      BindingExample mainPanel = new BindingExample();

      JFrame frame = new JFrame("Key Bindings Example");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Edit 2
Shoot, just saw the comments to your question, and yeah, a JList would probably be much better for your purposes.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • I have 4 panels that contain 5 JLabels. This works if I have 1 panel, but I can't seem to get it to work with 4 panels. The JLabels are created by a custom class that extends JLabel. – Eric Jul 29 '14 at 19:50
  • @eric: create a new question, create and post in this question your [minimal example program](http://stackoverflow.com/help/mcve) similar to what I've posted above, code we can compile, run and test (please read the link). – Hovercraft Full Of Eels Jul 29 '14 at 19:53
4

In the top of your class:

private JLabel hoveredLabel;

When adding a JLabel:

final JLabel label = ...

label.addMouseMotionListener(new MouseListener() {
    ...

    public void mouseEntered(MouseEvent me) {
        hoveredLabel = (JLabel)me.getSource();
    }

    public void mouseExited(MouseEvent me) {
        hoveredLabel = null;
    }
}

And your KeyListener:

public void keyPressed(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_DELETE){
        //Get the JLabel that the mouse has entered/hovering over
        //Perform action on that JLabel
        if (hoveredLabel != null)
            doSomethingWith(hoveredLabel);
    }
}

Make sure you add the KeyListener to the contentPane of the JFrame.

David Xu
  • 5,555
  • 3
  • 28
  • 50
  • I suggest adding `if(hoveredLabel != label)` in your `mouseEntered` method – Vince Jul 27 '14 at 23:56
  • That won't work as he wants to add 20 labels. You won't know which is which if the keyListener is put onto the contentPane – David Xu Jul 27 '14 at 23:57
  • Well, this is assuming you aren't going to manually create all 20 `MouseListener`s. Maybe have a method, `createLabel(String title)` which creates a new label each time, separating each object reference, allowing you to easily add the condition in there. `JLabel label = createLabel(String);` – Vince Jul 28 '14 at 00:02
  • 1
    +1 but... 1) It's `MouseListener` not `MouseMotionListener` 2) Impement just one `MouseListener` and replace `mouseEntered()` code by `hoveredLabel = (JLabel)me.getSource();` 3) You need `mouseExited()` as well. It should be `hoveredLabel = null;` 4) See [Key bindings vs. key listeners](http://stackoverflow.com/questions/15290035/key-bindings-vs-key-listeners-in-java/15290065#15290065) – dic19 Jul 28 '14 at 00:25
  • Good suggestions, I'll amend – David Xu Jul 28 '14 at 00:28
  • 2
    `KeyListener` is a poor choice, full stop, it suffers from too many issues. The `contentPane` is unlikely to be focusable, so the `KeyListener` is unlikely to work – MadProgrammer Jul 28 '14 at 00:35