2014-09-01 4 views
0

Я хотел бы сделать JButton с хорошим эффектом перехода. Я пишу класс, который распространяется JButton и добавляет к нему пользовательский MouseAdapter. Он почти работает, но если непрозрачность должна иметь 0, мой BufferedImage не исчезает.JButton hover эффект анимация. Изменение прозрачности с помощью прослушивателя мыши

Вот мой весь исходный код:

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

} 

Есть идеи, как улучшить это?

+0

Для лучшего помогите раньше, опубликуйте [MCVE] (http://stackoverflow.com/help/mcve) (минимальный, полный, проверяемый пример). –

+0

Я не знаю, имеет ли он какой-либо differnece, но минимальное значение непрозрачности составляет только 0,01 – Gumbo

ответ

0

Не использовать компоненты Swing из других резьб. Вместо этого используйте таймер качания. См. How to use swing timers

+0

Кажется, не имеет никакого отношения к вопросу ... – Marco13

+0

Ну, он использует свой собственный поток, и я бы использовал таймер. – keuleJ

+0

Правда, с одной стороны. С другой стороны: даже при использовании таймера здесь (или просто обертывание вызовов, которые сделаны из потока в 'SwingUtilities.invokeLater'), это не решит проблему.Я предполагаю, что это связано с 'RescaleOp', но * что * актуальная проблема не совсем ясна. – Marco13

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, который допускает переход между двумя непрозрачностью с предопределенной задержкой в ​​миллисекундах. Этот аниматор используется для увеличения непрозрачности изображения переднего плана, когда кнопка наводится мышью, и уменьшает ее, когда мышь покидает кнопку.

(Обратите внимание, что это может быть обобщен далее, и существует множество возможных «Параметры конфигурации» (например, задержка перехода), которые могут быть подвержены, но это просто предназначено в качестве примера)

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

Ничего себе, спасибо. Это именно то, что я хотел. Еще раз спасибо, если у меня будет достаточно звания, я дам вам полезную информацию. – KonradPrg