swing自定义组件之BUTTON(精)

swing自定义组件之BUTTON(精)
swing自定义组件之BUTTON(精)

Swing自定义组件之Button

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 https://www.360docs.net/doc/e312502923.html,ng.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 https://www.360docs.net/doc/e312502923.html,ng.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 https://www.360docs.net/doc/e312502923.html,ng.IllegalArgumentException 如果传入的参数为null

*/

public void setPressedBackgroundImage(Icon pressedBackgroundImagethrows 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 https://www.360docs.net/doc/e312502923.html,ng.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 https://www.360docs.net/doc/e312502923.html,ng.IllegalArgumentException 如果参数小于0,抛出此异常

*/

public void setHorizontalOffset(int horizontalOffset throws IllegalArgumentException{

if(horizontalOffset < 0 {

throw new IllegalArgumentException("horizontalOffset must >=0";

}

this.horizontalOffset = horizontalOffset;

}

/**

* 设置垂直偏移量

* @param verticalOffset 垂直偏移量

* @throws https://www.360docs.net/doc/e312502923.html,ng.IllegalArgumentException 如果参数小于0,抛出此异常

*/

public void setVerticalOffset(int verticalOffset throws IllegalArgumentException{

if(verticalOffset < 0 {

throw new IllegalArgumentException("verticalOffset must >=0";

}

this.verticalOffset = verticalOffset;

}

/**

* 设置按钮的图标

* @param icon 图标对象

* @throws https://www.360docs.net/doc/e312502923.html,ng.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 https://www.360docs.net/doc/e312502923.html,ng.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 https://www.360docs.net/doc/e312502923.html,ng.IllegalArgumentException 如果不在0.0f~1.0f之间会抛出此异常

*/

public synchronized void setAlpha(float alphathrows 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;

}

/**

* 指定这个按钮的动作

* @param action 按钮的动作

*/

public void setAction(Action action {

this.action = action;

}

/**

* 覆盖JComponent.setEnabled(boolean enabled,设置是否按钮可用或被禁止

* @param enabled 如果按钮可用则为true,否则为false

* @see https://www.360docs.net/doc/e312502923.html,ponent#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_OVE R,

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;

相关主题
相关文档
最新文档