JButton悬停效果动画。通过鼠标侦听器更改不透明度

时间:2014-09-01 08:17:34

标签: java swing animation jbutton rollover

我想用很好的过渡效果做JButton。我编写了一个由JButton扩展的类,并添加到自定义MouseAdapter。它几乎可以工作,但如果不透明度应该为0,那么我的一个BufferedImage就不会消失。

这是我的所有源代码:

public class ImageHoverButton extends JButton {

    public class MouseListener extends MouseAdapter
      {
        public void mouseExited(MouseEvent me)
        {
          new Thread(new Runnable()
          {
            public void run()
            {
              for (float i = 1f; i >= 0f; i -= .03f)
              {
                setOpacity(i);
                try
                {
                  Thread.sleep(10);
                }
                catch (Exception e)
                {
                }
              }
            }
          }).start();
        }

        public void mouseEntered(MouseEvent me)
        {
          new Thread(new Runnable()
          {
            public void run()
            {
              for (float i = 0f; i <= 1f; i += .03f)
              {
                  setOpacity(i);
                try
                {
                  Thread.sleep(10);
                }
                catch (Exception e)
                {
                }
              }
            }
          }).start();
        }

        public void mousePressed(MouseEvent me)
        {
          new Thread(new Runnable()
          {
            public void run()
            {
              for (float i = 1f; i >= 0.6f; i -= .1f)
              {
                  setOpacity(i);
                try
                {
                  Thread.sleep(1);
                }
                catch (Exception e)
                {
                }
              }
            }
          }).start();
        }
      }


    private static final long serialVersionUID = 1L;

    private BufferedImage imgBottom;
    private BufferedImage imgHover;
    private BufferedImage imgHoverRGB;

    // filter to imgInActive
    float[] scales = { 1f, 1f, 1f, 0f};
    float[] offsets = new float[4];
    RescaleOp rop = new RescaleOp(scales, offsets, null);

    /**
     * Constructor for image path 
     * @param img
     * @param x
     * @param y
     */

    public ImageHoverButton(String imgBottomPath, String imgHoverPath, int x, int y) {

        try {
            this.imgBottom = ImageIO.read(new File(imgBottomPath));
            this.imgHover = ImageIO.read(new File(imgHoverPath));


            imgHoverRGB = new BufferedImage(imgHover.getWidth(null), 
                                            imgHover.getHeight(null), 
                                            BufferedImage.TYPE_INT_ARGB);
            Graphics g = imgHoverRGB.getGraphics();
            g.drawImage(imgHover, 0, 0, null);

        } catch (IOException e) {
        } 
        this.setBounds(x, y, imgBottom.getWidth() + 40 , imgBottom.getHeight() + 50);
        addMouseListener(new MouseListener());
        setOpacity(0f);
        setOpaque(false);
        setBorderPainted(false);
        setRolloverEnabled(false);
        setCursor(new Cursor(Cursor.HAND_CURSOR));
        setLayout(null);



    }

    public void setOpacity(float opacity) {
        scales[3] = opacity;
        rop = new RescaleOp(scales, offsets, null);
        repaint();
    }

     public void paint(Graphics g) {
            Graphics2D g2d = (Graphics2D)g;
            g2d.drawImage(imgBottom, 50, 50, null);
            g2d.drawImage(imgHoverRGB, rop, 0, 0);
     }

}

知道如何改善这个吗?

2 个答案:

答案 0 :(得分:2)

我对RescaleOp不太熟悉,并且不记得曾经使用过这个。但似乎在这种情况下应用它的结果有些出乎意料。

作为替代方案,您可以考虑使用AlphaComposite。然后,实现所需效果所需的最小修改将是更改行

g2d.drawImage(imgHoverRGB, rop, 0, 0);

g2d.setComposite(AlphaComposite.getInstance(
    AlphaComposite.SRC_OVER, scales[3]));
g2d.drawImage(imgHoverRGB, 0, 0, null);

但是,代码还有其他几个问题:

  • 不要覆盖paint。相反,覆盖paintComponent
  • 不要在组件上调用setBounds(特别是在构造函数中)。展示位置应由布局管理员完成
  • 不要默默地吞下例外
  • 不要在按钮的构造函数中加载图像
  • 正确实施getPreferredSize
  • 由于鼠标移动,不会产生数百个线程。 (当您快速移动鼠标时,您将运行多个线程 - 其中一些线程会增加不透明度,其中一些线程会降低不透明度)

我创建了一个示例,显示了一种可能的方法:它包含一个OpacityAnimator,它允许两个不透明度之间的转换,具有预定义的延迟(以毫秒为单位)。当使用鼠标悬停按钮时,此动画制作器用于增加前景图像的不透明度,当鼠标离开按钮时,可以减少此动画。

(请注意,这可以进一步推广,并且有许多可能的&#34;配置设置&#34;(如转换延迟)可以公开,但这只是作为一个例子)

import java.awt.AlphaComposite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class HoverButtonTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    createAndShowGUI();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        });
    }

    private static void createAndShowGUI() throws IOException
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        BufferedImage backgroundImage = loadImage("background.png");
        BufferedImage foregroundImage = loadImage("foreground.png");
        f.getContentPane().setLayout(new FlowLayout());
        f.getContentPane().add(
            new ImageHoverButton(backgroundImage, foregroundImage));

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static BufferedImage loadImage(String path) throws IOException
    {
        return convertToARGB(ImageIO.read(new File(path)));
    }

    public static BufferedImage convertToARGB(BufferedImage image)
    {
        BufferedImage newImage = new BufferedImage(image.getWidth(),
            image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }

}

class ImageHoverButton extends JButton
{
    private class MouseHoverListener extends MouseAdapter
    {
        @Override
        public void mouseExited(MouseEvent me)
        {
            opacityAnimator.changeOpacity(0.0f, 250);
        }

        @Override
        public void mouseEntered(MouseEvent me)
        {
            opacityAnimator.changeOpacity(1.0f, 1000);
        }

        @Override
        public void mousePressed(MouseEvent me)
        {
            opacityAnimator.changeOpacity(0.5f, 50);
        }
    }

    private class OpacityAnimator
    {
        private final int DELAY_MS = 10;
        private final Timer timer;

        private float targetOpacity;
        private float currentOpacity;
        private float opacityStep;

        OpacityAnimator()
        {
            timer = new Timer(DELAY_MS, new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent e)
                {
                    if (currentOpacity > targetOpacity)
                    {
                        currentOpacity += opacityStep;
                        currentOpacity = Math.max(
                            currentOpacity, targetOpacity);
                    }
                    else if (currentOpacity < targetOpacity)
                    {
                        currentOpacity += opacityStep;
                        currentOpacity = Math.min(
                            currentOpacity, targetOpacity);
                    }
                    if (currentOpacity == targetOpacity)
                    {
                        timer.stop();
                    }
                    setOpacity(currentOpacity);
                }
            });
        }

        void changeOpacity(float targetOpacity, int durationMs)
        {
            timer.stop();
            this.targetOpacity = targetOpacity;

            float delta = targetOpacity - currentOpacity;
            if (durationMs > 0)
            {
                opacityStep = (delta / durationMs) * DELAY_MS;
            }
            else
            {
                opacityStep = delta;
            }
            timer.start();
        }
    }

    private final OpacityAnimator opacityAnimator;
    private final BufferedImage backgroundImage;
    private final BufferedImage foregroundImage;
    private float opacity = 0.0f;

    public ImageHoverButton(BufferedImage backgroundImage,
        BufferedImage foregroundImage)
    {
        this.backgroundImage = backgroundImage;
        this.foregroundImage = foregroundImage;
        this.opacityAnimator = new OpacityAnimator();
        addMouseListener(new MouseHoverListener());
        setOpaque(false);
        setBorderPainted(false);
        setRolloverEnabled(false);
        setCursor(new Cursor(Cursor.HAND_CURSOR));
    }

    @Override
    public Dimension getPreferredSize()
    {
        if (super.isPreferredSizeSet())
        {
            return super.getPreferredSize();
        }
        int w = Math
            .max(backgroundImage.getWidth(), foregroundImage.getWidth());
        int h = Math.max(backgroundImage.getHeight(),
            foregroundImage.getHeight());
        return new Dimension(w, h);
    }

    public void setOpacity(float opacity)
    {
        this.opacity = opacity;
        repaint();
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D) gr;
        g.drawImage(backgroundImage, 0, 0, null);
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
            opacity));
        g.drawImage(foregroundImage, 0, 0, null);
    }
}

答案 1 :(得分:0)

不要从其他线程访问Swing组件。请使用摇摆计时器。 见How to use swing timers