Java7中的一个新的类,JLayer,也已被添加到提高的Java Swing的能力。 JLayer主要用于装饰的Swing组件。你可以用它改变如何绘制组件,以及它们如何响应事件。下面是这个类的一个简单的例子:
先看运行结果:
1、鼠标进入登陆框时:聚光灯效果(Spotlight)
2、点击登录按钮:
import java.awt.AWTEvent;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.plaf.LayerUI;
/**
* @author http://blog.youkuaiyun.com/Cannel_2020
*
*/
public class TestJLayer {
private static JFrame loginFrame;
private LoginLayerUI layerUI;//LayerUI的一个子类
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJLayer().createUI();
}
});
}
public void createUI() {
loginFrame = new JFrame("From Cannel_2020's blog(优快云)");
loginFrame.setSize(340, 260);
loginFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
loginFrame.setLocationRelativeTo(null);
JPanel panel = createPanel();
layerUI = new LoginLayerUI();
/*
* JLayer的构造函数:JLayer(V view, LayerUI<V> ui),
* 第一个参数view为Component的一个子类,就是要要装饰的Swing组件
* 第二个参数为LayerUI<V>的一个实例,指定
*/
JLayer<JPanel> jlayer = new JLayer<JPanel>(panel, layerUI);
//注意这个add的是JLayer<JPanel>的一个实例
loginFrame.add(jlayer);
loginFrame.setVisible(true);
}
private JPanel createPanel() {
final Image background = new ImageIcon("image/background.jpg").getImage();//背景图片
JPanel panel = new JPanel() {
private static final long serialVersionUID = 6246862165441423926L;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(background, 0, 0, loginFrame.getWidth(),
loginFrame.getHeight(), this);
}
};
panel.setLayout(null);
Font font = new Font("微软雅黑",Font.PLAIN,14);
JLabel accountLabel = new JLabel("账号:");
accountLabel.setFont(font);
accountLabel.setBounds(40, 60, 40, 15);
JLabel pswLabel = new JLabel("密码:");
pswLabel.setFont(font);
pswLabel.setBounds(40, 98, 40, 15);
final JTextField userName = new JTextField();
userName.setFont(font);
userName.setBounds(88, 53, 155, 33);
userName.addFocusListener(new FocusAdapter(){
public void focusGained(FocusEvent e) {
userName.selectAll();
}
});
JPasswordField password = new JPasswordField();
password.setBounds(88, 90, 155, 33);
JLabel registerLabel = new JLabel("注册");
registerLabel.setFont(font);
registerLabel.setBounds(260, 60, 28, 19);
JLabel findBackLabel = new JLabel("找回");
findBackLabel.setFont(font);
findBackLabel.setBounds(260, 98, 28, 19);
JCheckBox rmbCBox = new JCheckBox("记住密码");
rmbCBox.setSelected(true);
rmbCBox.setBounds(90, 140, 77, 14);
rmbCBox.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() == ItemEvent.DESELECTED){
System.out.println("rmbCBox(JCheckBox)被选中!");
}
}
});
JCheckBox autoCBox = new JCheckBox("自动登录");
autoCBox.setBounds(175, 140, 77, 14);
JButton setButton = new JButton("设置");
setButton.setBounds(50, 170, 75, 27);
JButton loginButton = new JButton("登录");
loginButton.setFont(font);
loginButton.setBounds(90, 170, 75, 27);
//5秒钟后停止
final Timer stopper = new Timer(5000, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
layerUI.stop();
}
});
stopper.setRepeats(false);//设置计时器只在第一次时触发然后停止
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
layerUI.start();
//若stopper不是在运行,则启动stopper。
if (!stopper.isRunning()) {
stopper.start();
}
}
});
JButton exitButton = new JButton("退出");
exitButton.setFont(font);
exitButton.setBounds(170, 170, 75, 27);
exitButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
panel.add(userName);
panel.add(password);
panel.add(accountLabel);
panel.add(pswLabel);
panel.add(registerLabel);
panel.add(findBackLabel);
panel.add(rmbCBox);
panel.add(autoCBox);
panel.add(loginButton);
panel.add(exitButton);
return panel;
}
}
class LoginLayerUI extends LayerUI<JPanel> implements ActionListener {
private static final long serialVersionUID = 1L;
private boolean mIsRunning = false;// 点击按钮之后所触发的线程是否在运行
private boolean mIsFadingOut = false;//控制线程的停止,和处理
private Timer mTimer;// 在指定时间间隔触发一个或多个 ActionEvent。
private int mAngle = 0;//用于旋转时计算角度
private int mFadeCount = 0;//结合mFadeLimit用于设置针形旋转图案整体的渐变的效果
private int mFadeLimit = 30;
private boolean mActive;// 是否进入窗口内
private int mX, mY;// 记录鼠标的坐标
/*
* 渲染效果的过程中会不断调用paint
*/
public void paint(Graphics g, JComponent c) {
int w = c.getWidth();//获取组件c的宽度
int h = c.getHeight();
super.paint(g, c);//调用父类的paint方法,绘画组件c
Graphics2D g2 = (Graphics2D) g.create();
// 设置聚光灯(Spotlight)效果
if (mActive) {
// 创建径向渐变,中间透明。
java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX,
mY);
float radius = 72;
float[] dist = { 0.0f, 1.0f };
Color[] colors = { new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.black };
//RadialGradientPaint 类提供使用圆形辐射颜色渐变模式填充某一形状的方式
RadialGradientPaint p = new RadialGradientPaint(center, radius,
dist, colors);
//setPaint:为 Graphics2D 上下文设置 Paint 属性
g2.setPaint(p);
/*为 Graphics2D 上下文设置 Composite。
*Composite 用于所有绘制方法中,如 drawImage、drawString、draw 和 fill
*这里使用AlphaComposite 类实现一些基本的 alpha 合成规则
*将源色与目标色组合,在图形和图像中实现混合和透明效果
*/
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
.6f));
//填充是绘画
g2.fillRect(0, 0, c.getWidth(), c.getHeight());
}
if (!mIsRunning) {
return;
}
float fade = (float) mFadeCount / (float) mFadeLimit;
//fade的不同形成:12根针整体刚才出现的渐变效果,和登录成功后的针消失过程的渐变效果
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
.5f * fade));
g2.fillRect(0, 0, w, h);
int s = Math.min(w, h) / 5;
int cx = w / 2;
int cy = h / 2;
/* 为呈现算法设置单个首选项的值。
* RenderingHints.KEY_ANTIALIASING:抗锯齿提示键。
* RenderingHints.VALUE_ANTIALIAS_ON:抗锯齿提示值——使用抗锯齿模式完成呈现
*/
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
/*
* setStroke:设置呈现过程中要画的图形
* BasicStroke 类定义针对图形图元轮廓呈现属性的一个基本集合
*/
g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
//设置绘画颜色
g2.setPaint(Color.white);
/* rotate:旋转转换连接
* 第一个参数 Math.PI * mAngle / 180:旋转的角度,以弧度为单位
* 第二个参数 cx:x - 旋转原点的 x 坐标
* 第二个参数 cy:y - 旋转原点的 y 坐标
*/
g2.rotate(Math.PI * mAngle / 180, cx, cy);
//12跟针,各个针的颜色是不同的,针与针之间形成渐变的效果
for (int i = 0; i < 12; i++) {
float scale = (11.0f - (float) i) / 11.0f;
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
//通过设置AlphaComposite.getInstance()第二个参数的不同来实现12“针”颜色的不同
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
scale * fade));
}
//释放此图形的上下文以及它使用的所有系统资源。
g2.dispose();
}
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer) c;
// 启动鼠标事件和鼠标移动事件
jlayer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer) c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
// JLayer对象专用的鼠标事件,在installUI中启动事件,在uninstallUI中禁用事件
protected void processMouseEvent(MouseEvent e, JLayer l) {
if (e.getID() == MouseEvent.MOUSE_ENTERED)
mActive = true;
if (e.getID() == MouseEvent.MOUSE_EXITED)
mActive = false;
l.repaint();
}
// JLayer对象专用的鼠标移动事件,在installUI中启动事件,在uninstallUI中禁用事件
// 这里把鼠标坐标记录在mX和mY中
protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
// 确定鼠标移动事件相对于层的坐标位置。
Point p = SwingUtilities
.convertPoint(e.getComponent(), e.getPoint(), l);
mX = p.x;
mY = p.y;
l.repaint();
}
public void actionPerformed(ActionEvent e) {
if (mIsRunning) {
//firePropertyChange():通知的内部状态的更新
firePropertyChange("tick", 0, 1);
mAngle += 3;
if (mAngle >= 360) {
mAngle = 0;
}
if (mIsFadingOut) {
if (--mFadeCount == 0) {
mIsRunning = false;
mTimer.stop();
}
//防止AlphaComposite.getInstance()的第二个参数的值超出范围
} else if (mFadeCount < mFadeLimit) {
mFadeCount++;
}
}
}
public void start() {
if (mIsRunning) {
return;
}
mIsRunning = true;
mIsFadingOut = false;
mFadeCount = 0;
int fps = 24;
int tick = 1000 / fps;
//每tick毫秒调用一次本类中的actionPerformed方法
mTimer = new Timer(tick, this);
mTimer.start();
}
public void stop() {
mIsFadingOut = true;
}
//不断repaint()窗口
public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
if ("tick".equals(pce.getPropertyName())) {
l.repaint();
}
}
}