如何在面板内创建一个网格(带有网格线),并在单击任何网格单元格时让鼠标侦听器提供相对于面板的坐标?

How do I make a grid (with gridlines) inside a panel and have a mouse listener give coordinates relative to the panel when any grid cell is clicked?

我希望让下面的代码生成的网格在单击时报告鼠标指针相对于 pnlGrid 位置的 x 和 y 坐标。但是无论我点击哪里(在我添加 JLayeredPane 之前),我都没有得到任何坐标,除非鼠标指针位于红色区域。

所以我添加了几行代码来制作 JLayeredPane 我得到了鼠标坐标输出,但是没有网格线,如第二个屏幕截图所示。

如何在单击网格中的任何位置时同时获取网格线和鼠标坐标报告?

package gridcellcoordinator;
import gbl.GBConstraints;
import static java.awt.Color.*;
import java.awt.Color;
import java.awt.Dimension;
import static java.awt.EventQueue.invokeLater;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import static javax.swing.JLayeredPane.DEFAULT_LAYER;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.LineBorder;
import javax.swing.event.MouseInputAdapter;

public class GridCellCoordinator {

  final static int 
      GRID_PANEL_BORDER_WIDTH = 5,
      N = 11,
      CELLSIZE = 40;

  //static final JLayeredPane layer = new JLayeredPane();
  static final JPanel panel = new JPanel();
  static final int SM_CELL_BORDER_WIDTH = 1;
  static LineBorder SMcellBorder = new LineBorder(BLACK,SM_CELL_BORDER_WIDTH);

  static JTextField[][] cells = new JTextField[N][N];

  static JFrame frame = new JFrame();
  public GridCellCoordinator(){
    makeGrid();
  }

  private void makeGrid(){ 
    JPanel pnlGrid = new JPanel();
    pnlGrid.setLayout(new GridLayout(N,N));
    pnlGrid.setBackground(BLUE);
    pnlGrid.setBorder(BorderFactory.createLineBorder(Color.red,GRID_PANEL_BORDER_WIDTH));
    pnlGrid.setLayout(new GridLayout(N, N));

    for(int i = 0 ; i < N ; i++)
      for(int j = 0; j < N; j++){
        cells[i][j] = new JTextField();
        cells[i][j].setText("X");
        cells[i][j].setPreferredSize(new Dimension(CELLSIZE,CELLSIZE));
        cells[i][j].setHorizontalAlignment(JTextField.CENTER);
        cells[i][j].setFocusTraversalKeysEnabled(false);
        cells[i][j].setBorder(SMcellBorder);
        cells[i][j].setOpaque(true);
        pnlGrid.add(cells[i][j], new GBConstraints());
    }

    panel.addMouseListener(new MouseInputAdapter()
    {
      public void mousePressed (MouseEvent e){panelMousePressed (e);}
    });

    pnlGrid.setPreferredSize(new Dimension(N*(CELLSIZE + 1) + 2*GRID_PANEL_BORDER_WIDTH , 
                                           N*(CELLSIZE + 1) + 2*GRID_PANEL_BORDER_WIDTH));
    panel.add(pnlGrid);
    panel.setVisible(true);
    panel.setOpaque(true);
    panel.setPreferredSize(pnlGrid.getPreferredSize());
    panel.setVisible(true);
    //layer.add(pnlGrid, DEFAULT_LAYER);
    //layer.setPreferredSize(pnlGrid.getPreferredSize());
    //layer.setVisible(true);
    frame.add(panel);
    frame.setSize(new Dimension(pnlGrid.getPreferredSize()));
    frame.setVisible(true);
    frame.pack();
    System.out.println("pnlGrid component count: " + pnlGrid.getComponentCount());
    System.out.println("computed dimension: " + (N*(CELLSIZE + 1) + 2*GRID_PANEL_BORDER_WIDTH));
    System.out.println("pnlGrid pref size: " + pnlGrid.getPreferredSize());
    System.out.println("layer pref size: " + layer.getPreferredSize());
    System.out.println("panel pref size: " + panel.getPreferredSize());
  }

  public void panelMousePressed(MouseEvent e){
    JOptionPane.showMessageDialog(null,"Pressed:" + e.getX() + " " + e.getY());
  }

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

}

P.S。照原样,代码在左侧生成网格,但仅给出红色边框内的坐标。删除 4 层线上的注释栏以获取坐标但没有网格线。我想要网格线和坐标。

好吧,在上面的示例中,每个单独的编辑小部件都在占用您的鼠标。

一个普通的 Java table 只是在一个面板中显示 table,然后当它被点击时,它会在那时创建一个控件来处理编辑。因此,它不是大量的编辑控件。结果证明这是相当低效的。

所以,解决方案是"don't do what you're doing"。或者使用您已经尝试过的分层窗格。

附录:

问题是您有第一个 class 个组件的集合,每个组件都有自己的 "little" 个矩形,而不是一个更大的单一组件。这就是你遇到的问题。 Swing 正在处理所有这些标签和个别事物(因为它们是)。与他们的容器相比,他们没有任何全局上下文可以轻松报告。您必须根据它们在较大面板内转换为 "global" 坐标的本地坐标进行推断。

你可以使用 this or this

但是,要修复您的代码,您需要改变您的关注点。您不需要将 MouseListener 附加到 pnlGrid,而是将其附加到文本字段。这样做的原因是鼠标事件就像雨滴,任何附加 MouseListener 的组件都会像一把伞一样,防止 MouseEvents 发生在视觉上位于它们下方的组件上

要将 MouseEvent 从一个组件上下文转换为另一个组件上下文,您可以使用 SwingUtilities.convertMouseEvent or SwingUtilities.convertPoint

例如...

import java.awt.Color;
import static java.awt.Color.BLACK;
import static java.awt.Color.BLUE;
import java.awt.Dimension;
import static java.awt.EventQueue.invokeLater;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class GridCellCoordinator {

    final static int GRID_PANEL_BORDER_WIDTH = 5,
                    N = 11,
                    CELLSIZE = 40;

    //static final JLayeredPane layer = new JLayeredPane();
    static final JPanel panel = new JPanel();
    static final int SM_CELL_BORDER_WIDTH = 1;
    static LineBorder SMcellBorder = new LineBorder(BLACK, SM_CELL_BORDER_WIDTH);

    static JTextField[][] cells = new JTextField[N][N];

    static JFrame frame = new JFrame();

    public GridCellCoordinator() {
        makeGrid();
    }

    private void makeGrid() {
        JPanel pnlGrid = new JPanel();
        pnlGrid.setLayout(new GridLayout(N, N));
        pnlGrid.setBackground(BLUE);
        pnlGrid.setBorder(BorderFactory.createLineBorder(Color.red, GRID_PANEL_BORDER_WIDTH));
        pnlGrid.setLayout(new GridLayout(N, N));

        MouseListener mouseHandler = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                doMousePressed(e);
            }
        };

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                cells[i][j] = new JTextField() {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(20, 20);
                    }
                };
                cells[i][j].setText("X");
                cells[i][j].setPreferredSize(new Dimension(CELLSIZE, CELLSIZE));
                cells[i][j].setHorizontalAlignment(JTextField.CENTER);
                cells[i][j].setFocusTraversalKeysEnabled(false);
                cells[i][j].setBorder(SMcellBorder);
                cells[i][j].addMouseListener(mouseHandler);
                pnlGrid.add(cells[i][j]);
            }
        }
        panel.add(pnlGrid);
        panel.setVisible(true);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }

    public void doMousePressed(MouseEvent e) {
        Point p = e.getPoint();
        System.out.println("Source point = " + p + " within " + e.getComponent());
        p = SwingUtilities.convertPoint(e.getComponent(), p, e.getComponent().getParent());
        System.out.println("Converted point = " + p + " within " + e.getComponent().getParent());
    }

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

}

您应该避免使用 setPreferredSize 并在需要时更多地依赖 getPreferredSize。有关详细信息,请参阅 Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?