向如下工具类中的方法传入图像信息,来获得一个
Icon
对象。此
Icon
对象能够自适应
JLabel
或
JButton
大小进行缩放。
代码中的方法为静态方法,可直接使用。后续 Demo 和 注释 部分对代码内容和设计思路进行了详细介绍,希望能为大家提供一些参考,可略过。
代码
import java.net.URL;
import java.awt.Point;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Dimension;
import javax.swing.ImageIcon;
/**Title: SwingUtil.java
* Swing工具类
*
* @author Run
* @date 2019-08-20 */
public class SwingUtil {
/**创建一个可以自适应组件大小的ImageIcon对象
* @param image 从<code> Image </code>对象来创建ImageIcon
* @param constrained 是否等比例缩放 。当为<code> true </code>时,可通过
* {@link javax.swing.JComponent#setAlignmentX(float)}和
* {@link javax.swing.JComponent#setAlignmentY(float)}方法设置组件对齐方式。
* @date 2019-08-20 */
public static ImageIcon createAutoAdjustIcon(Image image, boolean constrained) {
ImageIcon icon = new ImageIcon(image) {
@Override
public synchronized void paintIcon(java.awt.Component cmp, Graphics g, int x, int y) {
//初始化参数
Point startPoint = new Point(0, 0);//默认绘制起点
Dimension cmpSize = cmp.getSize();//获取组件大小
Dimension imgSize = new Dimension(getIconWidth(), getIconHeight());//获取图像大小
//计算绘制起点和区域
if(constrained) {//等比例缩放
//计算图像宽高比例
double ratio = 1.0*imgSize.width/imgSize.height;
//计算等比例缩放后的区域大小
imgSize.width = (int) Math.min(cmpSize.width, ratio*cmpSize.height);
imgSize.height = (int) (imgSize.width/ratio);
//计算绘制起点
startPoint.x = (int)
(cmp.getAlignmentX()*(cmpSize.width - imgSize.width));
startPoint.y = (int)
(cmp.getAlignmentY()*(cmpSize.height - imgSize.height));
} else {//完全填充
imgSize = cmpSize;
}
//根据起点和区域大小进行绘制
if(getImageObserver() == null) {
g.drawImage(getImage(), startPoint.x, startPoint.y,
imgSize.width, imgSize.height, cmp);
} else {
g.drawImage(getImage(), startPoint.x, startPoint.y,
imgSize.width, imgSize.height, getImageObserver());
}
};
};
return icon;
}
/**创建一个可以自适应组件大小的Icon对象
* @param filename 指定文件名或者路径的字符串
* @param constrained 是否等比例缩放。当为<code> true </code>时,可通过
* {@link javax.swing.JComponent#setAlignmentX(float)}和
* {@link javax.swing.JComponent#setAlignmentY(float)}方法设置组件对齐方式。
* @date 2019-08-20 */
public static ImageIcon createAutoAdjustIcon(String filename, boolean constrained) {
return createAutoAdjustIcon(new ImageIcon(filename).getImage(), constrained);
}
/**创建一个可以自适应组件大小的ImageIcon对象
* @param url 从指定的<code> URL </code>对象来创建ImageIcon
* @param constrained 是否等比例缩放 。当为<code> true </code>时,可通过
* {@link javax.swing.JComponent#setAlignmentX(float)}和
* {@link javax.swing.JComponent#setAlignmentY(float)}方法设置组件对齐方式。
* @date 2019-08-20 */
public static ImageIcon createAutoAdjustIcon(URL url, boolean constrained) {
return createAutoAdjustIcon(new ImageIcon(url).getImage(), constrained);
}
}
使用:
- 通过代码中
SwingUtil.createAutoAdjustIcon(String , boolean)
等静态方法,创建一个能自适应组件大小的ImageIcon
对象。 - 在实现了
setIcon(Icon)
方法的JComponent
组件(如JLabel
、JButton
)中,调用setIcon(Icon)
方法,设置组件图标为该ImageIcon
。
注意:
- 在组件(如
JLabel
)中最好只放置此Icon
,不添加其它图标或文本,否则这些内容之间会产生覆盖。 - 当图像属性设置为
等比例缩放
时,可以调用JComponent
的setAlignmentX(float)
或setAlignmentY(float)
方法,分别设置图标的水平或垂直对齐方式。
Demo
下面的Demo展示了一张网络图片在JLabel
里的三种适应样式(等比例缩放、完全填充、不缩放)。
package demo;
import java.net.URL;
import java.awt.Point;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import javax.swing.border.LineBorder;
import java.net.MalformedURLException;
/**Title: Demo.java
* 示例代码
*
* @author Run
* @date 2019-08-20 */
public class Demo{
public static void main(String args[]){
SwingUtilities.invokeLater( () ->{
//运行示例代码
AutoAdjustIconDemo();
});
}
/**图像自适应组件大小
* @date 2019-08-20 */
static void AutoAdjustIconDemo() {
JFrame frame=new JFrame("Auto-adjust Icon");
JPanel panel=new JPanel(new GridLayout(1, 3, 10, 10));
//导入图片
Image image = null;
try {
//一张来自优快云的默认用户头像
URL url = new URL("https://imgconvert.csdnimg.cn/"
+ "aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw");
image = new ImageIcon(url).getImage();
} catch (MalformedURLException e) {
System.out.println("HTTP ERROR 404");
}
//等比例JLabel
JLabel ratioLabel = new JLabel();
ratioLabel.setIcon(SwingUtil.createAutoAdjustIcon(image, true));
ratioLabel.setBorder(new LineBorder(Color.RED));
ratioLabel.setAlignmentX(0.5F);
ratioLabel.setAlignmentY(0.5F);
//不等比例JLabel
JLabel filledLabel = new JLabel();
filledLabel.setIcon(SwingUtil.createAutoAdjustIcon(image, false));
filledLabel.setBorder(new LineBorder(Color.ORANGE));
//常规样式JLabel
JLabel normalLabel = new JLabel();
normalLabel.setIcon(new ImageIcon(image));
normalLabel.setBorder(new LineBorder(Color.BLUE));
//JPanel内容
panel.add(ratioLabel);
panel.add(filledLabel);
panel.add(normalLabel);
//JFrame内容
frame.getContentPane().add(panel);
frame.setSize(600,480);
//JFrame属性
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
//
/**Swing工具类
*
* @author Run
* @date 2019-08-20 */
class SwingUtil {
/**创建一个可以自适应组件大小的ImageIcon对象
* @param image 从<code> Image </code>对象来创建ImageIcon
* @param constrained 是否等比例缩放 。当为<code> true </code>时,可通过
* {@link javax.swing.JComponent#setAlignmentX(float)}和
* {@link javax.swing.JComponent#setAlignmentY(float)}方法设置组件对齐方式。
* @date 2019-08-20 */
public static ImageIcon createAutoAdjustIcon(Image image, boolean constrained) {
ImageIcon icon = new ImageIcon(image) {
@Override
public synchronized void paintIcon(java.awt.Component cmp, Graphics g, int x, int y) {
//初始化参数
Point startPoint = new Point(0, 0);//默认绘制起点
Dimension cmpSize = cmp.getSize();//获取组件大小
Dimension imgSize = new Dimension(getIconWidth(), getIconHeight());//获取图像大小
//计算绘制起点和区域
if(constrained) {//等比例缩放
//计算图像宽高比例
double ratio = 1.0*imgSize.width/imgSize.height;
//计算等比例缩放后的区域大小
imgSize.width = (int) Math.min(cmpSize.width, ratio*cmpSize.height);
imgSize.height = (int) (imgSize.width/ratio);
//计算绘制起点
startPoint.x = (int)
(cmp.getAlignmentX()*(cmpSize.width - imgSize.width));
startPoint.y = (int)
(cmp.getAlignmentY()*(cmpSize.height - imgSize.height));
} else {//完全填充
imgSize = cmpSize;
}
//根据起点和区域大小进行绘制
if(getImageObserver() == null) {
g.drawImage(getImage(), startPoint.x, startPoint.y,
imgSize.width, imgSize.height, cmp);
} else {
g.drawImage(getImage(), startPoint.x, startPoint.y,
imgSize.width, imgSize.height, getImageObserver());
}
};
};
return icon;
}
/**创建一个可以自适应组件大小的Icon对象
* @param filename 指定文件名或者路径的字符串
* @param constrained 是否等比例缩放。当为<code> true </code>时,可通过
* {@link javax.swing.JComponent#setAlignmentX(float)}和
* {@link javax.swing.JComponent#setAlignmentY(float)}方法设置组件对齐方式。
* @date 2019-08-20 */
public static ImageIcon createAutoAdjustIcon(String filename, boolean constrained) {
return createAutoAdjustIcon(new ImageIcon(filename).getImage(), constrained);
}
/**创建一个可以自适应组件大小的ImageIcon对象
* @param url 从指定的<code> URL </code>对象来创建ImageIcon
* @param constrained 是否等比例缩放 。当为<code> true </code>时,可通过
* {@link javax.swing.JComponent#setAlignmentX(float)}和
* {@link javax.swing.JComponent#setAlignmentY(float)}方法设置组件对齐方式。
* @date 2019-08-20 */
public static ImageIcon createAutoAdjustIcon(URL url, boolean constrained) {
return createAutoAdjustIcon(new ImageIcon(url).getImage(), constrained);
}
}
运行结果:
注释
在Java Swing中,部分JComponent
组件实现了setIcon(Icon)
方法,如JLabel
、JButton
等。通过setIcon(Icon)
可以在组件中设置需要显示的图像。其中的Icon
接口有三个方法:
public interface Icon
{
/**
* Draw the icon at the specified location. Icon implementations
* may use the Component argument to get properties useful for
* painting, e.g. the foreground or background color.
*/
void paintIcon(Component c, Graphics g, int x, int y);
int getIconWidth();
int getIconHeight();
}
ImageIcon
类实现了Icon
接口。上面程序中的关键代码SwingUtil.createAutoAdjustIcon(String , boolean)
,便是通过覆盖ImageIcon
里的paintIcon(Component, Graphics, int, int)
方法,来实现图像的缩放功能。
Swing 使用的是 MVC 框架。组件模型会在程序在运行时,委托对应的UI对象进行内容绘制。所以,在JLabel
对应的基本UI类javax.swing.plaf.basic.BasicLabelUI
中,可以寻找到对应的图像显示代码。在其BasicLabelUI.paint(Graphics, javax.swing.JComponent)
方法中,可以看到:
由此看出,JLabel
中图像的绘制任务,最终转交给了Icon
类的paintIcon(Component, Graphics, int, int)
方法这里。
接下来的事情就简单了。在paintIcon(Component, Graphics, int, int)
方法中,通过传入参数,可以获得:当前正在进行绘制的组件、图形上下文和图像左上角顶点。如果图像仅需要完全填充的样式,可以省略计算等比例缩放的过程,直接按照下面这样写:
public synchronized void paintIcon(java.awt.Component cmp, Graphics g, int x, int y) {
g.drawImage(getImage(), 0, 0, cmp.getWidth(), cmp.getHeight(), cmp);
}
java.awt.Graphics.drawImage(Image, int, int, int, int, ImageObserver)
方法会自动将图像(Image
参数)缩放到需要绘制的区域中。
当等比例缩放图片时,需要在调用drawImage(Image, int, int, int, int, ImageObserver)
方法前,额外进行一些计算:
- 计算图像宽和高的像素比;
- 依据图像像素比和组件的宽、高,计算组件能支持的最大图像尺寸;
- 计算缩放后,图像宽高与组件宽高间的差值,并依据组件X轴、Y轴的对齐参数,求出图像左上角顶点的坐标;
- 依据图像缩放后左上角顶点的坐标、缩放后的尺寸,对图像进行绘制。
即:
/* cmp 为需要进行绘制的 Component 组件 */
//初始化参数
Point startPoint = new Point(0, 0);//默认绘制起点
Dimension cmpSize = cmp.getSize();//获取组件大小
Dimension imgSize = new Dimension(getIconWidth(), getIconHeight());//获取图像大小
/* 等比例缩放,需要计算绘制起点和区域 */
//计算图像宽高比例
double ratio = 1.0*imgSize.width/imgSize.height;
//计算等比例缩放后的区域大小
imgSize.width = (int) Math.min(cmpSize.width, ratio*cmpSize.height);
imgSize.height = (int) (imgSize.width/ratio);
//计算绘制起点
startPoint.x = (int)
(cmp.getAlignmentX()*(cmpSize.width - imgSize.width));
startPoint.y = (int)
(cmp.getAlignmentY()*(cmpSize.height - imgSize.height));
两种缩放方式整合在一起,并结合ImageIcon
类的几个常用接口,便实现了文章开头部分的代码。