Pulsing image-button in Java

791 views Asked by At

I have created a simple musical metronome in Java. It starts and stops by pressing a button that has the shape of a butterfly. I would love to add a visual effect of the tempo by making the button/butterfly appear and disappear together with the metronome beat.

Would java.util.Timer be the way to go? Would that work with an image that is a button and that needs to keep its functions while pulsing?

Thank you so very much for suggestions and congratulations on the community.

1

There are 1 answers

1
Hovercraft Full Of Eels On BEST ANSWER

Would java.util.Timer be the way to go?

Yes, this could easily be used to display a pulsating image. When I've done this before, I've created an array of sine wave constants in my constructor and have used them to set an alpha composite inside of the timer.

Would that work with an image that is a button and that needs to keep its functions while pulsing?

It's possible although a little more difficult since the button doesn't really render itself, but rather its componentUI, here one of the subclasses of the BasicButtonUI.


Well, I can do it without messing with the componentUI, but I'm not sure if this is the correct way:

import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


@SuppressWarnings("serial")
public class PulsingButton extends JPanel {
   private static final int MAX_ALPHAS = 60;
   private float alpha = 1.0f;
   private JSpinner beatsPerMinSpinner = new JSpinner(new SpinnerNumberModel(60,  30, 120, 1));
   private JButton button = new JButton("Button") {
      @Override
      protected void paintComponent(Graphics g) {
         Graphics2D g2 = (Graphics2D) g;
         g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha));
         super.paintComponent(g2);
      };
   };
   private float[] alphas = new float[MAX_ALPHAS];
   private Timer timer;

   public PulsingButton() {
      beatsPerMinSpinner.addChangeListener(new ChangeListener() {

         @Override
         public void stateChanged(ChangeEvent e) {
            int value = ((Integer) beatsPerMinSpinner.getValue()).intValue();
            setTimerDelay(value);
         }
      });

      add(new JLabel("Beats Per Minute:"));
      add(beatsPerMinSpinner);


      button.addActionListener(new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent e) {
            System.out.println("Hello!");
         }
      });
      add(button);
      for (int i = 0; i < alphas.length; i++) {
         double theta = (Math.PI * 2 * i) / alphas.length;
         alphas[i] = (float) ((Math.cos(theta) + 1) / 2.0);
      }

      int bpm = ((Integer) beatsPerMinSpinner.getValue()).intValue();
      timer = new Timer(setTimerDelay(bpm), new TimerListener());
      timer.start();

      System.out.println(setTimerDelay(bpm) + "");
   }

   private int setTimerDelay(int bpm) {
      int milisecondsInMinute = 60 * 1000;
      int delay = milisecondsInMinute / (bpm * alphas.length);
      if (timer != null) {
         timer.setDelay(delay);
      }
      return delay;
   }

   private class TimerListener implements ActionListener {
      int index = 0;

      @Override
      public void actionPerformed(ActionEvent arg0) {
         alpha = alphas[index];
         index++;
         index %= alphas.length;
         repaint();
      }
   }

   private static void createAndShowGui() {
      PulsingButton mainPanel = new PulsingButton();

      JFrame frame = new JFrame("PulsingButton");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}