2

I need to animate some selected menuitems and their perent menus. So far the solution I got that works well with menuitems but doesn't work well with their parent menu. (Solution source :Is it possible to make some items in the menu to fade in with 500 ms onset delay in Java?). To do the same effect on JMenus, I tried to do the same thing that was in the solution for animating JMenuItems (overriding addNotify() and removeNotify() check FadeMenu Class).

My code is following (yo can run this code):

AnimationEngine.java

package aphemeralmenudemo;

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 FadeMenuDemo {

private AnimationEngine engine;

public static void main(String[] args) {
    new FadeMenuDemo();
}

public FadeMenuDemo() {
    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 Level1 = new JMenu("Level 1");
            Level1.add("111");
            Level1.add("222");
            JMenu Level2 = new FadeMenu("Flip");
            Level1.add(Level2);

            Level2.add(new FadeMenuItem("Fade 1"));
            Level2.add(new FadeMenuItem("Fade 2"));

            Level2.add("Static 1");
            Level2.add("Static 2");





            mb.add(Level1);

            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);
        }
    });
}

TestPane.java

public class TestPane extends JPanel {

    public TestPane() {
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }
}

FadeMenuItem.java

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();
        }
    }
}

FadeMenu.java

public class FadeMenu extends JMenu{

    public FadeMenu(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();
        }
    }

}

TimingListener.java

public interface TimingListener {

    public void timingEvent();
}

AnimationEngine.java

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();
    }
}

Overridden methods:

    @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();
        }
    }

Initially I looks fine. The menu text appeared in animation but when a sub item under that animated menu selected the menu text again animated. This is the problem, I don't want to animate the menu text upon selecting it's submenu. I really need help in this regard. Thanks in advance.

Community
  • 1
  • 1

1 Answers1

2

This is the problem, I don't want to animate the menu text upon selecting it's submenu.

Call repaint only when if menu is not selected.

sample code:

public FadeMenu(String text) {
    super(text);
    engine.addTimingListener(new TimingListener() {
        @Override
        public void timingEvent() {
            // This condition is added here to stop animation when selected 
            if (!isSelected()) {
                repaint();
            }
        }
    });
}
Braj
  • 46,415
  • 5
  • 60
  • 76
  • Many Many Thanks. I worked. I was stuck with this for about a day. You saved me a lot. Again Thanks. – Adnan Alam Khan Jul 17 '14 at 08:22
  • Another way to have the same effect is to create a separate AminatorEngine instance and use it to the fade menu along with your modification. (Only your modification also generates some odd menu appearance) – Adnan Alam Khan Jul 19 '14 at 19:48