How to close JFrame after user inactivity? - java

How would I go about closing a JFrame after user inactivity?
So far I have,
Thread.sleep(10000);
I would greatly appreciate it if someone could give me code to do this?
I am new to Java and want to know more about system security

This is an example of Braj's idea using a javax.swing.Timer.
It simplifies the process, as you don't need to monitor the time between events and ensures that when the timer is triggered, the event occurs within the Event Dispatching Thread, further reducing the complexity.
Also note that I included the AWTEvent.MOUSE_MOTION_EVENT_MASK and AWTEvent.MOUSE_WHEEL_EVENT_MASK events for good measure ;)
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AutoClose {
public static void main(String[] args) {
new AutoClose();
}
private Timer timer;
private JLabel label;
private JFrame frame;
public AutoClose() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
label = new JLabel("Waiting...");
frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
private int count;
#Override
public void eventDispatched(AWTEvent event) {
Object source = event.getSource();
if (source instanceof Component) {
Component comp = (Component) source;
Window win = null;
if (comp instanceof Window) {
win = (Window) comp;
} else {
win = SwingUtilities.windowForComponent(comp);
}
if (win == frame) {
timer.restart();
label.setText("Interrupted..." + (++count));
}
}
}
}, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK);
timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frame.dispose();
}
});
// You could use a WindowListener to start this
timer.start();
}
});
}
}

Try this one
Steps to follow:
Its listening for key event as well as mouse event.
A new thread is started that will check.
If the time difference is more than specified time (10 sec in below sample code) then dispose the window.
That's all.
Here is the sample code:
private long time;
...
long eventMask = AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK;
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent e) {
time = System.currentTimeMillis();
}
}, eventMask);
time = System.currentTimeMillis();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
if (System.currentTimeMillis() - time > 10000) {
widnow.dispose();
break;
}
}
}
}).start();

Related

Progress Bar value is not updating [duplicate]

I have made a very simple code to show it here, i have a button that should show a JDialog to check the progress status, i am using the invoke late to go through EDT and my loop isn't in the run method, so why isn't my bar updating ?
here is the code
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class JBarEx extends JFrame {
private JTextField progStatus = new JTextField("Undefined");
private JButton dialogBtn = new JButton("Show Progression dialog");
final JDialog dlg = new JDialog((JFrame) null, "prog Title", false);
final JProgressBar dpb = new JProgressBar(0, 100);
public JBarEx() {
JPanel pan = new JPanel();
dialogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
showProgress();
}
});
progStatus.setEditable(false);
pan.add(progStatus);
pan.add(dialogBtn);
setContentPane(pan);
this.setSize(200, 100);
setVisible(true);
}
public void showProgress() {
dlg.add(BorderLayout.CENTER, dpb);
dlg.add(BorderLayout.NORTH, new JLabel("prog message"));
dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(null);
dlg.setVisible(true);
for (int i = 0; i < 100; i++) {
final int ii = i;
try {
Thread.sleep(25);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
updateBar(ii);
}
});
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void updateBar(int newValue) {
dpb.setValue(newValue);
}
public static void main(String[] args) {
JBarEx jbx = new JBarEx();
}
}
Your showProgress method is being executed within the context of the Event Dispatching Thread. The EDT is responsible for, amongst other things, processing paint requests. This means that so long as your for-loop is executing, the EDT can not process any new paint requests (or handle the invokeLater events either) as it is blocking the EDT.
While there are any number of possible ways to solve the problem, based on your code example, the simplest would be to use a SwingWorker.
It has the capacity to allow your to execute the long running task the a background thread (freeing up the EDT), but also allows you means for publishing updates (if required) so that they can be processed in the EDT and also provides handy progress notification.
For example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class SwingWorkerProgress {
public static void main(String[] args) {
new SwingWorkerProgress();
}
public SwingWorkerProgress() {
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 JProgressBar pbProgress;
private JButton start;
public TestPane() {
setBorder(new EmptyBorder(10, 10, 10, 10));
pbProgress = new JProgressBar();
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridx = 0;
gbc.gridy = 0;
add(pbProgress, gbc);
start = new JButton("Start");
gbc.gridy++;
add(start, gbc);
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start.setEnabled(false);
ProgressWorker pw = new ProgressWorker();
pw.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if (name.equals("progress")) {
int progress = (int) evt.getNewValue();
pbProgress.setValue(progress);
repaint();
} else if (name.equals("state")) {
SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
switch (state) {
case DONE:
start.setEnabled(true);
break;
}
}
}
});
pw.execute();
}
});
}
}
public class ProgressWorker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
for (int i = 0; i < 100; i++) {
setProgress(i);
try {
Thread.sleep(25);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
}
Check out Concurrency in Swing for more details
Even if you fix the loop as others have pointed out, you'd still block the event dispatch thread. The for loop is run in showProgress() which is called from an event listener. The updates are pushed to the event queue, but that does not get processed until the loop has completed.
Use a Swing Timer instead. Something like this:
Timer timer = new Timer(25, new ActionListener() {
private int position;
#Override
public void actionPerformed(ActionEvent e) {
position++;
if (position < lastPosition) {
updateBar(position);
} else {
((Timer) e.getSource).stop();
}
}
});
timer.start();
where lastPosition would be the state where you want the progress bar to stop.
Unrelated to that bug, but a bug still, you should not create swing components outside the event dispatch thread. It's best to do it right from the start:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JBarEx jbx = new JBarEx();
}
});
}
for (int i = 0; i < 0; i++) {
You will never enter this code so will never call the updateBar(..) method
i needs to be greater than 0 in this case. If it is 1 then updateBar will be called once, if 2 then updateBar will be called twice etc
Also rather than doing
Thread.sleep(25);
take a look at java executors as these will help with your scheduling and remove the need for the sleep

JProgressBar on WindowsLookAndFeel only updates in increments of 5%

Take this code:
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
JFrame frame = new JFrame();
JProgressBar jpb = new JProgressBar();
frame.add(jpb);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jpb.setMaximum(1000);
for(int i = 0; i < 1000; i++) {
System.out.println("Processing " + i);
jpb.setValue(i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
and run it. You'll notice the JProgressBar graphic only updates every 5%.
Is this changeable? Can it update with more precise changes like every 1%?
(note: the default UIManager is more precise. Remove the WindowsLookAndFeel code to see).
You're at risk of blocking the Event Dispatching Thread (Thread.sleep) or you are violating the single thread rules of Swing by updating the UI outside of the Event Dispatching Thread
Take a look at Concurrency in Swing for more details
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JProgressBar pb = new JProgressBar();
pb.setStringPainted(true);
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
int progress = Math.round((index / 1000f) * 100f);
setProgress(progress);
Thread.sleep(50);
}
return null;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
SwingWorker worker = (SwingWorker) evt.getSource();
if ("progress".equals(evt.getPropertyName())) {
pb.setValue(worker.getProgress());
}
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(pb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
worker.execute();
}
});
}
}
Updated
So, after digging about the WindowProgressBarUI, it would seem that, that's the way it's designed to work...that is, it paints in chunks/blocks.
You can use setStringPainted as I did in the example, which uses a different painting branch, but this might not suit your requirements
You can have a read through JProgressBar: low values will not be displayed for more details, but essentially, this is how the UI delegate is designed, so as to meet the requirements of the OS look and feel requirements

Java Swing Timer Countdown

I have to make a countdown program which also shows the tenths of a second;
for example from a 10.0 second countdown, it should display 9.9s, 9.8s, ... 0.0s
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
timer.start();
timer2.start();
}
Double timeLeft=5000; //5 seconds
Timer timer=new Timer(1,countDown);
Timer timer2=new Timer(1000,countDown2);
ActionListener countDown=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timeLeft--;
SimpleDateFormat df=new SimpleDateFormat("mm:ss:S");
jLabel1.setText(df.format(timeLeft));
if(timeLeft<=0)
{
timer.stop();
}
}
};
what happens is it's taking more than 5 seconds to finish the 5 seconds.
I compared the code above with another Timer
int timeLeft2=5;
ActionListener countDown2=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timeLeft2--;
jLabel2.setText(String.valueOf(timeLeft2));
if(timeLeft2<=0)
{
time2.stop();
}
}
};
is it natural that they don't get the same?
The time between ticks (the time when actionPerfomed is called) is variable, it only guarantees that it will be at least n milliseconds
Instead of relying on some counter, which could become unreliable over time, you should try and calculate the difference between the time the Timer was started and the current time, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CountDown {
public static void main(String[] args) {
new CountDown();
}
public CountDown() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
private long startTime = -1;
private long duration = 5000;
private JLabel label;
public TestPane() {
setLayout(new GridBagLayout());
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime < 0) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long clockTime = now - startTime;
if (clockTime >= duration) {
clockTime = duration;
timer.stop();
}
SimpleDateFormat df = new SimpleDateFormat("mm:ss:SSS");
label.setText(df.format(duration - clockTime));
}
});
timer.setInitialDelay(0);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (!timer.isRunning()) {
startTime = -1;
timer.start();
}
}
});
label = new JLabel("...");
add(label);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Updating the label probably takes more that 1ms, which is why it can't keep up. If you only need to display tenths of a second, simply have your timer update less often.
ActionListener countDown=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timeLeft -= 100;
SimpleDateFormat df=new SimpleDateFormat("mm:ss:S");
jLabel1.setText(df.format(timeLeft));
if(timeLeft<=0)
{
timer.stop();
}
}
};
Timer timer=new Timer(100, countdown);

Stopping Thread on Mouse Hover Event in Swing

I have a desktop application in which I am showing one Frame as notification but I want to stop that thread when mouse hover to the notification frame. Then how could I can do that?
Here current code:
final Notification not = new Notification();
Notification.notification_name.setText(msg[1]);
final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.schedule(new Runnable() {
public void run() {
not.setVisible(false); //should be invoked on the EDT
not.dispose();
}
}, 6, TimeUnit.SECONDS);
It's showing and Exit in 6 sec.
The code that I am trying.
final Notification not = new Notification();
not.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
try {
super.mouseEntered(e);
System.out.println("Mouse Entered");
//s.wait();
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception x) {
x.printStackTrace();
}
}
#Override
public void mouseExited(MouseEvent e) {
try {
super.mouseExited(e);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.schedule(new Runnable() {
public void run() {
not.setVisible(false); //should be invoked on the EDT
not.dispose();
}
}, 6, TimeUnit.SECONDS);
In this code Notification frame is showing till 6 sec but if user hover that frame then it should be Show till user mouse exit from that Frame.
Whenever you deal with anything that might affect the UI in some way, you need to be careful and ensure that everything is done within the context of the EDT.
javax.swing.Timer allows you to setup a callback at some time in the future that when triggered, will be called within the context of the EDT
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class TimeExample {
public static void main(String[] args) {
new TimeExample();
}
public TimeExample() {
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 GridBagLayout());
frame.add(new TestPane());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
public TestPane() {
setBorder(new LineBorder(Color.BLACK));
timer = new Timer(6000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setBackground(Color.RED);
}
});
timer.setRepeats(false);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (timer.isRunning()) {
timer.stop();
setBackground(Color.BLUE);
}
}
#Override
public void mouseExited(MouseEvent e) {
if (!timer.isRunning()) {
timer.restart();
setBackground(UIManager.getColor("Panel.background"));
}
}
/**
* Testing purposes only!!
*
* #param e
*/
#Override
public void mouseClicked(MouseEvent e) {
setBackground(UIManager.getColor("Panel.background"));
timer.restart();
}
};
addMouseListener(ma);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
}
Have a look at How to Use Swing Timers for more details

Is it possible to make some items in the menu to fade in with 500 ms onset delay in Java?

I have a JMenu of 16 JMenuItems, of which I want 3 items to be displayed upfront and the rest 13 items to fade in with a 500 ms delay. Is there a way to do this animation in Java?
This is not as easy as it sounds.
Basically I originally thought "I'll attach a popup listener to the popup menu that the menu items are added to"...but apparently this doesn't work so well. The menu popup is built dynamically on demand. Makes sense, but it's still a pain.
So instead, I've found that if I wait for addNotify I can simply start the animation engine.
The animation engine is a simple concept. It has a javax.swing.Timer that ticks at a regular interval. Coupled with a start time and a duration, we can calculate the progress of the animation and generate the alpha value as required.
The only thing left is then to notify all the interested parties that the animation has changed and voila...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class FadeMenu {
private AnimationEngine engine;
public static void main(String[] args) {
new FadeMenu();
}
public FadeMenu() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
engine = new AnimationEngine();
JMenuBar mb = new JMenuBar();
JMenu flip = new JMenu("Flip");
flip.add("Static 1");
flip.add("Static 2");
flip.add("Static 3");
flip.add(new FadeMenuItem("Fade 1"));
flip.add(new FadeMenuItem("Fade 2"));
flip.add(new FadeMenuItem("Fade 3"));
flip.add(new FadeMenuItem("Fade 4"));
mb.add(flip);
JFrame frame = new JFrame("Testing");
frame.setJMenuBar(mb);
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 {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class FadeMenuItem extends JMenuItem {
public FadeMenuItem(String text) {
super(text);
engine.addTimingListener(new TimingListener() {
#Override
public void timingEvent() {
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(engine.getAlpha()));
super.paintComponent(g2d);
g2d.dispose();
}
#Override
public void removeNotify() {
Container parent = getParent();
if (parent instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) parent;
engine.stop();
}
super.removeNotify();
}
#Override
public void addNotify() {
super.addNotify();
Container parent = getParent();
if (parent instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) parent;
engine.restart();
}
}
}
public interface TimingListener {
public void timingEvent();
}
public class AnimationEngine {
private Timer fade;
private float alpha;
private long startTime;
private long duration = 1000;
private List<TimingListener> listeners;
public AnimationEngine() {
listeners = new ArrayList<>(5);
fade = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed >= duration) {
((Timer) e.getSource()).stop();
alpha = 1f;
} else {
alpha = (float) elapsed / (float) duration;
}
fireTimingEvent();
}
});
fade.setRepeats(true);
fade.setCoalesce(true);
fade.setInitialDelay(500);
}
public void addTimingListener(TimingListener listener) {
listeners.add(listener);
}
public void removeTimingListener(TimingListener listener) {
listeners.add(listener);
}
protected void fireTimingEvent() {
for (TimingListener listener : listeners) {
listener.timingEvent();
}
}
public void restart() {
fade.stop();
alpha = 0;
fireTimingEvent();
startTime = System.currentTimeMillis();
fade.start();
}
public float getAlpha() {
return alpha;
}
public void stop() {
fade.stop();
}
}
}
While this works on Windows, I'd be concerned that it might not work on other platforms, as the means by which the menus are generated are controlled (in part) by the UI delegate. This could become very messy, very quickly
start a timer to fire an event to fade in

Categories