TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
GUI组件要完成的任务有2个,展现与业务。对于按钮来说,文本、图标、边框、背景属于展现层,而这些元素在按钮不同状态下会不尽相同,一般来说至少有4种状态下的展现:普通、鼠标放在其上、被按下、被禁用,也就是按钮应该具备这4种状态。
下面给出的示例是swing实现的自定义按钮。
通常swing自定义组件继承javax.swing.JComponent并重写protected void paintComponent(Graphics g)方法实现自定义绘制。 重写paintComponent方法时通常要先去掉super.paintComponent(g),因为父方法调用会绘制背景色。不妨先看一下源代码中的调用过程。
在JComponent.java中paintComponent(Graphics g)方法定义如下:
protected void paintComponent(Graphics g) {
if (ui != null) {
Graphics scratchGraphics = (g == null) ? null : g.create();
try {
ui.update(scratchGraphics, this);
}
finally {
scratchGraphics.dispose();
}
}
}
其中ui的声明如下
protected transient ComponentUI ui;
然后转向ComponentUI的update(Graphics g, JComponent c)方法:
public void update(Graphics g, JComponent c) {
if (c.isOpaque()) {
g.setColor(c.getBackground());
g.fillRect(0, 0, c.getWidth(),c.getHeight());
}
paint(g, c);
}
可见如果发现组件是非透明的,就绘制背景,可以看出swing组件的setBackground方法如何绘制背景的。
一般简单的自定义组件,你可以只通过重写paintComponent方法来实现绘制,对于一般的组件这已经足够。对于自定义按钮一般的原则是准备4张背景图对应上述4种状态,这4种状态都可通过鼠标监听来感知,当状态改变时,调用repaint()使Button重绘。除了背景,按钮文本、图标等的改变一样也必须调用repaint()来刷新。
然后重要的一点是你必须重写public Dimension getPreferredSize()来获得按钮的最佳尺寸。getPreferredSize方法对于布局管理器来说至关重要,布局管理器会通过getPreferredSize的判断组件的最佳大小,并进行布局。而对于本范例而言,getPreferredSize的大小只和背景图片大小有关。
对于业务,尽量做到前台界面与后来业务分离。你可以自定义按钮动作监听器来实现,本例是沿用swing的Action实现,当鼠标抬起时,构造一个ActionEvent对象,然后交给Action成员的actionPerformed(ActionEvent e)处理。
代码如下:
- /*
- * ImageButton.java
- *
- * Created on 2007年11月11日, 下午6:30
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
- package demo.swing;
- import java.awt.AlphaComposite;
- import java.awt.Color;
- import java.awt.Container;
- import java.awt.Dimension;
- import java.awt.Font;
- import java.awt.FontMetrics;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.RenderingHints;
- import java.awt.Shape;
- import java.awt.event.ActionEvent;
- import java.awt.event.MouseEvent;
- import java.awt.font.TextAttribute;
- import java.awt.font.TextLayout;
- import java.awt.geom.AffineTransform;
- import java.util.HashMap;
- import javax.swing.Action;
- import javax.swing.Icon;
- import javax.swing.JComponent;
- import javax.swing.SwingConstants;
- import javax.swing.SwingUtilities;
- import javax.swing.event.MouseInputListener;
- /**
- * 为需要具备专业外观的按钮提供方便的实现。通过此类提供的若干方法可轻松实现专业外观。
- * @author 电玩
- */
- public final class ImageButton extends JComponent implements
- MouseInputListener {
- /**
- * 通常状态下的背景图片
- */
- private Icon backgroundImage;
-
- /**
- * 通常状态下的最佳尺寸
- */
- private Dimension preferredSize;
-
- /**
- * 鼠标光标在上方时的背景图片
- */
- private Icon rolloverBackgroundImage;
-
- /**
- * 鼠标光标在上方时的最佳尺寸
- */
- private Dimension rolloverPreferredSize;
-
- /**
- * 按钮被按下时的背景图片
- */
- private Icon pressedBackgroundImage;
-
- /**
- * 按钮被按下时的最佳尺寸
- */
- private Dimension pressedPreferredSize;
-
- /**
- * 按钮被禁止时的背景图片
- */
- private Icon disabledBackgroundImage;
-
- /**
- * 按钮被禁止时的最佳尺寸
- */
- private Dimension disabledPreferredSize;
-
- /**
- * 当前按钮的最佳尺寸
- */
- private volatile Dimension currentSize;
-
- /**
- * 通常状态下按钮的图标
- */
- private Icon icon;
-
- /**
- * 按钮被禁止时的图标
- */
- private Icon disabledIcon;
-
- /**
- * 按钮图标出现的位置
- */
- private IconOrientation iconOrientation = IconOrientation.WEST;
-
- /**
- * 按钮的默认尺寸
- */
- private static final Dimension DEFAULT_SIZE = new Dimension(100, 25);
-
- /**
- * 水平偏移量
- */
- private int horizontalOffset = DEFAULT_HORIZONTAL_OFFSET;
- /**
- * 默认的水平偏移量
- */
- private static final int DEFAULT_HORIZONTAL_OFFSET = 4;
-
- /**
- * 垂直偏移量
- */
- private int verticalOffset = DEFALUT_VERTICAL_OFFSET;
-
- /**
- * 默认的垂直偏移量
- */
- private static final int DEFALUT_VERTICAL_OFFSET = 2;
-
- /**
- * 显示在按钮上的文字
- */
- private String text;
-
- /**
- * 按钮文本的字体
- */
- private Font font;
-
- /**
- * 按钮文本的默认字体
- */
- private static final Font DEFAULT_FONT = Font.decode("Dialog-Plain-14");
-
- /**
- * 按钮被禁止时候文字的颜色
- */
- private Color disabledForeground;
-
- /**
- * 默认的按钮被禁止时文本的颜色
- */
- private final Color DEFAULT_DISABLED_FOREGROUND = new Color(192, 192, 192);
-
- /**
- * 按钮的状态
- */
- private volatile Status status = Status.DEFAULT;
-
- /**
- * 按钮的颜色透明度
- */
- private volatile float alpha = 1.0f;
-
- private static HashMap< RenderingHints.Key, Object> renderingHints;
-
- /**
- * 按钮执行的动作
- */
- private Action action;
-
- static {
- renderingHints = new HashMap();
- renderingHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
- RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
- renderingHints.put(RenderingHints.KEY_ANTIALIASING,
- RenderingHints.VALUE_ANTIALIAS_ON);
- renderingHints.put(RenderingHints.KEY_COLOR_RENDERING,
- RenderingHints.VALUE_COLOR_RENDER_QUALITY);
- renderingHints.put(RenderingHints.KEY_DITHERING,
- RenderingHints.VALUE_DITHER_DISABLE);
- renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS,
- RenderingHints.VALUE_FRACTIONALMETRICS_ON);
- renderingHints.put(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BICUBIC );
- renderingHints.put(RenderingHints.KEY_STROKE_CONTROL,
- RenderingHints.VALUE_STROKE_PURE);
- renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
- RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
- }
-
- /** 创建一个ImageButton按钮的实例 */
- public ImageButton() {
- currentSize = DEFAULT_SIZE;
- addMouseListener(this);
- addMouseMotionListener(this);
- }
-
- /**
- * 设置常规状态下按钮的背景。调用该方法会影响到按钮在常规状态下的最佳尺寸,
- * 由传入的图标参数对应的图标尺寸决定
- * @param backgroundImage 常规状态下背景图标对象
- * @throws java.lang.IllegalArgumentException 如果传入的参数为null
- */
- public void setBackgroundImage(Icon backgroundImage) throws IllegalArgumentException {
- if (backgroundImage == null) {
- throw new IllegalArgumentException("backgroundImage can"t be null");
- }
- this.backgroundImage = backgroundImage;
- preferredSize = new Dimension(backgroundImage.getIconWidth(),
- backgroundImage.getIconHeight());
- currentSize = preferredSize;
- }
-
- /**
- * 设置被禁用状态下按钮的背景。调用该方法会影响到按钮在禁用状态下的最佳尺寸,
- * 由传入的图标参数对应的图标尺寸决定
- * @param disabledBackgroundImage 禁用状态下背景图标对象
- * @throws java.lang.IllegalArgumentException 如果传入的参数为null
- */
- public void setDisabledBackgroundImage(Icon disabledBackgroundImage) throws IllegalArgumentException {
- if (disabledBackgroundImage == null) {
- throw new IllegalArgumentException(
- "disabledBackgroundImage can"t be null");
- }
- this.disabledBackgroundImage = disabledBackgroundImage;
- disabledPreferredSize = new Dimension(disabledBackgroundImage
- .getIconWidth(), disabledBackgroundImage.getIconHeight());
- }
-
- /**
- * 设置在被按下状态时按钮的背景。调用该方法会影响到按钮在被按下状态时的最佳尺寸,
- * 由传入的图标参数对应的图标尺寸决定
- * @param pressedBackgroundImage 被按下时的背景图标对象
- * @throws java.lang.IllegalArgumentException 如果传入的参数为null
- */
- public void setPressedBackgroundImage(Icon pressedBackgroundImage)throws IllegalArgumentException {
- if (pressedBackgroundImage == null) {
- throw new IllegalArgumentException(
- "pressedBackgroundImage can"t be null");
- }
- this.pressedBackgroundImage = pressedBackgroundImage;
- pressedPreferredSize = new Dimension(pressedBackgroundImage
- .getIconWidth(), pressedBackgroundImage.getIconHeight());
- }
- /**
- * 设置鼠标指针在其上方时按钮的背景。调用该方法会影响到按钮在鼠标指针在其上方
- * 时的最佳尺寸,由传入的图标参数对应的图标尺寸决定
- * @param rolloverBackgroundImage 鼠标指针在其上方时的背景图标对象
- * @throws java.lang.IllegalArgumentException 如果传入的参数为null
- */
- public void setRolloverBackgroundImage(Icon rolloverBackgroundImage) throws IllegalArgumentException {
- if (rolloverBackgroundImage == null) {
- throw new IllegalArgumentException(
- "rolloverBackgroundImage can"t be null");
- }
- this.rolloverBackgroundImage = rolloverBackgroundImage;
- rolloverPreferredSize = new Dimension(rolloverBackgroundImage
- .getIconWidth(), rolloverBackgroundImage.getIconHeight());
- }
-
- /**
- * 设置水平偏移量。
- * @param horizontalOffset 水平偏移量
- * @throws java.lang.IllegalArgumentException 如果参数小于0,抛出此异常
- */
- public void setHorizontalOffset(int horizontalOffset) throws IllegalArgumentException{
- if(horizontalOffset < 0) {
- throw new IllegalArgumentException("horizontalOffset must >=0");
- }
- this.horizontalOffset = horizontalOffset;
- }
-
- /**
- * 设置垂直偏移量
- * @param verticalOffset 垂直偏移量
- * @throws java.lang.IllegalArgumentException 如果参数小于0,抛出此异常
- */
- public void setVerticalOffset(int verticalOffset) throws IllegalArgumentException{
- if(verticalOffset < 0) {
- throw new IllegalArgumentException("verticalOffset must >=0");
- }
- this.verticalOffset = verticalOffset;
- }
-
- /**
- * 设置按钮的图标
- * @param icon 图标对象
- * @throws java.lang.IllegalArgumentException 如果传入的参数为null,抛出此异常
- */
- public synchronized void setIcon(Icon icon) throws IllegalArgumentException {
- if(icon == null) {
- throw new IllegalArgumentException(
- "icon can"t be null");
- }
- this.icon = icon;
- }
-
- /**
- * 设置按钮在被禁用时显示的图标
- * @param disabledIcon 图标对象
- * @throws java.lang.IllegalArgumentException 如果传入的参数为null,抛出此异常
- */
- public synchronized void setDisabledIcon(Icon disabledIcon) throws IllegalArgumentException {
- if(disabledIcon == null) {
- throw new IllegalArgumentException(
- "disabledIcon can"t be null");
- }
- this.disabledIcon = disabledIcon;
- }
-
- public void setIconOrientation(IconOrientation iconOrientation) throws IllegalArgumentException{
- if(iconOrientation == null) {
- throw new IllegalArgumentException(
- "iconOrientation can"t be null");
- }
- this.iconOrientation = iconOrientation;
- }
-
- public void setDisabledForeground(Color disabledForeground) {
- this.disabledForeground = disabledForeground;
- }
-
- /**
- * 设置按钮的透明度
- * @param alpha 透明度。范围在0.0f~1.0f之间。0.0f为完全透明,1.0f为完全显示
- * @throws java.lang.IllegalArgumentException 如果不在0.0f~1.0f之间会抛出此异常
- */
- public synchronized void setAlpha(float alpha)throws IllegalArgumentException{
- if (alpha < 0f || alpha > 1.0f) {
- throw new IllegalArgumentException(
- "alpha value must between 0.0 and 1.0");
- }
- this.alpha = alpha;
- repaint();
- }
-
- /**
- * 设置按钮显示的文本
- * @param text 显示的文本字符串
- */
- public synchronized void setText(String text) {
- if (text != null) {
- this.text = text;
- repaint();
- }
- }
-
- /**
- * 返回按钮的当前字体,如果之前没有设置字体,则返回默认字体
- * @return 按钮的当前字体
- */
- public Font getFont() {
- if(font == null) {
- return DEFAULT_FONT;
- }
- return font;
- }
-
- /**
- * 设置按钮的当前字体
- * @param font 字体实例
- */
- public void setFont(Font font) {
- this.font = font;
- super.setFont(font);
- }
- /**
- * 指定这个按钮的[code]动作
复制代码
* @param action 按钮的
*/
public void setAction(Action action) {
this.action = action;
}
/**
* 覆盖- JComponent.setEnabled(boolean enabled)
复制代码 ,设置是否按钮可用或被禁止
* @param enabled 如果按钮可用则为true,否则为false
* @see java.awt.Component#isEnabled
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (isEnabled() != enabled) {
if (disabledPreferredSize != null
&& !disabledPreferredSize.equals(currentSize)) {
currentSize = disabledPreferredSize;
revalidate();
}
}
}
/**
* 绘制按钮边框
* @param g 图形上下文
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
@Override
protected void paintBorder(Graphics g) {
}
/**
* 绘制子组件
* @param g 图形上下文
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
@Override
protected void paintChildren(Graphics g) {
}
/**
* 绘制按钮
* @param g 图形上下文
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.addRenderingHints(renderingHints);
if(alpha < 1.0f) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
alpha));
}
drawBackgroundImage(g2d);
drawIcon(g2d);
drawText(g2d);
g2d.dispose();
}
private void drawIcon(final Graphics2D g2d) {
Icon currentIcon = isEnabled() ? icon : disabledIcon;
if(currentIcon == null) {
return;
}
int w = currentIcon.getIconWidth();
int h = currentIcon.getIconHeight();
int offsetX = 0;
int offsetY = 0;
switch (iconOrientation) {
case WEST:
offsetX = horizontalOffset;
offsetY = getHeight() / 2 - h / 2;
break;
case EAST:
offsetX = getWidth() - horizontalOffset - w;
offsetY = getHeight() / 2 - h / 2;
break;
case NORTH:
offsetX = getWidth() / 2 - w / 2;
offsetY = verticalOffset;
break;
case SOUTH:
offsetX = getWidth() / 2 - w / 2;
offsetY = getHeight() - verticalOffset - h;
break;
case NORTH_WEST:
offsetX = horizontalOffset;
offsetY = verticalOffset;
break;
case NORTH_EAST:
offsetX = getWidth() - horizontalOffset - w;
offsetY = verticalOffset;
break;
case SOUTH_WEST:
offsetX = horizontalOffset;
offsetY = getHeight() - verticalOffset - h;
break;
case SOUTH_EAST:
offsetX = getWidth() - horizontalOffset - w;
offsetY = getHeight() - verticalOffset - h;
break;
case CENTER:
offsetX = getWidth() / 2 - w / 2;
offsetY = getHeight() / 2 - h / 2;
break;
}
currentIcon.paintIcon(this,g2d,offsetX,offsetY);
}
private void drawBackgroundImage(final Graphics2D g2d) {
if (!isEnabled()) {
if (disabledBackgroundImage != null) {
disabledBackgroundImage.paintIcon(this, g2d, 0, 0);
}
return;
}
switch (status) {
case ROLLOVER:
if (rolloverBackgroundImage != null) {
rolloverBackgroundImage.paintIcon(this, g2d, 0, 0);
} else if (backgroundImage != null) {
backgroundImage.paintIcon(this, g2d, 0, 0);
}
break;
case PRESSED:
if (pressedBackgroundImage != null) {
pressedBackgroundImage.paintIcon(this, g2d, 0, 0);
} else if (backgroundImage != null) {
backgroundImage.paintIcon(this, g2d, 0, 0);
}
break;
case PRESSED_EXIT:
default:
if (backgroundImage != null) {
backgroundImage.paintIcon(this, g2d, 0, 0);
}
break;
}
}
private void drawText(final Graphics2D g2d) {
if(text == null || text.isEmpty()) {
return;
}
Font font = getFont();
FontMetrics fm = getFontMetrics(font);
TextLayout textLayout = new TextLayout(text, font, g2d
.getFontRenderContext());
AffineTransform affineTransform = AffineTransform
.getTranslateInstance(
(getWidth() - fm.stringWidth(text)) / 2,
getHeight() / 2 + fm.getHeight() / 4);
Shape textShape = textLayout.getOutline(affineTransform);
if(isEnabled()) {
g2d.setPaint(getForeground());
} else {
g2d.setPaint(disabledForeground != null ? disabledForeground
: DEFAULT_DISABLED_FOREGROUND);
}
g2d.fill(textShape);
g2d.draw(textShape);
}
/**
* 鼠标单击事件的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mouseClicked(MouseEvent e) {
}
/**
* 按钮按下时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mousePressed(MouseEvent e) {
if (!isEnabled()) {
return;
} else if(!SwingUtilities.isLeftMouseButton(e)) {
return;
}
status = Status.PRESSED;
repaint();
if (pressedPreferredSize != null
&& !pressedPreferredSize.equals(currentSize)) {
currentSize = pressedPreferredSize;
revalidate();
}
}
/**
* 鼠标抬起时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mouseReleased(MouseEvent e) {
if (!isEnabled()) {
return;
} else if(!SwingUtilities.isLeftMouseButton(e)) {
return;
}
if (e.getX() > 0 && e.getY() > 0 && e.getX() < getPreferredSize().width
&& e.getY() < getPreferredSize().height) {
status = Status.ROLLOVER;
if (rolloverPreferredSize != null
&& !rolloverPreferredSize.equals(currentSize)) {
currentSize = rolloverPreferredSize;
}
try {
doAction(e);
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
status = Status.DEFAULT;
if (preferredSize != null && !preferredSize.equals(currentSize)) {
currentSize = preferredSize;
}
}
repaint();
revalidate();
}
/**
* 鼠标进入时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mouseEntered(MouseEvent e) {
if (!isEnabled()) {
return;
}
status = (status == Status.PRESSED_EXIT) ? Status.PRESSED
: Status.ROLLOVER;
repaint();
if (rolloverPreferredSize != null
&& !rolloverPreferredSize.equals(currentSize)) {
currentSize = rolloverPreferredSize;
revalidate();
}
}
/**
* 鼠标离开时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mouseExited(MouseEvent e) {
if (!isEnabled()) {
return;
}
status = (status == Status.PRESSED) ? Status.PRESSED_EXIT
: Status.DEFAULT;
repaint();
if (preferredSize != null && !preferredSize.equals(currentSize)) {
currentSize = preferredSize;
revalidate();
}
}
/**
* 鼠标拖拽时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mouseDragged(MouseEvent e) {
}
/**
* 鼠标在按钮上方移动时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用
*/
public void mouseMoved(MouseEvent e) {
}
/**
* 执行按钮的动作
* @param e 鼠标事件
* @throws java.lang.Exception 如果执行的业务代码抛出的异常
*/
private void doAction(MouseEvent e) throws Exception{
if (!isEnabled()) {
return;
}
if (action != null) {
ActionEvent ae = new ActionEvent(e.getSource(), e.getID(), "", e
.getWhen(), e.getModifiers());
action.actionPerformed(ae);
}
}
/**
* 返回当前鼠标状态的最佳尺寸。这个值是不固定的,随着鼠标针对按钮的不同状态这个值会随时
* 改变,一般地这个方法只被布局管理器所调用。
* @return 当前鼠标状态的最佳尺寸
*/
@Override
public Dimension getPreferredSize() {
return currentSize;
}
/**
* 按钮各个状态的枚举
*
* @author 刘一童
*
*/
private enum Status {
/**
* 默认状态
*/
DEFAULT,
/**
* 鼠标移动到按钮之上
*/
ROLLOVER,
/**
* 在按钮上按下鼠标左键
*/
PRESSED,
/**
* 在按钮上按下鼠标左键后鼠标光标移开按钮区域
*/
PRESSED_EXIT;
}
/**
* 图标在按钮上出现位置的枚举,此按钮只允许放置一个图标,这也符合大多数按钮的规范。
* @author 刘一童
*/
public enum IconOrientation {
/**
* 图标位于按钮的正左
*/
WEST,
/**
* 图标位于按钮的正北
*/
NORTH,
/**
* 图标位于按钮的正右
*/
EAST,
/**
* 图标位于按钮的正南
*/
SOUTH,
/**
* 图标位于按钮的左上方
*/
NORTH_WEST,
/**
* 图标位于按钮的左下方
*/
SOUTH_WEST,
/**
* 图标位于按钮的右上方
*/
NORTH_EAST,
/**
* 图标位于按钮的右下方
*/
SOUTH_EAST,
/**
* 图标位于按钮的中心
*/
CENTER;
}
}[/code]
源码下载:http://file.javaxxz.com/2014/11/11/235716281.zip |
|