有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

阅读编辑2,了解我真正缺少的东西,以使其发挥作用

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

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

有没有一种简单的方法可以做到这一点,或者我必须解析图像并找到alpha像素来创建自定义边框

我必须重写哪些方法才能使其按我想要的方式工作

还有一个问题,我稍后会问:是否最好使用某种算法来更改图像的颜色,使其看起来像是在人们单击时被单击,还是在按钮处于活动状态时创建第二个图像并绘制该图像更好

编辑:我刚读到另一个问题,我应该重新定义paintComponent()而不是paint(),我想知道为什么重新定义paint()效果很好

编辑2:我更改了所有内容,以确保使用带有图标的默认构造函数创建jbutton。我要做的是获取点击注册位置的X和Y位置,并在该位置抓取图标的像素,检查其alpha通道是否为0(如果为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

这只是一个猜测,但有没有可能当我的图像被投射到图标时,它会丢失其Alpha属性,因此不会返回正确的值?无论如何,如果有人真的能帮助我,告诉我应该改变什么以获得正确的值,我会非常感激

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


共 (6) 个答案

  1. # 1 楼答案

    如果您想拥有特定于形状的点击点,最好使用shape及其contains方法。如果需要,可以在创建自定义按钮类时创建一个形状,并通过环绕形状的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);
      }
    

    这会给你一个按钮,至少看起来像你的形象。 我问了一个类似的问题,关于基于图像的点击事件,形状有助于创造奇迹。 我想这取决于你的按钮图像有多复杂。 以下是参考资料:
    How can you detect a mouse-click event on an Image object in Java?

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

  2. # 2 楼答案

    如果你希望你的按钮布局是图像中的不透明像素,那么你应该重新定义paintComponent()方法。这是最正确的方法(覆盖paint()在旧时代起作用,但现在不被鼓励)

    然而,我认为这并不是你想要的:你只想在非透明像素上点击按钮时被检测到,对吗?在这种情况下,您必须解析图像,并在单击时将鼠标坐标与图像的像素alpha通道进行比较,因为JButton没有这样的功能

  3. # 3 楼答案

    我认为你走错了路。不必重写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

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

  4. # 4 楼答案

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

      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()上调用它

  5. # 6 楼答案

    因为多个答案中都有好的元素,但没有一个答案是完整的,所以我会回答自己的问题,这样其他有相同问题的人可以尝试类似的方法

    我使用一个扩展了JButton的新类创建了我的按钮,并使用了一个新的构造函数,该构造函数将BuffereImage作为参数,而不是图标。原因是当我做了一些像我的按钮。getIcon(),它将返回一个图标,然后我必须对其进行各种操作,使其成为大小合适的BuffereImage,结果它无论如何都无法工作,因为它似乎是第一次对图标进行强制转换,使其丢失了像素中的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;
      }
    

    你可以点击按钮来实现鼠标听器,如下所示:

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

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

    谢谢大家的帮助,我不可能独自想出这个解决方案