[英]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
。 每次單擊按鈕時,基於添加到JLabel
的MouseListener
, JLabel
的文本應交替顯示。
我面臨的問題是,即使我在圖像外部(也在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;
}
還將引用bi
從Image
更改為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
bi
的JPanel
,像這樣:
@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);
}
其中img
是bi
, text
是按鈕的文本(即“ 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.