繁体   English   中英

在圆形JLabel图像周围创建一个可单击区域

[英]Create a clickable area around round JLabel image

我试图用这样的椭圆形图像创建一个JLabel。

在此处输入图片说明

我的代码如下:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public final class RoundedButtonDemo {
    private static Image bi;

    public static void main(String[] args) {
        try {
            loadImage();

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    createAndShowGUI();
                }
            });
        } catch (IOException e) {
            // handle exception
        }
    }

    private static void loadImage() throws IOException {
        int newWidth = 80;
        int newHeight = 40;
        bi = ImageIO.read(RoundedButtonDemo.class.getResource("/resources/login.png"));
        bi = bi.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);

    }

    private static void createAndShowGUI() {
        final JFrame frame = new JFrame();
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JLabel label = new JLabel();
        label.setSize(new Dimension(5, 5));
        label.setIcon(new ImageIcon(bi));
        label.setText("Hello World");
        label.setHorizontalTextPosition(JLabel.CENTER);
        // label.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        label.addMouseListener(new MouseListener() {

            private int count = 0;

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getSource() == label) {
                    if (count % 2 == 0) {
                        label.setText("Bye");
                    } else {
                        label.setText("Hello World");
                    }

                    count++;
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mouseExited(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mousePressed(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mouseReleased(MouseEvent e) {
                // TODO Auto-generated method stub

            }
        });

        frame.add(label);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

此代码创建一个包含以上图像的JLabel 每次单击按钮时,基于添加到JLabelMouseListenerJLabel的文本应交替显示。

我面临的问题是,即使我在图像外部(也在JLabel外部)单击,也会触发MouseListener ,并且文本会交替显示。

我要实现的目标是:圆形按钮,只要单击MouseListener表面上的任意位置,它就会响应MouseListener

我尝试了您的代码。 之所以会出现这种现象,是因为您的JLabel实际上填满了整个框架。 您需要为框架设置布局; 像这样的东西:

// ...
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());             // <-- you need this

final JLabel label = new JLabel();
label.setPreferredSize(new Dimension(80, 40)); // <-- also this
label.setIcon(new ImageIcon(bi));
label.setText("Hello World");
// ...

FlowLayout是最简单的布局管理器之一,其中有很多。 您可以在这里阅读有关它们的信息: https : //docs.oracle.com/javase/tutorial/uiswing/layout/visual.html

希望这可以帮助。

PS:您最好尝试调试此问题,并添加如下所示的边框:

// label.setBorder(BorderFactory.createLineBorder(Color.BLACK));

不知道为什么要注释掉。 也许您没有注意到整个框架区域周围都有黑色边框。 尝试将颜色设置为Color.RED或更明显。

正如yoshi指出的那样, JLabel填充整个内容窗格,因此单击内容窗格中的任何位置都会导致标签发生鼠标事件。

一些单词:

1)如果您希望JLabel在其父组件(垂直和水平)中居中,也可以使用GridBagLayout而不是FlowLayout 这个解决方案在这里

像这样:

final JPanel singleCenteredComponentJPanel = new JPanel(new GridBagLayout());
singleCenteredComponentJPanel.add(label);
frame.add(singleCenteredComponentJPanel);

2)此外,如果您想忽略鼠标在标签图标的任何透明像素上的单击(如果有),可以执行以下操作:

使用BufferedImage.getRGB(int x, int y)BufferedImage.getColorModel()ColorModel.getAlpha(int pixel)方法来确定每个鼠标事件的单击像素的alpha值。 如果Alpha值等于0,则该像素是完全透明的,否则Alpha值在1到255之间(包括两者),这又意味着该像素不是完全透明的。 示例代码如下。

而不是Image.getScaledInstance(...)来缩放图像(返回Image ,但是我们需要BufferedImage ),请使用此处提供的解决方案。

像这样:

private static void loadImage() throws IOException {
    int newWidth = 80;
    int newHeight = 40;
    bi = ImageIO.read(RoundedButtonDemo.class.getResource("/resources/login.png"));
    bi = getScaledBufferedImage(bi, newWidth, newHeight);

}

public static BufferedImage getScaledBufferedImage(final Image img,
                                                   final int newWidth,
                                                   final int newHeight) {
    final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
    final GraphicsDevice gdev = genv.getDefaultScreenDevice();
    final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
    final BufferedImage simg = gcnf.createCompatibleImage(newWidth, newHeight, Transparency.TRANSLUCENT);

    //Painting input image to output image:
    final Graphics2D g2d = simg.createGraphics();
    g2d.drawImage(img, 0, 0, newWidth, newHeight, null); //@Docs "...is scaled if necessary.".
    g2d.dispose();

    return simg;
}  

还将引用biImage更改为BufferedImage

然后是标签的MouseListener

private int count = 0;

@Override
public void mouseClicked(MouseEvent e) {
    if (e.getSource() == label) {

        //Get the mouse click position (in pixels),
        //relative to the top-left corner of label:
        final Point relativeClickPoint = e.getPoint();

        //Obtain alpha value from the TYPE_INT_ARGB pixel:
        final int pixel = bi.getRGB(relativeClickPoint.x, relativeClickPoint.y);
        final int alpha = bi.getColorModel().getAlpha(pixel);

        if (alpha > 0) { //Check if the pixel is not transparent.
            if (count % 2 == 0) {
                label.setText("Bye");
            } else {
                label.setText("Hello World");
            }
            count++;
        }
    }
}

请注意上述实现:图像的所有透明像素都将被忽略(包括形状“内部”的透明像素,如果有的话)。 这意味着用户可以单击图像的中心,如果单击的像素完全透明,则不会发生任何事情。 因此,我假设您的图片并非如此。

3)对于Image的缩放,可以使用collapseInside(...)方法确定新的大小:

/**
 * @param resultDim Output dimension (same aspect ratio as the original dimension, and inside containerDim).
 * @param originalDim Original dimension.
 * @param containerDim Dimension with the maximum width and maximum height.
 */
public static void collapseInside(final Dimension resultDim,
                                  final Dimension originalDim,
                                  final Dimension containerDim) {
    resultDim.setSize(originalDim);
    if (resultDim.width > containerDim.width) {
        //Adjusting height for max width:
        resultDim.height = ( resultDim.height * containerDim.width ) / resultDim.width;
        resultDim.width = containerDim.width;
    }
    if (resultDim.height > containerDim.height) {
        //Adjusting width for max height:
        resultDim.width = ( resultDim.width * containerDim.height ) / resultDim.height;
        resultDim.height = containerDim.height;
    }
}

使用此方法:
a)缩放Image的宽度将小于或等于容器的大小宽度( resultDim.width <= containerDim.width )。
b)高度相同( resultDim.height <= containerDim.height )。
c)原始Image尺寸的长宽比将保留在新Image尺寸中(resultDim.width / resultDim.height == originalDim.width / originalDim.height)。
使用Image.getScaledInstance(...)和先前的getScaledBufferedImage(...) ,可以更改Image的纵横比。
所以我编辑getScaledBufferedImage(...)以使用getScaledBufferedImage(...) collapseInside(...)

public static BufferedImage getScaledBufferedImage(final Image img,
                                                   final int newWidth,
                                                   final int newHeight) {
    final Dimension originalDim = new Dimension(img.getWidth(null), img.getHeight(null)),
                    containerDim = new Dimension(newWidth, newHeight),
                    resultDim = new Dimension();
    collapseInside(resultDim, originalDim, containerDim);

    final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
    final GraphicsDevice gdev = genv.getDefaultScreenDevice();
    final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
    final BufferedImage simg = gcnf.createCompatibleImage(resultDim.width, resultDim.height, Transparency.TRANSLUCENT);

    //Painting input image to output image:
    final Graphics2D g2d = simg.createGraphics();
    g2d.drawImage(img, 0, 0, resultDim.width, resultDim.height, null); //@Docs "...is scaled if necessary.".
    g2d.dispose();

    return simg;
}

以上所有三个的完整代码:

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public final class RoundedButtonDemo {
    private static BufferedImage bi;

    public static void main(String[] args) {
        try {
            loadImage();

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    createAndShowGUI();
                }
            });
        } catch (IOException e) {
            // handle exception
        }
    }

    private static void loadImage() throws IOException {
        int newWidth = 80;
        int newHeight = 40;
        bi = ImageIO.read(RoundedButtonDemo.class.getResource("/resources/login.png"));
        bi = getScaledBufferedImage(bi, newWidth, newHeight);

    }

    public static BufferedImage getScaledBufferedImage(final Image img,
                                                       final int newWidth,
                                                       final int newHeight) {
        final Dimension originalDim = new Dimension(img.getWidth(null), img.getHeight(null)),
                        containerDim = new Dimension(newWidth, newHeight),
                        resultDim = new Dimension();
        collapseInside(resultDim, originalDim, containerDim);

        final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
        final GraphicsDevice gdev = genv.getDefaultScreenDevice();
        final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
        final BufferedImage simg = gcnf.createCompatibleImage(resultDim.width, resultDim.height, Transparency.TRANSLUCENT);

        //Painting input image to output image:
        final Graphics2D g2d = simg.createGraphics();
        g2d.drawImage(img, 0, 0, resultDim.width, resultDim.height, null); //@Docs "...is scaled if necessary.".
        g2d.dispose();

        return simg;
    }

    /**
     * @param resultDim Output dimension (same aspect ratio as the original dimension, and inside containerDim).
     * @param originalDim Original dimension.
     * @param containerDim Dimension with the maximum width and maximum height.
     */
    public static void collapseInside(final Dimension resultDim,
                                      final Dimension originalDim,
                                      final Dimension containerDim) {
        resultDim.setSize(originalDim);
        if (resultDim.width > containerDim.width) {
            //Adjusting height for max width:
            resultDim.height = ( resultDim.height * containerDim.width ) / resultDim.width;
            resultDim.width = containerDim.width;
        }
        if (resultDim.height > containerDim.height) {
            //Adjusting width for max height:
            resultDim.width = ( resultDim.width * containerDim.height ) / resultDim.height;
            resultDim.height = containerDim.height;
        }
    }

    private static void createAndShowGUI() {
        final JFrame frame = new JFrame();
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JLabel label = new JLabel();
        label.setSize(new Dimension(5, 5));
        label.setIcon(new ImageIcon(bi));
        label.setText("Hello World");
        label.setHorizontalTextPosition(JLabel.CENTER);
        // label.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        label.addMouseListener(new MouseListener() {

            private int count = 0;

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getSource() == label) {

                    //Get the mouse click position (in pixels),
                    //relative to the top-left corner of label:
                    final Point relativeClickPoint = e.getPoint();

                    //Obtain alpha value from the TYPE_INT_ARGB pixel:
                    final int pixel = bi.getRGB(relativeClickPoint.x, relativeClickPoint.y);
                    final int alpha = bi.getColorModel().getAlpha(pixel);

                    if (alpha > 0) { //Check if the pixel is not transparent.
                        if (count % 2 == 0) {
                            label.setText("Bye");
                        } else {
                            label.setText("Hello World");
                        }
                        count++;
                    }
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mouseExited(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mousePressed(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mouseReleased(MouseEvent e) {
                // TODO Auto-generated method stub

            }
        });

        final JPanel singleCenteredComponentJPanel = new JPanel(new GridBagLayout());
        singleCenteredComponentJPanel.add(label);
        frame.add(singleCenteredComponentJPanel);

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

另外,您也可以重写paintComponent(...)JPanel ,并使用Graphics.drawImage(...)来绘制Image biJPanel ,像这样:

@Override
protected void paintComponent(final Graphics g) {
    super.paintComponent(g);

    //Drawing the image:
    g.drawImage(img, 0, 0, null);

    //Drawing the text:
    //For centering the text, I used code from:
    //https://stackoverflow.com/questions/27706197/how-can-i-center-graphics-drawstring-in-java
    final FontMetrics metrics = g.getFontMetrics(g.getFont());
    final int x = (img.getWidth(null) - metrics.stringWidth(text)) / 2;
    final int y = (img.getHeight(null) - metrics.getHeight()) / 2 + metrics.getAscent();
    g.drawString(text, x, y);
}

其中imgbitext是按钮的文本(即“ Hello World”和“ Bye”)。
然后按原样将MouseListener添加到JPanel

完整的代码(作为“ RoundedButtonDemo1 ”类,在同一包中):

import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public final class RoundedButtonDemo1 {
    private static BufferedImage bi;

    public static void main(String[] args) {
        try {
            loadImage();

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    createAndShowGUI();
                }
            });
        } catch (IOException e) {
            // handle exception
        }
    }

    private static void loadImage() throws IOException {
        int newWidth = 80;
        int newHeight = 40;
        bi = ImageIO.read(RoundedButtonDemo1.class.getResource("/resources/login.png"));
        bi = getScaledBufferedImage(bi, newWidth, newHeight);

    }

    public static BufferedImage getScaledBufferedImage(final Image img,
                                                       final int newWidth,
                                                       final int newHeight) {
        final Dimension originalDim = new Dimension(img.getWidth(null), img.getHeight(null)),
                        containerDim = new Dimension(newWidth, newHeight),
                        resultDim = new Dimension();
        collapseInside(resultDim, originalDim, containerDim);

        final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
        final GraphicsDevice gdev = genv.getDefaultScreenDevice();
        final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
        final BufferedImage simg = gcnf.createCompatibleImage(resultDim.width, resultDim.height, Transparency.TRANSLUCENT);

        //Painting input image to output image:
        final Graphics2D g2d = simg.createGraphics();
        g2d.drawImage(img, 0, 0, resultDim.width, resultDim.height, null); //@Docs "...is scaled if necessary.".
        g2d.dispose();

        return simg;
    }

    /**
     * @param resultDim Output dimension (same aspect ratio as the original dimension, and inside containerDim).
     * @param originalDim Original dimension.
     * @param containerDim Dimension with the maximum width and maximum height.
     */
    public static void collapseInside(final Dimension resultDim,
                                      final Dimension originalDim,
                                      final Dimension containerDim) {
        resultDim.setSize(originalDim);
        if (resultDim.width > containerDim.width) {
            //Adjusting height for max width:
            resultDim.height = ( resultDim.height * containerDim.width ) / resultDim.width;
            resultDim.width = containerDim.width;
        }
        if (resultDim.height > containerDim.height) {
            //Adjusting width for max height:
            resultDim.width = ( resultDim.width * containerDim.height ) / resultDim.height;
            resultDim.height = containerDim.height;
        }
    }

    private static class CustomButton extends JPanel {
        private Image img;
        private String text;

        public void setImage(final Image img) {
            this.img = img;
            final Dimension imgDim = new Dimension(img.getWidth(null), img.getHeight(null));
            setMinimumSize(imgDim);
            setPreferredSize(imgDim);
            repaint();
        }

        public Image getImage() {
            return img;
        }

        public void setText(final String text) {
            this.text = text;
            repaint();
        }

        public String getText() {
            return text;
        }

        @Override
        protected void paintComponent(final Graphics g) {
            super.paintComponent(g);

            //Drawing the image:
            g.drawImage(img, 0, 0, null);

            //Drawing the text:
            //For centering the text, I used code from:
            //https://stackoverflow.com/questions/27706197/how-can-i-center-graphics-drawstring-in-java
            final FontMetrics metrics = g.getFontMetrics(g.getFont());
            final int x = (img.getWidth(null) - metrics.stringWidth(text)) / 2;
            final int y = (img.getHeight(null) - metrics.getHeight()) / 2 + metrics.getAscent();
            g.drawString(text, x, y);
        }
    }

    private static void createAndShowGUI() {
        final JFrame frame = new JFrame();
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final CustomButton button = new CustomButton();
        button.setSize(new Dimension(5, 5));
        button.setImage(bi);
        button.setText("Hello World");
        //button.setHorizontalTextPosition(SwingConstants.CENTER);
        //button.setOpaque(false);
        //button.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        button.addMouseListener(new MouseListener() {

            private int count = 0;

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getSource() == button) {

                    //Get the mouse click position (in pixels),
                    //relative to the top-left corner of label:
                    final Point relativeClickPoint = e.getPoint();

                    //Obtain alpha value from the TYPE_INT_ARGB pixel:
                    final int pixel = bi.getRGB(relativeClickPoint.x, relativeClickPoint.y);
                    final int alpha = bi.getColorModel().getAlpha(pixel);

                    if (alpha > 0) { //Check if the pixel is not transparent.
                        if (count % 2 == 0) {
                            button.setText("Bye");
                        } else {
                            button.setText("Hello World");
                        }
                        count++;
                    }
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mouseExited(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mousePressed(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public void mouseReleased(MouseEvent e) {
                // TODO Auto-generated method stub

            }
        });


        final JPanel singleCenteredComponentJPanel = new JPanel(new GridBagLayout());
        singleCenteredComponentJPanel.add(button);
        frame.add(singleCenteredComponentJPanel);

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

另外,如果您想要普通的按钮,而不是标签,似乎在这里您也可以重塑按钮的形状!

除此之外,我还尝试了以下方法:
1)使用JLayeredPane 有一个教程对它们进行了解释,我猜想他们可能有一种方法可以获取给定Point或类似对象的可见窗格的顺序,但事实并非如此。
2)在MouseListener使用Container.getComponentAt(Point)Container.findComponentAt(Point) ,但是(正如我在测试后发现的那样),这些方法不会“透视”非透明(+透明)像素。
3)在JPanel搜索重塑或半透明(受“ 如何创建半透明和成形的Windows”启发),但未找到任何内容。
4) 如何用JLayer类装饰组件 ,第一行说:

...使您能够利用组件并响应组件事件,而无需直接修改基础组件。

看起来很有希望,但我在这里完成了。

现在就这些了。 再见。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM