Mousemotionlistener in Java Swing, Using It with Components Inside Components etc

MouseMotionListener in Java Swing, using it with components inside components etc

How about using a GlassPane? I think its meant to address exactly these types of situations.

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

component.getMouseMotionListener return more than one

  • output is correct, JTextField has another notifiers implemented API, they are notified from added MouseListener

  • see whats debuger returns

Sample Image

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class MouseAndJTextField {

private JFrame fr = new JFrame();
private JTextField jf = new JTextField(20);

public MouseAndJTextField() {
jf.addMouseListener(new MouseListener() {
@Override
public void mouseReleased(MouseEvent arg0) {
}

@Override
public void mousePressed(MouseEvent arg0) {
}

@Override
public void mouseExited(MouseEvent arg0) {
}

@Override
public void mouseEntered(MouseEvent arg0) {
}

@Override
public void mouseClicked(MouseEvent arg0) {
System.out.println(jf.getMouseListeners().length);
}
});
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fr.add(jf);
fr.pack();
fr.setVisible(true);
}

public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
MouseAndJTextField fs = new MouseAndJTextField();
}
});
}
}

swing mouse listeners being intercepted by child components

For example

Sample Image

Sample Image

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class TestMouseListener {

public static void main(String[] args) {
final JComboBox combo = new JComboBox();
combo.setEditable(true);
for (int i = 0; i < 10; i++) {
combo.addItem(i);
}
final JLabel tip = new JLabel();
tip.setPreferredSize(new Dimension(300, 20));
JPanel panel = new JPanel();
panel.add(combo);
panel.add(tip);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
panel.addMouseListener(new MouseAdapter() {

@Override
public void mouseEntered(MouseEvent e) {
tip.setText("Outside combobox");
}

@Override
public void mouseExited(MouseEvent e) {
Component c = SwingUtilities.getDeepestComponentAt(
e.getComponent(), e.getX(), e.getY());
// doesn't work if you move your mouse into the combobox popup
tip.setText(c != null && SwingUtilities.isDescendingFrom(
c, combo) ? "Inside combo box" : "Outside combobox");
}
});
}

private TestMouseListener() {
}
}

Manually movable JComponent in a JScrollPane

Scrollbars automatically appear/disappear when the preferred size of the component displayed in the viewport of the scroll panes changes.

You can use the Drag Layout. This class is a custom layout manager and will automatically recalculate the preferred size of the panel as components are dragged around the panel.

You will need to handle the mouseReleased event so you can revalidate() the panel, and invoke the DragLayout once you are finished dragging the component so the preferred size can be reset. Or you could use the ComponentMover class which is referenced in the blog article to do the dragging of the component for you. It supports an auto resize property which will do the revalidate() for you.

Isometric Game Mouse logic and movement around an environment

it resets itself to the original location before moving again.

Did you add any debug code to look at your calculation of the x/y position?

@Override
public void mousePressed(MouseEvent evt) {

if(SwingUtilities.isRightMouseButton(evt)) {
mouseClickX = evt.getX();
mouseClickY = evt.getY();
}
}

@Override
public void mouseDragged(MouseEvent evt) {
if(SwingUtilities.isRightMouseButton(evt)) {
System.out.println(mouseClickX + " : " + evt.getX());
positionX = mouseClickX - evt.getX();
positionY = mouseClickY - evt.getY();
}
}

You set the mouseClickX/Y to the mouse point, then in the mouseDragged you subtract the mouse point from the value. Which means you basically get 0 (well actually 1, since the mouse moved 1 pixel to generate the drag event).

So in the mousePressed logic you can't just reset the mouseClickX/Y to the current mouse point. You need to track that last mouse point as well:

@Override
public void mousePressed(MouseEvent evt) {

if(SwingUtilities.isRightMouseButton(evt)) {
//mouseClickX = evt.getX();
//mouseClickY = evt.getY();
mouseClickX = positionX + evt.getX();
mouseClickY = positionY + evt.getY();
}
}

How to NOT extend Swing components?

in which I can reposition components with the mouse:

Then you just add the MouseListener/MouseMotionListener to the components that you want to drag. There is no need to extend any component to add that functionality.

Check out the Component Mover class for examples of ways to do basic dragging of a component as well as a more complex dragging solution.



Related Topics



Leave a reply



Submit