Reputation: 17142
I have a JPanel
(the grid), nested inside the main JFrame
. I am trying to set a Mouselistener
on the grid & report the event to the main frame.
App.java (Entry point)
public class App {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new MainBoard("Tic Swing Toe");
}
});
}
}
MainBoard.java (The main container, the controller)
public class MainBoard extends JFrame {
public static final int WINDOW_MIN_WIDTH = 800;
public static final int WINDOW_MIN_HEIGHT = 700;
private HeaderPanel mHeaderPanel; // The header panel
private BoardPanel mBoardPanel; // The game board panel
public MainBoard(String windowTitle) {
// Set window title
super(windowTitle);
setMinimumSize(new Dimension(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT));
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(new TilePanel());
setLayout(new BorderLayout());
pack();
setVisible(true);
// Setup components
initGUI();
// Setup listeners on child components
setupChildListeners();
}
/**
* Setup the GUI components
*/
private void initGUI() {
mHeaderPanel = new HeaderPanel();
mBoardPanel = new BoardPanel();
add(mHeaderPanel, BorderLayout.NORTH);
add(mBoardPanel, BorderLayout.WEST);
}
/**
* Sets listeners on child components
*/
private void setupChildListeners() {
mBoardPanel.setHoverListener((x, y) -> System.out.println(x + " - " + y));
}
/**
* The background of the application
*/
class TilePanel extends JPanel {
private BufferedImage mTile;
public TilePanel() {
try {
// Read image from URL, could change it to disk file
mTile = ImageIO.read(new URL("http://turbo.designwoop.com/uploads/2012/03/01_free_subtle_textures_apple_ios_linen_texture.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int tileWidth = mTile.getWidth();
int tileHeight = mTile.getHeight();
for (int y = 0; y < getHeight(); y += tileHeight) {
for (int x = 0; x < getWidth(); x += tileWidth) {
g2d.drawImage(mTile, x, y, this);
}
}
g2d.dispose();
}
}
}
BoardPanel.java (The JPanel containing the grid, which I'm interested in)
public class BoardPanel extends JPanel {
public static final int BOARD_BORDER_RADIUS = 20;
public static final int CELL_MARGIN = BOARD_BORDER_RADIUS / 2;
public static final int CELL_SIZE = 120;
public static final int BOARD_WIDTH = CELL_SIZE * 3 + CELL_MARGIN * 4;
public static final int BOARD_HEIGHT = CELL_SIZE * 3 + CELL_MARGIN * 4;
// The hover listener that shall report to the
private CellHoverListener mHoverListener;
public void setHoverListener(CellHoverListener listener) {
mHoverListener = listener;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
addMouseListener(new BoardMouseListener());
}
// A custom MouseListener
class BoardMouseListener implements MouseListener {
@Override
public void mouseClicked(MouseEvent e) { }
@Override
public void mousePressed(MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent e) {
if(mHoverListener != null)
mHoverListener.onCellHover(e.getX(), e.getY());
}
@Override
public void mouseExited(MouseEvent e) { }
}
@Override
public Dimension getPreferredSize() {
return new Dimension(BOARD_WIDTH, BOARD_HEIGHT);
}
}
HeaderPanel.java (The header containing the 2 text lines)
public class HeaderPanel extends JPanel {
public static final String HEADER_TEXT = "Pure Tic Tac Toe Java - AI";
public static final String SMALL_HEADER_TEXT =
"The only game where you are the champion if it's a draw";
// TODO: figure out a way to fill the whole header dynamically
public static final int HEADER_WIDTH = 1366; // temporary solution
public static final int HEADER_HEIGHT = 100;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw the dark blue rectangle
g.drawRect(0, 0, HEADER_WIDTH, HEADER_HEIGHT);
g.setColor(new Color(33, 33, 33, 240)); // Dark blue
g.fillRect(0, 0, HEADER_WIDTH, HEADER_HEIGHT);
// Draw the first line of text
g.setColor(Color.WHITE);
FontMetrics fm = g.getFontMetrics();
Rectangle2D header = fm.getStringBounds(HEADER_TEXT, g);
int x = (this.getWidth() - (int) header.getWidth()) / 2;
int y = (this.getHeight() - (int) header.getHeight()) / 4 + fm.getAscent();
g.drawString(HEADER_TEXT, x, y);
// Draw the second line of text
fm = g.getFontMetrics();
Rectangle2D miniHeader = fm.getStringBounds(SMALL_HEADER_TEXT, g);
x = (this.getWidth() - (int) miniHeader.getWidth()) / 2;
y = (this.getHeight() - (int) miniHeader.getHeight()) * 3 / 4 + fm.getAscent();
g.setColor(new Color(44, 170, 231, 250));
g.drawString(SMALL_HEADER_TEXT, x, y);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(HEADER_WIDTH, HEADER_HEIGHT);
}
}
CellHoverListener.java
public interface CellHoverListener {
void onCellHover(int x, int y);
}
is that the hover events are not detected at all. I tried to set a MouseListener
directly on the BoardPanel
, but same old behavior.
How can I correctly setup a listener on mouse events in this case?
Upvotes: 2
Views: 203
Reputation: 13427
Your main problem with detecting movement events is that you are not assigning a MouseMotionListener
:
For example, create a constructor for you panel with:
public BoardPanel() {
BoardMouseListener bml = new BoardMouseListener();
addMouseListener(bml);
addMouseMotionListener(bml);
}
or wherever you set up your components if you don't want a constructor. Then @Override
methods for whichever events you want to detect:
class BoardMouseListener extends MouseAdapter {
@Override
public void mouseEntered(MouseEvent e) {
System.out.println(e.getX() + " - " + e.getY());
}
@Override
public void mouseMoved(MouseEvent e) {
System.out.println(e.getX() + " - " + e.getY());
}
}
Your other problems are:
addMouseListener
inside paintComponent
. Each time the component is painted you are creating and registering a new listener. This is bad.setVisible
should be called only after you finish setting up all your components.extends MouseAdapter
instead of implements MouseListener
if you don't need to override all of its methods. If you press a button during the movement it won't be "recorded" since this is now a dragging event - use the apprropriate method.Upvotes: 2
Reputation: 12332
First, remove addMouseListener()
from paintComponent()
. Note that paintComponent()
is called repeatedly -- and I'm sure you weren't intending to keep adding new mouse listeners on each repaint()
.
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
}
Add your listener in a constructor or some kind of initGUI()
call:
public BoardPanel() {
addMouseListener(new BoardMouseListener());
}
Add a debugging statement here. You'll see that it is indeed being called.
public void mouseEntered(final MouseEvent e) {
System.out.println(e);
if (mHoverListener != null) {
mHoverListener.onCellHover(e.getX(), e.getY());
}
}
Upvotes: 2