How to Add Mouselistener to Item on Java Swing Canvas

How to add MouseListener to item on Java Swing Canvas

Don't override paint components, use paintComponent and don't forget to call super.paintComponent

A component already has a concept of "location", so when painting, the top left position of your component is actually 0x0

What you are doing is actually painting beyond the boundaries of you component

For example, if you place your Circle at 100x100 and then did...

g2.fillOval(x, y, DIMENSION, DIMENSION); 

You would actually start painting at 200x200 (100 for the actual location of the component and 100 for you additional positioning).

Instead use

g2.fillOval(x, y, DIMENSION, DIMENSION); 

And go back and try using JLayeredPane.

You could actually write your own layout manager that takes the location of the component and it's preferred size and updates the components bounds and then apply this to a JLayeredPane. This gives you the "benefits" of an absolute layout, but keeps you within how Swing works to update its components when things change.

You should also be careful with doing anything like...

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

The Graphics context is a shared resource. That means, anything you apply to, will still be in effect when the next component is painted. This may produce some strange results.

Instead try using...

Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//...
g2.dispose();

Updated

For zooming I would take a closer look at JXLayer (or JLayer in Java 7)

The JXLayer (and excellent PBar extensions) have gone quite on the net, so you can grab a copy from here

(I tried finding a better example, but this is the best I could do with the limited time I have available)

Sample Image

Updated with working zooming example

ZoomZoom

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class TestJLayerZoom {

public static void main(String[] args) {
new TestJLayerZoom();
}

public TestJLayerZoom() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private JXLayer<JComponent> layer;
private DefaultTransformModel transformModel;
private JPanel content;

public TestPane() {

content = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;

JLabel label = new JLabel("Hello");
JTextField field = new JTextField("World", 20);

content.add(label, gbc);
content.add(field, gbc);

gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;

final JSlider slider = new JSlider(50, 200);
slider.addChangeListener(new ChangeListener() {

@Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
double scale = value / 100d;
transformModel.setScale(scale);
}
});
content.add(slider, gbc);

transformModel = new DefaultTransformModel();
transformModel.setScaleToPreferredSize(true);

Map<RenderingHints.Key, Object> hints = new HashMap<>();
//hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
//hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

layer = TransformUtils.createTransformJXLayer(content, transformModel, hints);
setLayout(new BorderLayout());
add(layer);

}

}

}

I've left the rendering hints in to demonstrate their use, but I found that they screwed with the positing of the cursor within the text field, but you might like to have a play

How to add MouseListener to JList model

Heres a great article tutorial i found that helped me to accomplish what your trying to accomplish.

http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html#popup

i also found that link from this stack overflow article

How do I create a right click context menu in Java Swing?

Hope this helps

Swing custom component dummy mouselistener

I still don't fully understand your question. As I see it, you've

  • created several JPanels
  • Given one JPanel a MouseListener and MouseMotionListener
  • You've added that JPanel to the bottom of another JPanel.
  • The JPanel that's sitting on the bottom registers all mouse events as it has been told to do
  • So your program is behaving as expected based on your code.

  • The question I have is this: how is it not behaving as you expect it to?

  • If you expect that adding a JPanel with MouseListeners and MouseMotionListeners attached will result in all the JPanels of the GUI to be listened to, well of course that won't happen. To have more of the GUI register mouse events, you'll have to add MouseListeners and MouseMotionListeners to those components. And that is my answer so far to your question as I see it. If I didn't answer the true question you currently face, then please clarify it for us.

You state:

What I want is an invisible (transparent) panel on top of the blue one in the above screenshot that is just as large as the blue one, not a subpanel that is sitting in the bottom. I want the blue one to contain this one (should not be a problem since it is just a jcomponent). What I hope to achieve is a sort over "invisible" overlay that registers mousevents so I don't have to implement these events in the blue panel.

Consider using a JLayer for this. As per the JLayer API:

JLayer is a good solution if you only need to do custom painting over compound component or catch input events from its subcomponents.


OK, I've experimented with it a bit and came up with something like this:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.*;
import javax.swing.plaf.LayerUI;

@SuppressWarnings("serial")
public class GraphPane2 extends JPanel {
private static final int GRAPH_WIDTH = 1000;
private static final int GRAPH_HEIGHT = 750;
private Graph2 graph2 = new Graph2(GRAPH_WIDTH, GRAPH_HEIGHT);

public GraphPane2() {
LayerUI<Graph2> myLayerUI = new MyLayerUI<Graph2>();
JLayer<Graph2> panelLayer = new JLayer<Graph2>(graph2, myLayerUI);
setLayout(new BorderLayout());
add(panelLayer);

myLayerUI.addPropertyChangeListener(new PropertyChangeListener() {

@Override
public void propertyChange(PropertyChangeEvent evt) {
if (MyLayerUI.MOUSE_RELEASED.equals(evt.getPropertyName())) {
Rectangle rect = (Rectangle) evt.getNewValue();
System.out.println(rect);
}
}
});
}

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

JFrame frame = new JFrame("Graph Pane2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}

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

@SuppressWarnings("serial")
class MyLayerUI<V extends JComponent> extends LayerUI<V> {
private static final Color FILL_COLOR = new Color(0, 128, 0, 128);
public static final String MOUSE_RELEASED = "mouse released";
private Point pressedPt;
private Point draggedPt;
private Rectangle rect;

@Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (rect != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FILL_COLOR);
g2.fill(rect);
}
}

public void installUI(JComponent c) {
super.installUI(c);
((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
}

public void uninstallUI(JComponent c) {
super.uninstallUI(c);
((JLayer)c).setLayerEventMask(0);
}

@Override
public void eventDispatched(AWTEvent e, JLayer<? extends V> l) {
MouseEvent mEvt = (MouseEvent) e;
int id = mEvt.getID();
int btn = mEvt.getButton();
if (id == MouseEvent.MOUSE_PRESSED && btn == MouseEvent.BUTTON1) {
pressedPt = mEvt.getPoint();
rect = new Rectangle(pressedPt.x, pressedPt.y, 0, 0);
}
if (id == MouseEvent.MOUSE_PRESSED && btn != MouseEvent.BUTTON1) {
pressedPt = null;
}
if (id == MouseEvent.MOUSE_DRAGGED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
}
if (id == MouseEvent.MOUSE_RELEASED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
firePropertyChange(MOUSE_RELEASED, null, rect);
}
l.repaint();
}
}

@SuppressWarnings("serial")
class Graph2 extends JPanel {
private static final int MAX_DATA_POINTS = 100;
private static final int STEP = 10;
private static final Stroke STROKE = new BasicStroke(3f);
private Path2D path2D;
private int width;
private int height;
private int[] data = new int[MAX_DATA_POINTS + 1];
private Random random = new Random();

public Graph2(int width, int height) {
this.width = width;
this.height = height;
init();

addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
path2D = new Path2D.Double();
int w = getWidth();
int h = getHeight();
double x = 0;
double y = ((double) MAX_DATA_POINTS - data[0]) * h
/ MAX_DATA_POINTS;
path2D.moveTo(x, y);
for (int i = 1; i < data.length; i++) {
x = (i * w) / (double) MAX_DATA_POINTS;
y = ((double) MAX_DATA_POINTS - data[i]) * h
/ (double) MAX_DATA_POINTS;
path2D.lineTo(x, y);
}
}
});
}

@Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}

protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (path2D != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(STROKE);
g2d.draw(path2D);
}
};

private void init() {
// create and fill random data
data[0] = 0;
boolean up = true;
// max and min data values -- used for normalization
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 1; i < data.length; i++) {
up = random.nextInt(4) < 3 ? up : !up;
if (up) {
data[i] = data[i - 1] + random.nextInt(STEP);
} else {
data[i] = data[i - 1] - random.nextInt(STEP);
}

if (data[i] > max) {
max = data[i];
}
if (data[i] < min) {
min = data[i];
}
}

// normalize the data
for (int i = 0; i < data.length; i++) {
int datum = (MAX_DATA_POINTS * (data[i] - min)) / (max - min);
data[i] = datum;
}

}
}

This will look like:

Sample Image

How to add a mouselistener below other things in the JFrame?

The solution turned out to be simple!

instead of creating a panel for the mouse listener, you can add a mouselistener to the game object, namely as follows

public Window(int width, int title, String title, Game game) {
super(title);
HandlerClass handler = new HandlerClass();

setPreferredSize(new Dimension(width,height));
setMaximumSize(new Dimension(width,height));
setMinimumSize(new Dimension(width,height));
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);

setVisible(true);
add(game, BorderLayout.CENTER);
game.addMouseListener(handler);
game.start();
}

Hope this helps!

JMapViewer, MouseListener called 2 times

Your Test extends JMapViewer, adding a MouseListener in an instance initializer block. As a consequence, the "default constructor will call the no-argument constructor of the superclass." The superclass, JMapController, adds your MouseListener—you guessed it—a second time.

public JMapController(JMapViewer map) {
this.map = map;
if (this instanceof MouseListener)
map.addMouseListener((MouseListener) this);

}

Instead, create a new JMapController or DefaultMapController, as shown here, and use it to construct your JMapViewer.

import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import org.openstreetmap.gui.jmapviewer.DefaultMapController;
import org.openstreetmap.gui.jmapviewer.JMapViewer;

/**
* @see https://stackoverflow.com/a/39461854/230513
*/
public class TestMapController {

private void display() {
JFrame f = new JFrame("TestMapController");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMapViewer map = new JMapViewer();
new DefaultMapController(map) {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println(e.getPoint());
}
};
f.add(map);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}

public static void main(String[] args) {
EventQueue.invokeLater(new TestMapController()::display);
}
}

Using addMouseListener() and paintComponent() for JPanel

You must add the mouseListener to the panel. That doesn't happen by default as you might have expected ;-)

MainClass1() {
addMouseListener(this);
}

BTW: it's not recommended to expose public api that's only meant to be used internally. So instead of letting the panel implement MouseListener (which enforces the public exposure), let the panel create and use a MouseListener:

private MouseListener mouseListener;
MainClass1() {
mouseListener = createMouseListener();
addMouseListener(mouseListener);
}

protected MouseListener createMouseListener() {
MouseListener l = new MouseListener() {

}
return l;
}

BTW 2: calling the repaint on the limited area isn't exactly what you want (?) - it temporarily adds the squares to the painting, they are lost whenever the whole panel is repainted (same effect as with getGraphics). Depending on what you really want,

  • paint a single square at the most recently clicked position: call repaint()
  • paint squares at all locations ever clicked: store the locations in a list and implement repaint to loop over that list. Here you may call the repaint with parameters, but why bother?

Using MouseListener with vlcj

With vlcj on Linux and Windows adding a MouseListener to the video surface Canvas should just work in the usual way.

If you use the vlcj MediaPlayerComponent encapsulation, this works (for me at least):

mediaPlayerComponent.getVideoSurface().addMouseListener(listener);

If you don't use a MediaPlayerComponent, then just add your listener directly to your Canvas.

MouseEvents inside JList objects

More exactly, I added a mouse listener for the list, but I want it to trigger something if the mouse point is located inside the bounds of the JButton, and another action if it's on the data item.

An easier solution would be to use a JTable. The data is separated into columns and the JTable has an API to let you know which row/column was selected.

You can use the Table Button Column as your renderer/editor for the button.

Edit:

only if I click the JButton of the FIRST element of the list does it trigger the required mouse action

Sounds like your conversion of the mouse point is off.

, how can all their JButtons have the same location?

Again, the button location is releative to the renderer panel. The panel itself is relative to the row in the JList. So I would guess you need to need the row index and add the height of each of the previous rows to your calculation.

MouseListener Help Java

Any Component can have a MouseListener. JLabel is nice for a colored rectangle, as long as you make it opaque.

Addendum: Having recommended MouseAdapter elsewhere, I should mention that one instance is enough.

Addendum: This update adds the mouse listener in the ColorLabel constructor.

ColorLabel picture

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;

/** @see http://stackoverflow.com/questions/5136859 */
public class ColorLabel extends JLabel {

private static final int N = 10;
private static final Random random = new Random();
private static final MouseAdapter listener = new MouseAdapter() {

@Override
public void mousePressed(MouseEvent e) {
ColorLabel label = (ColorLabel) e.getSource();
label.setBackground(new Color(random.nextInt()));
}
};

public ColorLabel() {
this.setOpaque(true);
this.setBackground(new Color(random.nextInt()));
this.setPreferredSize(new Dimension(32, 32));
this.addMouseListener(listener);
}

private void displayGrid() {
JFrame f = new JFrame("ColorGrid");
f.setLayout(new GridLayout(N, N));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i = 0; i < N * N; i++) {
final ColorLabel label = new ColorLabel();
f.add(label);
}
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
new ColorLabel().displayGrid();
}
});
}
}


Related Topics



Leave a reply



Submit