【发布时间】:2018-04-25 17:39:14
【问题描述】:
背景:目前在我的项目中,我有多个组件,每个组件都有一个自定义 MouseListener。使用这些 MouseListener,大部分代码都在 mouseEntered/mouseExit 方法中。
现在我正在尝试在光标下实现发光效果。为此,我一直使用堆叠在一起的 JPanel,但对于我提供的示例代码,我将设置窗格(ContentPane 和 GlassPane)。
示例中显示的问题是 GlassPane (glowPanel) 拦截了所有 MouseEvents,因此我不知道如何合并 mouseEntered/mouseExit 事件,因为 GlassPane/glowPanel 覆盖了整个窗口:又名 mouseEntered/mouseExit 事件永远不会发生。我在想我可以随时跟踪我所在的组件,当它发生变化时,我会重新调度 mouseEntered/mouseExit,但我不确定这是否是解决此问题的最佳方法。另一种选择只是向 mouseEntered/mouseExit 发送垃圾邮件,但我知道这行不通。
在示例代码中,我提供了两种我尝试过的方法。
MouseListener方式:使用鼠标事件更新鼠标当前所在的glowPanel并重绘。无法正常工作,无法正确重新调度 mouseEntered、mouseExit,因为 GlowPanel 从不触发此类事件(
线程:让一个线程在循环中运行,每隔一段时间就休眠一次。当它唤醒时获取鼠标位置,然后用新位置重新绘制发光面板。这可行,但我认为如果我能让 mouseEntered/mouseExit 事件正常工作,MouseListener 方式总体上会更好。
我知道这段代码不是最漂亮的。我试图尽可能快地做到这一点,以展示一个例子。我希望稍后会通过代码并使其更漂亮。
public class GlowPanel extends JPanel{
//Attributes
private MouseAdapter repaintAdapter;
private MouseAdapter redispatchAdapter;
private static Color INNER_COLOR = new Color(30, 30, 30, 127);
private static Color OUTER_COLOR = new Color(90, 90, 90, 127);
private static int RADIUS = 25;
private static int DIAMETER = RADIUS * 2;
private Thread glowThread;
private static Point p = new Point(0,0);
private static GlowPanel glowPanel;
private static Boolean MOUSE_LISTEN_STATE = true;
private static Boolean GLOW_THREAD_STATE = false;
private Boolean CURRENT_STATE;
private static ButtonGroup buttonGroup;
//Start Constructor
public GlowPanel(){
//Set Attributes
this.setName("GLOW PANEL");
this.setOpaque(false);
this.setVisible(true);
//this.event
//Add MouseMouseListener
this.repaintAdapter = new RepaintMouseAdapter(this);
this.redispatchAdapter = new RedispatchAdapter();
}
//End Constructor
//Start Methods
/**
* Set state to mouseListen
*/
public synchronized void setStateMouseListen(){
if(!MOUSE_LISTEN_STATE.equals(CURRENT_STATE)){
if(this.glowThread != null){
this.glowThread.interrupt();
}
this.addMouseMotionListener(this.repaintAdapter);
this.addMouseListener(this.redispatchAdapter);
this.addMouseMotionListener(this.redispatchAdapter);
CURRENT_STATE = MOUSE_LISTEN_STATE;
}
}
/**
* Set state to off (don't do any effects)
*/
public synchronized void setStateOff(){
if(this.glowThread != null){
this.glowThread.interrupt();
}
this.removeMouseMotionListener(this.repaintAdapter);
this.removeMouseListener(this.redispatchAdapter);
this.removeMouseMotionListener(this.redispatchAdapter);
CURRENT_STATE = null;
}
/**
* Set state to GlowThread
*/
public synchronized void setStateGlowThread(){
if(!GLOW_THREAD_STATE.equals(CURRENT_STATE)){
this.removeMouseMotionListener(this.repaintAdapter);
this.removeMouseListener(this.redispatchAdapter);
this.removeMouseMotionListener(this.redispatchAdapter);
this.glowThread = new Thread(new Runnable(){
public void run(){
while(!Thread.currentThread().isInterrupted()){
try {
Thread.sleep(1000);
GlowPanel.this.p = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(p, GlowPanel.this);
GlowPanel.this.repaint();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
this.glowThread.start();
CURRENT_STATE = GLOW_THREAD_STATE;
}
}
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Point p = GlowPanel.this.p;
Shape circle = new Ellipse2D.Double(p.getX() - RADIUS, p.getY() - RADIUS, DIAMETER, DIAMETER);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(GlowPanel.INNER_COLOR);
//g2.fill(circle);
Shape circle2 = new Ellipse2D.Double(p.getX() - RADIUS + 9, p.getY() - RADIUS + 9, DIAMETER - 18, DIAMETER - 18);
//Area a = new Area(circle);
//a.subtract(new Area(circle2));
g2.fill(circle);
g2.setColor(GlowPanel.OUTER_COLOR);
g2.fill(circle2);
}
/**
* Create GUI used to show example
*/
public static void createGUI(){
JFrame jFrame = new JFrame();
//GlowPanel
GlowPanel.glowPanel = new GlowPanel();
//ContentPanel
JPanel contentPanel = new JPanel();
GridBagLayout gBL = new GridBagLayout();
gBL.columnWidths = new int[]{100, 100, 100};
gBL.rowHeights = new int[]{200, 50};
contentPanel.setLayout(gBL);
//Initial Constraints
GridBagConstraints gBC = new GridBagConstraints();
gBC.fill = GridBagConstraints.BOTH;
gBC.gridx = 0;
gBC.gridy = 0;
gBC.weightx = 1;
gBC.weighty = 1;
gBC.gridwidth = 3;
//Hover Panel
JPanel hoverPanel = new JPanel();
hoverPanel.setOpaque(true);
hoverPanel.setBackground(Color.blue);
hoverPanel.addMouseListener(new MouseAdapter(){
@Override
public void mouseClicked(MouseEvent e){}
@Override
public void mousePressed(MouseEvent e){}
@Override
public void mouseReleased(MouseEvent e){}
@Override
public void mouseEntered(MouseEvent e){
hoverPanel.setBackground(Color.yellow);
}
@Override
public void mouseExited(MouseEvent e){
hoverPanel.setBackground(Color.blue);
}
});
contentPanel.add(hoverPanel, gBC);
//Create Buttons
JToggleButton mouseListenButton = new JToggleButton("Mouse Listen");
mouseListenButton.addActionListener(new AbstractAction(){
@Override
public void actionPerformed(ActionEvent e) {
GlowPanel.glowPanel.setStateMouseListen();
}
});
gBC.gridy++;
gBC.weighty = 1;
gBC.gridwidth = 1;
contentPanel.add(mouseListenButton, gBC);
JToggleButton glowThreadButton = new JToggleButton("GlowThread Button");
glowThreadButton.addActionListener(new AbstractAction(){
@Override
public void actionPerformed(ActionEvent e) {
GlowPanel.glowPanel.setStateGlowThread();
}
});
gBC.gridx++;
contentPanel.add(glowThreadButton, gBC);
JToggleButton offButton = new JToggleButton("Off Button");
offButton.addActionListener(new AbstractAction(){
@Override
public void actionPerformed(ActionEvent e) {
GlowPanel.glowPanel.setStateOff();
}
});
gBC.gridx++;
contentPanel.add(offButton, gBC);
GlowPanel.buttonGroup = new ButtonGroup();
GlowPanel.buttonGroup.add(mouseListenButton);
GlowPanel.buttonGroup.add(glowThreadButton);
GlowPanel.buttonGroup.add(offButton);
jFrame.setContentPane(contentPanel);
jFrame.setGlassPane(GlowPanel.glowPanel);
jFrame.getGlassPane().setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
GlowPanel.createGUI();
}
});
}
@Override
public boolean isOptimizedDrawingEnabled(){
return false;
}
//End Methods
public static class RepaintMouseAdapter extends MouseAdapter{
private JComponent jC;
public RepaintMouseAdapter(JComponent jC){
this.jC = jC;
}
@Override
public void mouseMoved(MouseEvent e){
GlowPanel.p = e.getPoint();
this.jC.repaint();
}
}
public static class RedispatchAdapter extends MouseAdapter{
private JComponent lastComponentEntered;
@Override
public void mouseClicked(MouseEvent e){
this.dispatchEvent(e);
}
@Override
public void mousePressed(MouseEvent e){
this.dispatchEvent(e);
}
@Override
public void mouseReleased(MouseEvent e){
this.dispatchEvent(e);
}
@Override
public void mouseEntered(MouseEvent e){
this.dispatchEvent(e);
System.out.println("MOUSE ENTERED");
}
@Override
public void mouseExited(MouseEvent e){
this.dispatchEvent(e);
}
@Override
public void mouseWheelMoved(MouseWheelEvent e){
this.dispatchEvent(e);
}
@Override
public void mouseDragged(MouseEvent e){
this.dispatchEvent(e);
}
@Override
public void mouseMoved(MouseEvent e){
this.dispatchEvent(e);
}
private void dispatchEvent(MouseEvent e) {
Component glass = (Component) e.getSource();
Point glassPanePoint = e.getPoint();
Container container = glass.getParent();
Point containerPoint = SwingUtilities.convertPoint(glass,
glassPanePoint, container);
if (containerPoint.y < 0) { // we're not in the content pane
// Could have special code to handle mouse events over
// the menu bar or non-system window decorations, such as
// the ones provided by the Java look and feel.
} else {
// The mouse event is probably over the content pane.
// Find out exactly which component it's over.
JComponent component = (JComponent) GlowPanel.getDeepestComponentAt(
container, containerPoint.x, containerPoint.y);
//System.out.println(component);
if (component != null) {
// Forward events to component below
Point componentPoint = SwingUtilities.convertPoint(
glass, glassPanePoint, component);
if(e instanceof MouseWheelEvent){
MouseWheelEvent wE = (MouseWheelEvent) e;
component.dispatchEvent(new MouseWheelEvent(component, wE.getID(), wE.getWhen(),
wE.getModifiers(), componentPoint.x, componentPoint.y, wE.getClickCount(),
wE.isPopupTrigger(), wE.getScrollType(), wE.getScrollAmount(), wE.getWheelRotation()));
} else{
component.dispatchEvent(new MouseEvent(component, e
.getID(), e.getWhen(), e.getModifiers(),
componentPoint.x, componentPoint.y, e
.getClickCount(), e.isPopupTrigger()));
}
if(this.lastComponentEntered != null && !this.lastComponentEntered.equals(component)){
System.out.println(!this.lastComponentEntered.equals(component));
}
if(this.lastComponentEntered != null && !this.lastComponentEntered.equals(component)){
e.getComponent().setCursor(component.getCursor());
component.dispatchEvent(new MouseEvent(component, MouseEvent.MOUSE_ENTERED, e.getWhen(),
e.getModifiers(), componentPoint.x, componentPoint.y,
e.getClickCount(), e.isPopupTrigger()));
Point lastComponentPoint = SwingUtilities.convertPoint(
glass, glassPanePoint, this.lastComponentEntered);
this.lastComponentEntered.dispatchEvent(new MouseEvent(this.lastComponentEntered,
MouseEvent.MOUSE_EXITED, e.getWhen(),
e.getModifiers(), lastComponentPoint.x, lastComponentPoint.y,
e.getClickCount(), e.isPopupTrigger()));
}
this.lastComponentEntered = component;
System.out.println(this.lastComponentEntered);
}
}
e.getComponent().repaint();
}
}
public static Component getDeepestComponentAt(Component parent, int x, int y) {
if (!parent.contains(x, y)) {
return null;
}
if (parent instanceof Container) {
Component components[] = ((Container)parent).getComponents();
for (Component comp : components) {
if (comp != null && comp.isVisible() && !(comp instanceof GlowPanel) && !(comp instanceof JLabel)) {
Point loc = comp.getLocation();
if (comp instanceof Container) {
comp = getDeepestComponentAt(comp, x - loc.x, y - loc.y);
} else {
comp = comp.getComponentAt(x - loc.x, y - loc.y);
}
if (comp != null && comp.isVisible()) {
return comp;
}
}
}
}
return parent;
}
}
希望这行得通。我基本上将项目中的内容复制粘贴到示例类中。
【问题讨论】:
-
您需要将鼠标事件重新发送到父容器 - for example
-
对于它的价值,我会专注于使用
MouseMoitionListener并通过玻璃窗格的mouseMoved事件跟踪鼠标位置并重新调度(转换后的)@987654325 @ 到父容器