[英]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.