Swing自定义组件之Button
GUI组件要完成的任务有2个,展现与业务。对于按钮来说,文本、图标、边框、背景属于展现层,而这些元素在按钮不同状态下会不尽相同,一般来说至少有4种状态下的展现:普通、鼠标放在其上、被按下、被禁用,也就是按钮应该具备这4种状态。
下面给出的示例是swing 实现的自定义按钮。 |
通常swing自定义组件继承javax.swing.JComponent并重写protectedvoid paint Component(Graphics g)方法实现自定义绘制。重写paintComponent方法时通常要先去掉super.paintComponent(g),因为父方法调用会绘制背景色。不妨先看一下
源代码中的调用过程。
在JComponent.java中paintComponent(Graphicsg)方法定义如下:protectedv
oid paintComponent(Graphics g) { |
}
}
}
其中ui的声明如下
protectedtransient ComponentUI ui;
然后转向ComponentUI的update(Graphicsg, JComponent c)方法:
publicvoid update(Graphics g, JComponent c) {
if(c.isOpaque()) {
g.setColor(c.getBackground()); |
|
}
可见如果发现组件是非透明的,就绘制背景,可以看出swing组件的setBackground方法如何绘制背景的。
一般简单的自定义组件,你可以只通过重写paintComponent 方法来实现绘制,对 于一般的组件这已经足够。对于自定义按钮一般的原则是准备4 张背景图对应上述4 种状态,这4 种状态都可通过鼠标****来感知,当状态改变时,调用repaint()使Button |
重绘。除了背景,按钮文本、图标等的改变一样也必须调用repaint()来刷新。
然后重要的一点是你必须重写publicDimension getPreferredSize()来获得按钮的最佳尺寸。getPreferredSize方法对于布局管理器来说至关重要,布局管理器会通过getPreferredSize的判断组件的最佳大小,并进行布局。而对于本范例而言,getPr
eferredSize 的大小只和背景图片大小有关。 |
*Created on 2007 年11月11日,下午6:30
*
* To change this template, choose Tools | Template Manager * and openthe template in the editor.
*/
packagedemo.swing;
importjava.awt.AlphaComposite;
importjava.awt.Color;
importjava.awt.Container;
importjava.awt.Dimension;
import java.awt.Font; |
|
importjava.awt.RenderingHints;
importjava.awt.Shape;
importjava.awt.event.ActionEvent;
importjava.awt.event.MouseEvent;
importjava.awt.font.TextAttribute;
importjava.awt.font.TextLayout;
importjava.awt.geom.AffineTransform;
importjava.util.HashMap;
importjavax.swing.Action;
importjavax.swing.Icon;
importjavax.swing.JComponent;
importjavax.swing.SwingConstants;
importjavax.swing.SwingUtilities;
importjavax.swing.event.MouseInputListener; /**
* 为需要具备专业外观的按钮提供方便的实现。通过此类提供的若干方 |
*/
private Icon backgroundImage;
/**
* 通常状态下的最佳尺寸
*/
private Dimension preferredSize;
/**
* 鼠标光标在上方时的背景图片
*/
private Icon rolloverBackgroundImage;
/** | |
/**
* 按钮被按下时的背景图片
*/
private Icon pressedBackgroundImage;
/**
* 按钮被按下时的最佳尺寸
*/
private Dimension pressedPreferredSize;
/**
* 按钮被禁止时的背景图片
*/
private Icon disabledBackgroundImage;
/**
* 按钮被禁止时的最佳尺寸
*/
private Dimension disabledPreferredSize; |
private Icon icon;
/**
* 按钮被禁止时的图标
*/
private Icon disabledIcon;
/**
* 按钮图标出现的位置
*/
private IconOrientation iconOrientation = IconOrientation.WES T;
/** |
|
privatestatic final Dimension DEFAULT_SIZE = new Dimension(10
0,25);
/**
*水平偏移量
*/
privateint horizontalOffset = DEFAULT_HORIZONTAL_OFFSET;
/**
*默认的水平偏移量
*/
privatestatic final int DEFAULT_HORIZONTAL_OFFSET = 4;
/**
*垂直偏移量
*/
privateint verticalOffset = DEFALUT_VERTICAL_OFFSET;
/**
* 默认的垂直偏移量 |
*按钮文本的字体
*/
privateFont font;
/**
*按钮文本的默认字体
*/
privatestatic final Font DEFAULT_FONT = Font.decode("Dialog-P
lain-14");
/**
*按钮被禁止时候文字的颜色
*/ | |
*/
private final Color DEFAULT_DISABLED_FOREGROUND = new Color(19 2,192, 192);
/**
* 按钮的状态
*/
private volatile Status status = Status.DEFAULT;
/**
* 按钮的颜色透明度
*/
private volatile float alpha = 1.0f;
private static HashMap< RenderingHints.Key, Object> renderingHints;
/**
* 按钮执行的动作
*/ |
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 如果传入的参数 |
}
this.backgroundImage = backgroundImage;
preferredSize = new Dimension(backgroundImage.getIconWid th(),
backgroundImage.getIconHeight());
currentSize = preferredSize;
}
/**
* 设置被禁用状态下按钮的背景。调用该方法会影响到按钮在禁用状态下的最佳尺寸,
* 由传入的图标参数对应的图标尺寸决定 |
*/
public void setDisabledBackgroundImage(Icon disabledBackgrou ndImage)throws IllegalArgumentException {
if (disabledBackgroundImage == null) {
throw new IllegalArgumentException(
"disabledBackgroundImage can't be null"); }
this.disabledBackgroundImage = disabledBackgroundImage; disabledPreferredSize = new Dimension(disabledBackground Image
.getIconWidth(), disabledBackgroundImage.getIco nHeight());
}
/**
* 设置在被按下状态时按钮的背景。调用该方法会影响到按钮在被
按下状态时的最佳尺寸, |
if (pressedBackgroundImage == null) {
throw new IllegalArgumentException(
"pressedBackgroundImage can't be null"); }
this.pressedBackgroundImage = pressedBackgroundImage; pressedPreferredSize = new Dimension(pressedBackgroundIm age
.getIconWidth(), pressedBackgroundImage.getIcon Height());
}
/** |
* @param rolloverBackgroundImage 鼠标指针在其上方时的背景图标对象
* @throws java.lang.IllegalArgumentException 如果传入的参数为null
*/
public void setRolloverBackgroundImage(Icon rolloverBackgrou ndImage)throws IllegalArgumentException {
if (rolloverBackgroundImage == null) {
throw new IllegalArgumentException(
"rolloverBackgroundImage can't be null"); }
this.rolloverBackgroundImage = rolloverBackgroundImage; rolloverPreferredSize = new Dimension(rolloverBackground Image
.getIconWidth(), rolloverBackgroundImage.getIco
nHeight()); |
public void setHorizontalOffset(int horizontalOffset) throwsIllegalArgumentException{
if(horizontalOffset < 0) {
throw
newIllegalArgumentException("horizontalOffset must >=0");
}
this.horizontalOffset = horizontalOffset;
}
/**
* 设置垂直偏移量 |
*/
public void setVerticalOffset(int verticalOffset) throws IllegalArgumentException{
if(verticalOffset < 0) {
throw new IllegalArgumentException("verticalOffset m ust>=0");
}
this.verticalOffset = verticalOffset;
}
/**
* 设置按钮的图标
* @param icon 图标对象
* @throws java.lang.IllegalArgumentException 如果传入的参数为null,抛出此异常
*/
public synchronized void setIcon(Icon icon) throws IllegalArgu |
/**
* 设置按钮在被禁用时显示的图标
* @param disabledIcon 图标对象
* @throws java.lang.IllegalArgumentException 如果传入的参数为null,抛出此异常
*/
public synchronized void setDisabledIcon(Icon disabledIcon) throwsIllegalArgumentException {
if(disabledIcon == null) {
throw new IllegalArgumentException(
} | "disabledIcon can't be null"); | |
} | | |
public void setIconOrientation(IconOrientation iconOrientati on)throws IllegalArgumentException{
if(iconOrientation == null) {
throw new IllegalArgumentException(
"iconOrientation can't be null");
}
this.iconOrientation = iconOrientation;
}
public void setDisabledForeground(Color disabledForeground) {
this.disabledForeground = disabledForeground;
}
/**
* 设置按钮的透明度
*@paramalpha透明度。范围在0.0f~1.0f之间。0.0f为完全透明,
1.0f 为完全显示 |
"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) { |
public void setAction(Action action) {
this.action = action;
}
/**
* 覆盖JComponent.setEnabled(booleanenabled),设置是否按钮可用或被禁止
* @param enabled 如果按钮可用则为true,否则为false
* @see java.awt.Component#isEnabled
*/
@Override
public void setEnabled(boolean enabled) { |
&& !disabledPreferredSize.equals(currentSiz e)) {
currentSize = disabledPreferredSize;
revalidate();
}
}
}
/**
* 绘制按钮边框
* @param g 图形上下文
* @deprecated 此方法只在内部被调用,外部不要显式调用 */
@Override
protected void paintBorder(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(AlphaCo | ||
mposite.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: |
caseNORTH:
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; |
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); |
return;
}
switch (status) {
caseROLLOVER:
if (rolloverBackgroundImage != null) {
rolloverBackgroundImage.paintIcon(this, g2d, 0, 0);
} elseif (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()) { |
.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 ? disabledFor eground
: DEFAULT_DISABLED_FOREGROUND); | |
} | |
}
/**
* 鼠标单击事件的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用 */
public void mouseClicked(MouseEvent e) {
}
/**
* 按钮按下时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用 */
public void mousePressed(MouseEvent e) {
if (!isEnabled()) {
return; |
currentSize = pressedPreferredSize;
revalidate();
}
}
/**
* 鼠标抬起时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用 */
public void mouseReleased(MouseEvent e) {
if (!isEnabled()) { |
}
if (e.getX() > 0 && e.getY() > 0 && e.getX() <getPreferredS ize().width
&& e.getY() < getPreferredSize().height) { status = Status.ROLLOVER;
if (rolloverPreferredSize != null
&& !rolloverPreferredSize.equals(currentSiz e)) {
currentSize = rolloverPreferredSize;
}
try {
doAction(e);
} catch (Exception ex) {
ex.printStackTrace();
}
} else { |
revalidate();
}
/**
* 鼠标进入时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用 */
public void mouseEntered(MouseEvent e) {
if (!isEnabled()) {
return;
} |
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 |
}
/**
* 鼠标拖拽时的处理
* @param e 鼠标事件
* @deprecated 此方法只在内部被调用,外部不要显式调用 */
public void mouseDragged(MouseEvent e) {
}
/**
* 鼠标在按钮上方移动时的处理
* @param 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.get ID(), "",e
.getWhen(), e.getModifiers());
action.actionPerformed(ae);
} |
@Override
public Dimension getPreferredSize() {
return currentSize;
}
/**
* 按钮各个状态的枚举
*
* @author 刘一童
*
*/
private enum Status { /** | |
DEFAULT,
/**
* 鼠标移动到按钮之上
*/
ROLLOVER,
/**
* 在按钮上按下鼠标左键
*/
PRESSED,
/**
* 在按钮上按下鼠标左键后鼠标光标移开按钮区域 */
PRESSED_EXIT;
}
/**
* 图标在按钮上出现位置的枚举,此按钮只允许放置一个图标,这 |
WEST,
/**
* 图标位于按钮的正北
*/
NORTH,
/**
* 图标位于按钮的正右
*/
EAST,
/**
* 图标位于按钮的正南 */ | |
* 图标位于按钮的左上方
*/
NORTH_WEST,
/**
* 图标位于按钮的左下方
*/
SOUTH_WEST,
/**
* 图标位于按钮的右上方
*/
NORTH_EAST,
/**
* 图标位于按钮的右下方
*/
SOUTH_EAST,
/** |