Java - JPanel содержит ошибку метода точки

У меня есть 2d-массив сеток (JPanels), которые добавляются в другую JPanel с помощью GridLayout. Этот JPanel добавляется в JFrame. Всякий раз, когда на JFrame происходит щелчок, я пытаюсь взять точку щелчка и определить, содержит ли какая-либо из этих сеток в массиве 2d эту точку.

Я пытаюсь сделать это внутри frame.addMouseListener...

Я знаю, что кадр регистрирует щелчки мыши. По какой-то причине Решетки не регистрируют, что они должны содержать эту Точку. Кто-нибудь может это объяснить? if(theView[i][j].contains(me.getPoint())){ Кажется, эта строка кода меня не устраивает.

Первоначально я пытался, чтобы сетки знали, когда на них нажимают, чтобы мне не приходилось координировать между рамкой и сетками, но я не мог заставить это работать.

Вот дизайнер уровней.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import javax.swing.*;

public class LevelDesigner extends JPanel implements ButtonListener{
    private final int SIZE = 12;
    private int [][] thePit;    
    private Grid [][] theView;
    private ButtonPanel bp;
    public static int val;
    private int rows, cols;
    private JPanel gridPanel;
    private JFrame frame;

    public LevelDesigner(int r, int c){    
        frame = new JFrame();        
        int h = 10, w = 10;
        setVisible(true);
        setLayout(new BorderLayout());
        setBackground(Color.BLUE);
        rows = r;
        cols = c;
        thePit = new int[r][c];
        theView = new Grid[r][c];   
        gridPanel = new JPanel();
        gridPanel.setVisible(true);
        gridPanel.setBackground(Color.BLACK);
        gridPanel.setPreferredSize(getMaximumSize());
        GridLayout gridLayout = new GridLayout();
        gridLayout.setColumns(cols);
        gridLayout.setRows(rows);
        gridPanel.setLayout(gridLayout);

        for(int i = 0; i < r; i++){
            for(int j = 0; j < c; j++){
                theView[i][j] = new Grid(i, j, SIZE, this);
                gridPanel.add(theView[i][j]);
            }
        }

        String test [] = {"0", "1","2","3","4","save"};
        bp =  new ButtonPanel(test,  this);
        this.add(bp, BorderLayout.SOUTH);
        this.add(gridPanel, BorderLayout.CENTER);

        frame.addMouseListener(new MouseAdapter(){
            public void mousePressed(MouseEvent me) {

                for(int i = 0; i < rows; ++i){
                    for(int j = 0; j < cols; ++j){
                        if(theView[i][j].contains(me.getPoint())){
                            theView[i][j].actionPerformed(null);
                            return;
                        }
                    }
                }
            } 
        });

        frame.setVisible(true);
        frame.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
        frame.setTitle("Epic Crawl - Main Menu");
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.repaint();
        frame.add(this);
    }

    public String toString(){
        int noRows = thePit.length;
        int noColumns = thePit[0].length;

        String s="";
        for (int r=0;r<noRows;r++){
            for (int c=0;c<noColumns;c++){
                s=s + thePit[r][c] + " ";
            }
            s=s+"\n";
        }
        return(s);
    }

    public void notify( int i, int j){
        thePit[i][j] = val;
    }

    public void print(){
        final JFileChooser fc = new JFileChooser();
        fc.setCurrentDirectory(new java.io.File("."));
        int returnVal = fc.showSaveDialog( null);

        if( returnVal == JFileChooser.APPROVE_OPTION ){
            try{
                PrintWriter p = new PrintWriter( 
                new File( fc.getSelectedFile().getName() ) );
                System.out.println(" printing");

                p.println( this );
                p.close();   
            }
            catch( Exception e){
                System.out.println("ERROR: file not saved");
            }
        }
    }

    public void buttonPressed(String buttonLabel, int id){
        if(id == 5)
            print();
        else
            val = id;
    }

    public void buttonReleased( String buttonLabel, int buttonId ){}
    public void buttonClicked( String buttonLabel, int buttonId ){}

    public static void main(String arg[]){ 
        LevelDesigner levelDesigner = new LevelDesigner(4, 4);
    }
}

А вот и Сетка.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Grid extends JPanel implements ActionListener{
    LevelDesigner grid;    
    int myI, myJ;
    private String[] imageNames = {"dirt.png", "grass.png", "Door.png", "woodfloor.png", "32x32WoodFloor.png"};
    BufferedImage gridImage;
    private String imagePath;

    public Grid(int i, int j, int size, LevelDesigner m){
        imagePath = "";
        grid = m;
        myI = i;
        myJ = j;
        setBackground(Color.RED);
        this.setBorder(BorderFactory.createLineBorder(Color.black));
        this.setVisible(true);
    }

   @Override
   public void actionPerformed(ActionEvent ae){
        grid.notify(myI, myJ);
        imagePath = "Images/" + imageNames[LevelDesigner.val];
        gridImage = null;
        InputStream input = this.getClass().getClassLoader().getResourceAsStream(imagePath);
        try{
            gridImage = ImageIO.read(input);
        }catch(Exception e){System.err.println("Failed to load image");}
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g); // Important to call super class method
        g.clearRect(0, 0, getWidth(), getHeight()); // Clear the board
        g.drawImage(gridImage, 0, 0, getWidth(), getHeight(), null);
    }
}

person Andrew_CS    schedule 19.07.2013    source источник
comment
Работа с Points запутана, потому что вы всегда должны знать, в какой системе координат вы находитесь - в этом случае это вас кусает. Гораздо лучше прикрепить слушателей к панелям JPanel, в которых вы хотите, чтобы произошло событие.   -  person roippi    schedule 19.07.2013
comment
Изначально я пытался пойти по этому пути, потому что хотел избежать использования Point, спасибо за совет!   -  person Andrew_CS    schedule 19.07.2013
comment
Я знаю, что вы выбрали правильный ответ, но, тем не менее, см. редактирование моего ответа.   -  person Hovercraft Full Of Eels    schedule 19.07.2013
comment
Я сделал, спасибо за ответ.   -  person Andrew_CS    schedule 19.07.2013


Ответы (2)


Component#contains "Проверяет, содержит ли этот компонент указанную точку, где координаты точки x и y определены относительно системы координат этого компонента"

Это означает, что contains вернет true только в том случае, если Point находится в пределах 0 x 0 x width x height.

Итак, если компонент имеет размер 400x200 и размер 200x200 (например). Когда вы щелкаете внутри, точка мыши будет находиться между 400x200 и 600x400, что на самом деле находится за пределами относительного положения компонента (200x200) - все еще запутанно...

По сути, вам нужно либо преобразовать точку щелчка в относительную координату в контексте проверяемого компонента...

Point p = SwingUtilities.convertPoint(frame, me.getPoint(), theView[i][j])
if (theView[i][j].contains(p)) {...

Или используйте компоненты Rectanglebounds...

if (theView[i][j].getBounds().contains(me.getPoint())) {...

Итак, помните, события мыши относятся к компоненту, для которого они были сгенерированы.

person MadProgrammer    schedule 19.07.2013
comment
Использовал Point p = SwingUtilities.convertPoint(frame, me.getPoint(), theView[i][j]) if (theView[i][j].contains(p)), и он прекрасно работает. Пришлось добавить вызов repaint() внутри метода actionPerformed Grid, но, вероятно, в любом случае это должно было быть. Спасибо! - person Andrew_CS; 19.07.2013

Метод contains проверяет, находится ли Point в границах JPanel, но использует систему координат относительно JPanel. Вместо этого рассмотрите возможность использования findComponentAt(Point p).

Например:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class TestMouseListener {
   private static final int SIDE_COUNT = 4;
   private JPanel mainPanel = new JPanel();
   private MyGridCell[][] grid = new MyGridCell[SIDE_COUNT][SIDE_COUNT];

   public TestMouseListener() {
      mainPanel.setLayout(new GridLayout(SIDE_COUNT, SIDE_COUNT));
      for (int i = 0; i < grid.length; i++) {
         for (int j = 0; j < grid[i].length; j++) {
            grid[i][j] = new MyGridCell();
            mainPanel.add(grid[i][j].getMainComponent());
         }
      }

      mainPanel.addMouseListener(new MouseAdapter() {
         @Override
         public void mousePressed(MouseEvent e) {
            Point p = e.getPoint();
            Component c = mainPanel.findComponentAt(p);
            for (MyGridCell[] gridRow : grid) {
               for (MyGridCell myGridCell : gridRow) {
                  if (c == myGridCell.getMainComponent()) {
                     myGridCell.setLabelText("Pressed!");
                  } else {
                     myGridCell.setLabelText("");
                  }
               }
            }
         }
      });
   }

   public Component getMainComponent() {
      return mainPanel;
   }

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

      JFrame frame = new JFrame("TestMouseListener");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

class MyGridCell {
   private static final int PREF_W = 200;
   private static final int PREF_H = PREF_W;
   @SuppressWarnings("serial")
   private JPanel mainPanel = new JPanel() {
      public Dimension getPreferredSize() {
         return MyGridCell.this.getPreferredSize();
      };
   };
   private JLabel label = new JLabel();

   public MyGridCell() {
      mainPanel.setBorder(BorderFactory.createLineBorder(Color.black));
      mainPanel.setLayout(new GridBagLayout());
      mainPanel.add(label);
   }

   public Component getMainComponent() {
      return mainPanel;
   }

   public void setLabelText(String text) {
      label.setText(text);
   }

   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }
}
person Hovercraft Full Of Eels    schedule 19.07.2013
comment
+1 от меня, хотя в прошлом у меня были проблемы с этим, он должен делать то, что хочет ОП. - person MadProgrammer; 19.07.2013
comment
Я не знал, что смогу найти компонент в зависимости от местоположения, приятно знать! - person Andrew_CS; 19.07.2013