代码改变世界

Java7的一个新类——JLayer:装饰的Swing组件

2012-03-13 21:01  java线程例子  阅读(670)  评论(0编辑  收藏  举报

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.csdn.net/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(CSDN)");
		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();
		}
	}
}