我有两个JPanels
大小相等的,一个在另一个之上。顶层用作拖动选择面板,另一层添加了其他组件。我的问题是这些添加的组件的鼠标事件处理程序没有被触发,因为它们是由覆盖面板处理的。我怎样才能仍然拖动这些添加的组件的顶部,但仍然拥有mouseEntered
并mouseExited
启用底层组件?
这是一个屏幕截图:
如您所见,选择矩形绘制在覆盖层上JPanel
,但好像我的鼠标无法通过此面板查看下面的内容(寻找更好的解释方式)。
我有两个JPanels
大小相等的,一个在另一个之上。顶层用作拖动选择面板,另一层添加了其他组件。我的问题是这些添加的组件的鼠标事件处理程序没有被触发,因为它们是由覆盖面板处理的。我怎样才能仍然拖动这些添加的组件的顶部,但仍然拥有mouseEntered
并mouseExited
启用底层组件?
这是一个屏幕截图:
如您所见,选择矩形绘制在覆盖层上JPanel
,但好像我的鼠标无法通过此面板查看下面的内容(寻找更好的解释方式)。
重新发明轮子的替代方法是使用 JLayer(jdk7 的新功能,可用于SwingLabs 子项目 JXLayer 中的 jdk6):
JLayer 是 Swing 组件的通用装饰器,使您能够实现各种高级绘画效果以及接收其边界内生成的所有 AWTEvents 的通知
下面是一个简单的例子——只是为了演示它的用法,逻辑显然不完整:)——用 mouseEvents 跨越橡皮筋
// UI which allows to span a rubberband on top of the component
public static class RubberBandUI<V extends JComponent> extends LayerUI<V> {
private JLayer<?> l;
private Rectangle rubberband;
private boolean selecting;
@Override
public void installUI(JComponent c) {
super.installUI(c);
l = (JLayer<?>) c;
// this LayerUI will receive mouse/motion events
l.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
@Override
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
// JLayer must be returned to its initial state
l.setLayerEventMask(0);
l = null;
}
@Override
public void paint(Graphics g, JComponent l) {
// this paints layer as is
super.paint(g, l);
if (rubberband == null) return;
Graphics2D g2 = (Graphics2D) g;
// custom painting is here
g2.setColor(Color.RED);
g2.setStroke(new BasicStroke(2f));
g2.draw(rubberband);
}
// intercept events as appropriate
@Override
protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends V> l) {
super.processMouseMotionEvent(e, l);
if (e.getID() == MouseEvent.MOUSE_DRAGGED && selecting) {
Point point = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
adjustRubberband(point);
l.repaint();
}
}
@Override
protected void processMouseEvent(MouseEvent e, JLayer<? extends V> l) {
super.processMouseEvent(e, l);
if (e.getID() == MouseEvent.MOUSE_RELEASED) {
endRubberband();
}
if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getSource() == l) {
startRubberband(e.getPoint());
}
}
// logic to start/stop/adjust the rubberband
private void adjustRubberband(Point point) {
// logic to span the rubberband
int width = point.x - rubberband.x;
int height = point.y - rubberband.y;
rubberband.setSize(width, height);
}
private void startRubberband(Point p) {
rubberband = new Rectangle(p);
selecting = true;
// block events to child components while drawing
l.getGlassPane().setVisible(true);
l.repaint();
}
private void endRubberband() {
selecting = false;
l.getGlassPane().setVisible(false);
l.repaint();
}
public void clear() {
rubberband = null;
l.repaint();
}
}
示例用法片段:
JPanel panel = new JPanel();
for (int i = 0; i < 3; i++) {
panel.add(new JButton("JButton"));
panel.add(new JCheckBox("JCheckBox"));
panel.add(new JTextField("JTextField"));
}
JLayer<JComponent> l = new JLayer<JComponent>(panel, new RubberBandUI<JComponent>());
frame.add(l);
不要使用覆盖面板。我在你上一篇文章中提出了一个关于你如何做到这一点的建议。
或者,如果您确实使用了覆盖面板,那么只需将其用于绘图并让底层面板侦听鼠标事件,然后在覆盖面板上调用重新绘制。
编辑:
也许更好的方法是在各个组件上使用 MouseListener,然后处理矩形的绘制,您可以使用全局事件侦听器来侦听 mousePressed、mouseDragged、mouseReleased 事件。您需要检查每个事件的来源,以查看来源是否是面板本身或面板上的组件。您可以SwingUtilities.isDescendingFrom(...)
用来帮助进行第二次测试。