从包含透明像素的图像创建自定义JButton

时间:2011-07-18 16:09:24

标签: java swing icons jbutton imageicon

阅读编辑2,了解我实际上缺少的内容

我目前正在尝试使用在photoshop中创建的具有alpha参数的图像创建一些自定义JButtons。

到目前为止,覆盖paint()方法绘制图像的工作方式是绘制按钮以显示正确的图像。不过,我想通过使其形状(可点击区域)与图像上的可见像素相同来改进它(现在,如果我绘制按钮的边框,它就是正方形)。

有没有一种简单的方法可以做到这一点,还是我必须解析图像并找到alpha像素来制作自定义边框?

我必须覆盖哪些方法才能使其按照我想要的方式工作?

另外,我将在稍后提出的另一个问题是:使用某种算法更改图像的颜色以使其看起来像人们点击它时点击它或者我更好创建第二个图像并在按钮处于活动状态时绘制该图像?

编辑:我刚读了一些其他问题,我应该重新定义paintComponent()而不是paint(),我想知道为什么重新定义paint()工作正常?

编辑2:我更改了所有内容,以确保使用带图标的默认构造函数创建JButton。我要做的是获取点击注册位置的X和Y位置并抓住该位置的图标像素并检查其alpha通道以查看它是否为0(如果是,则不执行任何操作,否则执行此操作应该采取的行动。)

问题是,alpha通道始终返回255(蓝色,红色和绿色在透明像素上为238)。在其他像素上,一切都返回它应该返回的值。

这是一个例子(如果你愿意,可以用另一个图像试试)重新创建我的问题:

public class TestAlphaPixels extends JFrame
{
  private final File FILECLOSEBUTTON = new File("img\\boutonrondX.png");  //My round button with transparent corners
  private JButton closeButton = new JButton(); //Creating it empty to be able to place it and resize the image after the button size is known


  public TestAlphaPixels() throws IOException
  {
    setLayout(null);
    setSize(150, 150);

    closeButton.setSize(100, 100);
    closeButton.setContentAreaFilled(false);
    closeButton.setBorderPainted(false);

    add(closeButton);

    closeButton.addMouseListener(new MouseListener()
      {

        public void mouseClicked(MouseEvent e)
        {
        }

        public void mousePressed(MouseEvent e)
        {
        }

        public void mouseReleased(MouseEvent e)
        {
          System.out.println("Alpha value of pixel (" + e.getX() + ", " + e.getY() + ") is: " + clickAlphaValue(closeButton.getIcon(), e.getX(), e.getY()));
        }

        public void mouseEntered(MouseEvent e)
        {
        }

        public void mouseExited(MouseEvent e)
        {
        }
      });
    Image imgCloseButton = ImageIO.read(FILECLOSEBUTTON);

    //Resize the image to fit the button
    Image newImg = imgCloseButton.getScaledInstance((int)closeButton.getSize().getWidth(), (int)closeButton.getSize().getHeight(), java.awt.Image.SCALE_SMOOTH);
    closeButton.setIcon(new ImageIcon(newImg));


  }

  private int clickAlphaValue(Icon icon, int posX, int posY) 
  {
    int width = icon.getIconWidth();
    int height = icon.getIconHeight();

    BufferedImage tempImage = (BufferedImage)createImage(width, height);
    Graphics2D g = tempImage.createGraphics();

    icon.paintIcon(null, g, 0, 0);

    g.dispose();

    int alpha = (tempImage.getRGB(posX, posY) >> 24) & 0x000000FF;

    return alpha;
  } 
  public static void main(String[] args)
  {
    try
    {
      TestAlphaPixels testAlphaPixels = new TestAlphaPixels();
      testAlphaPixels.setVisible(true);
      testAlphaPixels.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    catch(IOException ioe)
    {
      ioe.printStackTrace();
    }
  }
}

What this sample actually displays

这只是一个疯狂的猜测,但有可能当我的图像被转换为​​Icon时,它会丢失其Alpha属性,因此不会返回正确的值吗?无论如何,如果有人能真正帮助我并告诉我应该改变什么以获得正确的价值,我真的很感激。

我猜这是因为当我尝试使用原始图像时,alpha通道的值很好,但我实际上无法使用BufferedImage,因为我调整了它的大小,所以我实际上得到了图像的通道值原始大小...

6 个答案:

答案 0 :(得分:6)

我认为你走错了路。您不必覆盖paint()和paintComponent()方法。 JButton已经“知道”仅显示图像:

ImageIcon cup = new ImageIcon("images/cup.gif");
JButton button2 = new JButton(cup);

请参阅以下教程,例如:http://www.apl.jhu.edu/~hall/java/Swing-Tutorial/Swing-Tutorial-JButton.html

此外,秋千是完全定制的。您可以控制不透明度,边框,颜色等。您可能应该覆盖一些提到的方法来更改功能。但在大多数情况下,有更好,更简单的解决方案。

答案 1 :(得分:4)

由于多个答案都有好的元素,但没有一个答案是自己完成的,我会回答我自己的问题,所以其他有相同问题的人可以尝试类似的东西。

我使用扩展JButton的新类创建了我的按钮,新的构造函数将BufferedImage作为参数而不是图标。原因是当我做了像myButton.getIcon()之类的东西时,它会返回一个Icon,然后我必须对它进行各种操作才能使它成为一个大小合适的BufferedImage,它最终无法工作无论如何,因为它似乎是第一次投射到Icon使它丢失像素中的alpha数据,所以我无法检查用户是否点击了透明像素。

所以我为构造函数做了类似的事情:

public class MyButton extends JButton
{
   private BufferedImage bufImg;

   public MyButton(BufferedImage bufImg)
   {
      super(new ImageIcon(bufImg));
      this.bufImg = bufImg;
   }
 }

然后我为我的bufImg创建了一个访问器,它使用getSize()方法调整图像大小以适应JButton,然后返回一个调整大小合适的图像。我在getBufImg()访问器中进行转换,因为在调整窗口大小时图像大小可能会改变。当您调用getBufImg()时,通常是因为您单击了按钮,因此您当前没有调整窗口大小。

有点像这样会使图像返回正确的大小:

 public BufferedImage getBufImg()
    {
      BufferedImage newImg = new BufferedImage(getSize().getWidth(), getSize().getHeight(), BufferedImage.TYPE_INT_ARGB); //Create a new buffered image the right size
      Graphics2D g2d = newImg.createGraphics();
      g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

      g2d.drawImage(bufImg, 0, 0, getSize().getWidth(), getSize().getHeight(), null);
      g2d.dispose();

      return newImg;
    }

使用该缓冲图像,您可以编写如下方法:

  private int clickAlphaValue(BufferedImage bufImg, int posX, int posY) 
  {
    int alpha;

    alpha = (bufImg.getRGB(posX, posY) >>24) & 0x000000FF; //Gets the bit that contains alpha information

    return alpha;
  }

您调用实现MouseListener的按钮,如下所示:

myButton.addMouseListener(new MouseListener()
    {

    public void mouseClicked(MouseEvent e)
    {
    }

    public void mousePressed(MouseEvent e)
    {
    }

    public void mouseReleased(MouseEvent e)
    {
      if(clickAlphaValue(((myButton)e.getSource()).getBufImg(), e.getX(), e.getY()) != 0) //If alpha is not set to 0
        System.exit(0); //Or other things you want your button to do
    }

    public void mouseEntered(MouseEvent e)
    {
    }

    public void mouseExited(MouseEvent e)
    {
    }
  });

瞧!只有单击非透明像素时,该按钮才会执行操作。

感谢大家的帮助,我无法独自提出这个解决方案。

答案 2 :(得分:3)

如果您想拥有特定于形状的点击点,最好使用Shape及其contains方法。如果需要,可以在创建自定义按钮类作为其一部分时创建形状,并通过环绕形状的contains方法实现包含方法。

对于自定义JButton,创建一个扩展JButton的类,如下所示:

import java.awt.*;
import javax.swing.*;

public class CustomButton extends JButton{

    /** Filename of the image to be used as the button's icon. */
    private String fileName;
    /** The width of the button */
    private int width;
    /** The height of the button. */
    private int height;

 public CustomButton(String fileName, int width, int height){
    this.fileName = fileName;
    this.width = width;
    this.height = height;
    createButton();
}

/**
 * Creates the button according to the fields set by the constructor.
 */
private void createButton(){
    this.setIcon(getImageIcon(filename));
    this.setPreferredSize(new Dimension(width, height));
    this.setMaximumSize(new Dimension(width, height));
    this.setFocusPainted(false);
    this.setRolloverEnabled(false);
    this.setOpaque(false);
    this.setContentAreaFilled(false);
    this.setBorderPainted(false);
    this.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); 
  }
}

如果你想这样做,可以加载ImageIcon。

  public ImageIcon getImageIcon(String fileName){
    String imageDirectory = "images/"; //relative to classpath
    URL imgURL = getClass().getResource(imageDirectory + fileName);
    return new ImageIcon(imgURL);
  }

这将为您提供一个至少看起来像您的图像的按钮。 我在点击时问了一个关于基于图像的事件的类似问题,Shapes帮助了奇迹。 我想这归结为按钮图像的复杂程度。 无论如何这里是参考:
How can you detect a mouse-click event on an Image object in Java?

PS:也许考虑从图像生成形状,围绕所有不透明的像素。不知道这是否可行,但这意味着只有当用户点击它的图像部分时才会“按下”按钮。只是一个想法。

答案 3 :(得分:2)

如果您希望按钮布局是图像中非透明像素的布局,则应重新定义paintComponent()方法。这是最正确的做法(重写paint()在旧时工作,但现在不鼓励)。

但是我觉得它并不完全是你想要的:只要按钮位于非透明像素上,你就想要点击它,对吧?在这种情况下,你必须解析你的图像,并在点击时将鼠标坐标与图像的像素alpha通道进行比较,因为JButton没有这样的功能。

答案 4 :(得分:1)

paintComponent()而不是paint()取决于您XxxButtonUI内的paint()或仅覆盖paintComponent(),但是存在选项JButton#setIcon。< / p>

答案 5 :(得分:1)

如果你有一个圆形按钮,这正是你需要的:

  public class RoundButton extends JButton {

       public RoundButton() {
         this(null, null);
      }
       public RoundButton(Icon icon) {
         this(null, icon);
      }
       public RoundButton(String text) {
         this(text, null);
      }
       public RoundButton(Action a) {
         this();
         setAction(a);
      }

       public RoundButton(String text, Icon icon) {
         setModel(new DefaultButtonModel());
         init(text, icon);
         if(icon==null) return;
         setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
         setContentAreaFilled(false);
         setFocusPainted(false);
         initShape();
      }

    protected Shape shape, base;
    protected void initShape() {
      if(!getBounds().equals(base)) {
        Dimension s = getPreferredSize();
        base = getBounds();
        shape = new Ellipse2D.Float(0, 0, s.width, s.height);
      }
    }
    @Override public Dimension getPreferredSize() {
      Icon icon = getIcon();
      Insets i = getInsets();
      int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
      return new Dimension(iw+i.right+i.left, iw+i.top+i.bottom);
    }

    @Override public boolean contains(int x, int y) {
      initShape();
      return shape.contains(x, y);
      //or return super.contains(x, y) && ((image.getRGB(x, y) >> 24) & 0xff) > 0;
    }
  }

JButton有contains()方法。覆盖它并在mouseReleased()上调用它;