Как создать сетку (с линиями сетки) внутри панели и заставить прослушиватель мыши давать координаты относительно панели при нажатии любой ячейки сетки?

Я надеялся, что сетка, созданная кодом ниже, сообщает координаты x и y местоположения указателя мыши относительно pnlGrid при нажатии. Но где бы я ни щелкал (до того, как добавил 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 линиях слоя, чтобы получить координаты, но не линии сетки. Мне нужны ОБЕ линии сетки и координаты.


person DSlomer64    schedule 23.03.2015    source источник
comment
Что ж, я думаю, что доволен своим обходным путем, хотя я хотел бы знать, как избежать обходного пути, то есть изменить JTextFields на JLabels. Думаю, я близок к тому, чтобы это работало с JTextFields, но я слишком туп, чтобы увидеть проблему.   -  person DSlomer64    schedule 24.03.2015
comment
Ой. Я также оставил многослойную панель вне изображения.   -  person DSlomer64    schedule 24.03.2015
comment
Для пример и пример   -  person MadProgrammer    schedule 24.03.2015
comment
Чтобы перевести MouseEvent из контекста одного компонента в другой, вы можете использовать SwingUtilities.convertMouseEvent или SwingUtilities.convertPoint   -  person MadProgrammer    schedule 24.03.2015


Ответы (2)


Вы можете использовать что-то вроде это или это

Однако, чтобы исправить свой код, вам нужно изменить свой фокус. Вместо того, чтобы прикреплять MouseListener к pnlGrid, вам нужно прикрепить его к текстовым полям. Причина этого в том, что события мыши подобны каплям дождя, любой компонент с прикрепленным MouseListener будет действовать как зонтик, предотвращая появление MouseEvents на компонентах, которые визуально находятся под ними.

Чтобы перевести MouseEvent из контекста одного компонента в другой, вы можете использовать SwingUtilities.convertMouseEvent или 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, когда это необходимо. См. Должен ли я избегать использования набора (предпочтительно |Максимум|Минимум)Размер методов в Java Swing? подробнее

person MadProgrammer    schedule 23.03.2015
comment
@MadProgrammer - спасибо за ссылки, особенно на SwingUtilities, некоторые колеса которого я заново изобретал. Давно поручаю сделать как вы предлагаете (назначить слушателей на каждую ячейку). Однако, пытаясь выяснить, как перетащить JLabel из JPanel по сетке (на прошлой неделе), мне пришлось использовать JLayeredPane для всей сетки и еще один JPanel. Удаление было неточным, поэтому я хотел найти способ перейти от e.getX() и e.getY() к строке и столбцу сетки. Отсюда вопрос. Проблема решена, но не так, как описано в предыдущем комментарии. Может опубликовать как ответ. - person DSlomer64; 25.03.2015
comment
Тогда я бы сказал, что вы неправильно выполняете процесс перетаскивания. Вообще говоря, вы не хотите передавать метку, но с данными меток гораздо проще иметь дело - для пример - person MadProgrammer; 25.03.2015
comment
Вы также можете ознакомиться с перетаскиванием и передачей данных. - person MadProgrammer; 25.03.2015
comment
@MadProgrammer - спасибо за еще советы и ссылки. Кстати, я получил свой подход от ссылки Trashgod на эту программу Chessboard. Поскольку это не прошло гладко, я изучу ваш пример кода и посмотрю, как это работает. Как это бывает, перетаскивание метки с выгравированной рамкой создает впечатление, что вы на самом деле перетаскиваете плитку Scrabble по доске, но работа с данными внутри метки внутри панели из 7 меток была кошмаром в течение нескольких дней. - person DSlomer64; 26.03.2015

Ну, каждый отдельный виджет редактирования — это то, что ест вашу мышь в приведенном выше примере.

Обычная таблица Java просто отображает таблицу на одной панели, а затем, когда ее щелкают, в это время создается элемент управления для обработки редактирования. Таким образом, это не большой набор элементов управления редактированием. Получается довольно неэффективно.

Итак, решение: «Не делай того, что делаешь». Или используйте многоуровневую панель, как вы уже пробовали.

Дополнения:

Проблема в том, что у вас есть набор первоклассных компонентов, каждый со своими «маленькими» прямоугольниками, а не более крупный отдельный компонент. Это проблема, с которой вы столкнулись. Swing обрабатывает все эти ярлыки и отдельные вещи (потому что они есть). У них нет глобального контекста, о котором можно было бы легко сообщить, по сравнению с их контейнером. Вам придется экстраполировать на основе их локальных координат, преобразованных в «глобальные» координаты на большей панели.

person Will Hartung    schedule 23.03.2015
comment
Верно. Вот почему я попробовал многоуровневую панель, но не смог получить ни линии сетки, ни координаты. Тем не менее, см. мой комментарий выше о моем обходном пути (изменение 3 или 4 строк, изменение текстовых полей на метки). - person DSlomer64; 24.03.2015