簡體   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