I have an unusual issue in which a componentListener/Adapter is being disabled whenever the mouseListener/Adapter detects an event. In the code below, the componentMoved()
method override works perfectly until the mouseClicked()
method override is triggered in the MouseAdapter()
. Any ideas how to fix this?
public AlertScroller(String msg,Color col) {
addComponentListener(new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
setShape(new Rectangle2D.Double(0, 0, getWidth(), newHeight));
if(!isVisible())
setVisible(true);
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent click) {
if(SwingUtilities.isLeftMouseButton(click)) {
autoClear = false;
scrollOff();
}
}
});
setAlwaysOnTop(true);
JPanel panel = new JPanel();
panel.setBorder(compound);
panel.setBackground(col);
JLabel imgLbl = new JLabel(msg);
imgLbl.setFont(new Font("Arial",Font.BOLD,30));
panel.add(imgLbl);
setContentPane(panel);
pack();
}
The class that this method is in extends JWindow
.
Edit: Added the scrollOff()
method's code.
public void scrollOff() {
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
int taskBar = scnMax.bottom;
int x = screenSize.width - getWidth();
int yEnd = screenSize.height - taskBar;
int yStart = this.getBounds().y;
setLocation(x,yStart);
int current = yStart;
newHeight = this.getBounds().height;
while(current < yEnd) {
current+=2;
newHeight = yEnd - current;
setLocation(x,current);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
dispose();
Main.alertControl.setAlertActive(false);
}
This is basically the exact reverse of the scrollOn() method and works perfectly fine as long as it's triggered by any means other than through the Listener
.
Edit 2: Fixed code below thanks to MadProgrammer's advice
public void scrollOff() {
x = screenSize.width - getWidth();
yEnd = screenSize.height - taskBar;
yStart = this.getBounds().y;
setLocation(x,yStart);
current = yStart;
newHeight = this.getBounds().height;
ActionListener action = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
if(current < yEnd) {
current+=2;
newHeight = yEnd - current;
setLocation(x,current);
} else {
timer.stop();
}
}
};
timer = new Timer(30, action);
timer.setInitialDelay(0);
timer.start();
Main.alertControl.setAlertActive(false);
}
I also updated the AlertScroller()
constructor method with an else if
to hide the window properly when I'm done:
addComponentListener(new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
setShape(new Rectangle2D.Double(0, 0, getWidth(), newHeight));
if(!isVisible())
setVisible(true);
else if(getBounds().y == screenSize.height - taskBar)
setVisible(false);
}
});
Placing setVisible(false)
anywhere else caused the window to become visible again.
The while loop
is dangerous, the Thread.sleep
is dangerous and dispose is just plain scary...
You're violating the single thread rules of Swing and blocking the Event Dispatching Thread.
See Concurrency in Swing for more details
dispose
may be disposing of the native peer that is associated with the window, causing no end of issues...
See JWindow#dispose
for more details.
Assuming, you're trying to slide the window on/off screen, you should utilise a Swing Timer
, which when triggered, would update the position of the Window until it reaches it's target point and which time you would simply change the window visibility. This assumes you want to reuse the instance of the window..
See How to Use Swing Timers for more details.