使用Keybind和MouseInfo获取Label

时间:2014-07-29 21:06:28

标签: java swing jpanel jlabel key-bindings

这是我之前question.

的后续问题

我有两个JPanels,每个JLabelsJLabels。当我将鼠标移动到其中一个import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; 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 KeybindExample extends JPanel { public KeybindExample() { setLayout(new BorderLayout()); add(firstRow(), BorderLayout.NORTH); add(secondRow(),BorderLayout.SOUTH); 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 JPanel firstRow(){ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); String [] x = {"1","2","3","4","5"}; for(int i = 0; i < 5; i++){ JLabel label = new JLabel(x[i]); label.setPreferredSize(new Dimension(50,50)); panel.add(label); } return panel; } private static JPanel secondRow(){ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); String [] x = {"6","7","8","9","10"}; for(int i = 0; i < 5; i++){ JLabel label = new JLabel(x[i]); label.setPreferredSize(new Dimension(50,50)); panel.add(label); } return panel; } private static void createAndShowGui() { KeybindExample mainPanel = new KeybindExample(); 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(); } }); } } 并按下删除键时,我想打印标签的文本。这是我尝试过的:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: javax.swing.JPanel cannot be cast to javax.swing.JLabel

我收到以下错误:

JPanel

如果我理解正确,我的JPanels(KeybindExample)包含两个JPanels,这两个JLabels中的每一个都包含JPanel。因此,当我执行getComponentAt(x,y)时,我得到的组件是另一个JLabel,这导致了类型转换的问题。

我认为我需要做的是获得Component At(x,y)的组件,它是 Component component = getComponentAt(x, y); if (component != null) { JPanel selectPanel = (JPanel) component; Component comp = selectPanel.getComponentAt(x,y); if(comp != null){ JLabel selectLabel = (JLabel) comp; System.out.println("Selected Label: " + selectLabel.getText()); } } } 。换句话说,我需要更进一步。我尝试了以下方法,但它只适用于顶部面板,有时仍会返回相同的错误。

{{1}}

}

我如何拥有多个包含多个JLabel的JPanel,但是能够拥有鼠标悬停在文本上的JLabel。

2 个答案:

答案 0 :(得分:3)

关键问题是 - 如何在某个时间点将引用送到所需的JLabel,这时按下删除键。您当前的解决方案(我之前提出的解决方案)无法正常工作,因为代码会找到相对于KeybindExample JPanel的当前组件,因为这是您调用getComponentAt()方法的组件。一种可能的解决方案是在实际持有JLabel的JPanel上调用getComponentAt()。另一种可能的解决方案是计算相对于每个JLabel的鼠标位置,并使用contains(...)方法查看鼠标是否超过其中一个JLabel。要做到这一点,您需要访问所有JLabel,可能需要将它们放在List<JLabel>中。例如......

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class KeybindExample extends JPanel {
   private List<JLabel> labelList = new ArrayList<>();

   public KeybindExample() {
      setLayout(new BorderLayout());
      add(firstRow(), BorderLayout.NORTH);
      add(secondRow(), BorderLayout.SOUTH);
      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();

         for (JLabel label : labelList) {
            int x = ptOnScrn.x - label.getLocationOnScreen().x;
            int y = ptOnScrn.y - label.getLocationOnScreen().y;

            if (label.contains(x, y)) {
               System.out.println("Selected Label: " + label.getText());
            }

         }
         // 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 JPanel firstRow() {
      JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
      String[] x = { "1", "2", "3", "4", "5" };
      for (int i = 0; i < 5; i++) {
         JLabel label = new JLabel(x[i]);
         labelList.add(label);
         label.setPreferredSize(new Dimension(50, 50));
         panel.add(label);
      }

      return panel;
   }

   private JPanel secondRow() {
      JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
      String[] x = { "6", "7", "8", "9", "10" };
      for (int i = 0; i < 5; i++) {
         JLabel label = new JLabel(x[i]);
         labelList.add(label);
         label.setPreferredSize(new Dimension(50, 50));
         panel.add(label);
      }

      return panel;
   }

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

      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();
         }
      });
   }
}

答案 1 :(得分:3)

尽管this answer中描述的方法确实很好,但很快就会因为Component#getComponentAt(int x, int y)有其局限性而陷入困境:

  

确定此组件或其直接子组件之一   包含(x, y)位置,如果是,则返回包含的位置   零件。 此方法只有一个级别。如果点(x, y)   在一个子组件内部,它本身有子组件,它不会去   向下看子组件树。

注意: 备注是我的。

恕我直言,你应该遵循KISS(当然没有最后的S)原则,并采用this other answer中描述的更简单,更强大和可扩展的方法。简而言之:

  • 保留JLabel班级成员(比方说hoveredLabel

  • 仅使用MouseListener实施一个mouseEntered()来更新getSource()上的此类成员,并将其设置为mouseExited()上的null

  • 将此鼠标侦听器附加到所有标签。

  • 仅在hoveredLabel

  • 时才使用键绑定删除hoveredLabel != null