zoukankan      html  css  js  c++  java
  • java之数字彩虹雨

    © 版权声明:本文为博主原创文章,转载请注明出处

    数字彩虹雨:

      从上至下,随机出现一串字符串,以不同的速度运行到底部;类似于黑客帝国里面的场景

    GitHub:https://github.com/TabooNight/BinaryRain

    实例:

    1.代码结构

    2.pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
      	<modelVersion>4.0.0</modelVersion>
    
    	<groupId>org.game.BinaryRain</groupId>
    	<artifactId>BinaryRain</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<packaging>jar</packaging>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	</properties>
    
    	<dependencies>
    		<!-- junit -->
    		<dependency>
    			<groupId>junit</groupId>
    			<artifactId>junit</artifactId>
    			<version>4.12</version>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    </project>
    

    3.BinaryRain.properties

    foreground=default
    background=default
    width=default
    height=default
    characters=default
    colorful=true
    music=BinaryRainMusic.wav
    

    4.BinaryRain.java

    package org.game.BinaryRain;
    
    import java.applet.Applet;
    import java.applet.AudioClip;
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.Toolkit;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.Properties;
    
    import javax.swing.JDialog;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    
    /**
     * 数字彩虹雨
     *
     */
    public class BinaryRain extends JDialog {
    	
    	private static final long serialVersionUID = 1L;
    	
    	private Color foreground, background;// 前景色、背景色
    	private Dimension size;//Dimension类封装单个对象中组件的高度和宽度(精确到整数)
    	private char[] RAIN_CHARACTERS;// 字符数组
    	private boolean isColorful;// 颜色是否铺满
    	private boolean hasMusic;// 是否播放音乐
    	private AudioClip music;// 音频对象
    	private boolean isStart = false;// 是否开始
    	private RainPanel panel = new RainPanel();// RainPanel对象
    	private ArrayList<Rain> rains = new ArrayList<Rain>();
    	private Font rainFont = new Font("arial", Font.BOLD, 15);;// 创建文字雨的字体
    	
    	/**
    	 * 通过构造方法初始化
    	 */
    	private BinaryRain() {
    		
    		try {
    			initProperties();
    			init();
    		} catch (Exception e) {
    			JOptionPane.showMessageDialog(null, "Failed to init.
    " + e, 
    					"BinaryRain", JOptionPane.ERROR_MESSAGE);
    			System.exit(1);// 非正常退出
    		}
    		
    	}
    	
    	
    	/**
    	 * 读取配置文件并初始化
    	 * 
    	 * @throws Exception
    	 */
    	private void initProperties() throws Exception {
    		
    		Properties props = new Properties(); 
    		File file = new File(System.getProperty("user.dir") 
    				+ "/src/main/resources/BinaryRain.properties");// 获取配置文件
    		boolean dw = true, dh = true, df =true, db = true, dc = true, dcf = true;
    
    		if (file.exists() && file.isFile()) {
    			InputStream fis = new FileInputStream(file);// 创建文件输入流
    			props.load(fis);// 加载属性文件
    
    			// 获取前景色,默认default
    			String strFore = props.getProperty("foreground", "default").toLowerCase();
    			if (!"default".equals(strFore)) {
    				df = false;
    				foreground = getColor(strFore);// 获取颜色
    				if (foreground == null) {
    					foreground = Color.getColor(strFore, Color.GREEN);// 获取颜色对象,默认绿色
    				}
    			}
    			
    			// 获取背景色,默认default
    			String strBack = props.getProperty("background", "default").toLowerCase();
    			if (!"default".equals(strBack)) {
    				db = false;
    				background = getColor(strBack);// 获取颜色
    				if (background == null) {
    					background = Color.getColor(strBack, Color.GREEN);// 获取颜色对象,默认绿色
    				}
    			}
    			
    			// 获取宽度
    			size = new Dimension();
    			String strW = props.getProperty("width", "default").toLowerCase();
    			if (!"default".equals(strW)) {
    				dw = false;
    				size.width = Integer.valueOf(strW);
    			}
    			
    			// 获取高度
    			String strH = props.getProperty("height", "default").toLowerCase();
    			if (!"default".equals(strH)) {
    				dh = false;
    				size.width = Integer.valueOf(strH);
    			}
    			
    			// 获取字符数组
    			String strC = props.getProperty("characters", "default");
    			if (!"default".equalsIgnoreCase(strC)) {
    				dc = false;
    				String[] cs = strC.split(",");
    				RAIN_CHARACTERS = new char[cs.length];
    				for (int i = 0, s = RAIN_CHARACTERS.length; i < s; i++) {
    					RAIN_CHARACTERS[i] = cs[i].charAt(0);
    				}
    			}
    			
    			// 判断颜色是否铺满
    			String strCF = props.getProperty("colorful", "default");
    			if (!"default".equalsIgnoreCase(strCF)) {
    				dcf = false;
    				isColorful = Boolean.valueOf(strCF);
    			}
    			
    			// 判断是否播放音乐
    			String strM = props.getProperty("music", "default");
    			if (!"default".equalsIgnoreCase(strM)) {
    				File musicFile = new File(System.getProperty("user.dir") 
    						+ "/src/main/resources/" + strM);
    				if (musicFile.exists() && musicFile.isFile()) {
    					if ((music = Applet.newAudioClip(musicFile.toURI().toURL())) != null) {
    						hasMusic = true;
    					}
    				}
    			}
    			
    			fis.close();
    			
    		}
    		
    		if (dw & dh) {// 高度和宽度都是default,获取屏幕高和宽
    			size = Toolkit.getDefaultToolkit().getScreenSize();
    		} else if (dw) {// 宽度是default,获取屏幕宽度
    			size.width = Toolkit.getDefaultToolkit().getScreenSize().width;
    		} else if (dh) {// 高度是default,获取屏幕高度
    			size.height = Toolkit.getDefaultToolkit().getScreenSize().height;
    		}
    		
    		if (df) {// 前景色是default
    			foreground = Color.GREEN;
    		}
    		
    		if (db) {// 背景色是default
    			background = Color.BLACK;
    		}
    		
    		if (dc) {// 字符数组是的default
    			RAIN_CHARACTERS = new char[126 - 33 + 1];
    			for (int c = 0, i = 33, l = RAIN_CHARACTERS.length; c < l; c++, i++) {
    				RAIN_CHARACTERS[c] = (char) i;
    			}
    		}
    		
    		if (dcf) {// 颜色铺满是default
    			isColorful = false;
    		}
    		
    	}
    	
    	/**
    	 * 根据字符串获取其对应的颜色
    	 * 
    	 * @param color
    	 * 				颜色
    	 * @return
    	 */
    	private Color getColor(String color) {
    		
    		if (color == null || color.isEmpty()) {
    			return null;
    		}
    		if (color.startsWith("#")) {
    			int i = Integer.valueOf(color.substring(1), 16);
    			return new Color(i);
    		}
    		// [\d]:数字		[\p{Blank}]:空格或制表符
    		if (color.matches("[\d]+[\p{Blank}]*,[\p{Blank}]*[\d]+[\p{Blank}]*,[\p{Blank}]*[\d]+")) {
    			String[] cs = color.split("[\p{Blank}]*,[\p{Blank}]");
    			if (cs.length != 3) {
    				return null;
    			}
    			int r = Integer.valueOf(cs[0]);
    			int g = Integer.valueOf(cs[1]);
    			int b = Integer.valueOf(cs[2]);
    			return new Color(r, g, b);
    		}
    		return null;
    		
    	}
    	
    	/**
    	 * 初始化窗口
    	 */
    	private void init() {
    		
    		setAlwaysOnTop(true);// 设置窗口靠前
    		setResizable(false);// 不能改变大小
    		setUndecorated(true);// 设置此frame窗口失去边框和标题栏的修饰(必须在setVisible之前)
    		setTitle("Binary Rain");// 设置标题
    		// 创建一个BufferedImage对象
    		BufferedImage cursor = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB_PRE);
    		setCursor(Toolkit.getDefaultToolkit().createCustomCursor(cursor, 
    				new Point(8, 8), "Disable Cursor"));// 确认光标的形状
    		setSize(size);// 设置窗口大小
    		setLocationRelativeTo(null);// 设置窗口相对于指定组件的位置,null表示位于屏幕的中央
    		
    		addKeyListener(new KeyAdapter() {// 新增一个按键侦听器
    			@Override
    			public void keyPressed(KeyEvent e) {
    				if ((e.isAltDown() && e.getKeyCode() == KeyEvent.VK_F4) 
    						|| (e.getKeyCode() == KeyEvent.VK_ESCAPE)) {// 按下Alt+F4或者Esc键
    					setVisible(false);// 设置窗口不可见
    					System.exit(0);// 正常停止程序
    				}
    			}
    		});
    		
    		addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				if (isRaining()) {
    					stop();
    				}
    				System.exit(0);// 正常停止程序
    			}
    		});
    		
    		add(panel, BorderLayout.CENTER);// 将指定组件添加到此容器
    		
    	}
    	
    	/**
    	 * 重写setVisible方法,当不显示时停止创建文字雨
    	 */
    	public void setVisible(boolean flag) {
    		
    		super.setVisible(flag);
    		if (!flag) {
    			stop();
    		}
    		
    	}
    	
    	/**
    	 * 是否开始创建文字雨
    	 * 
    	 * @return
    	 */
    	public boolean isRaining() {
    		
    		return isStart;
    		
    	}
    	
    	/**
    	 * 停止文字雨
    	 */
    	public void stop() {
    		
    		isStart = false;// 开始标识置为false
    		if (hasMusic) {// 播放音乐
    			music.stop();// 停止播放音乐
    		}
    		
    	}
    	
    	/**
    	 * 开始一个新的线程,创建一条文字雨,使用synchronized保证一个时间内只有一条线程可以执行
    	 */
    	private synchronized void newRain() {
    		
    		Rain r = new Rain(getRandomLength(), (int) (Math.random() * size.width), 
    				(int) (Math.random() * -60 * 15), (int) (Math.random() * 8 + 2), 
    				(float) (Math.random() * 10 + 10));
    		rains.add(r);
    		new Thread(r).start();
    		
    	}
    	
    	/**
    	 * 获取随机长度(10-50)
    	 * 
    	 * @return
    	 */
    	public int getRandomLength() {
    		
    		return (int) (Math.random() * 40 + 10);
    		
    	}
    	
    	/**
    	 * 获取随机字符串
    	 * 
    	 * @return
    	 */
    	public String getRandomChar() {
    		
    		return String.valueOf(RAIN_CHARACTERS[(int) (Math.random() * RAIN_CHARACTERS.length)]);
    		
    	}
    	
    	/**
    	 * 获取面板大小
    	 * 
    	 * @return
    	 */
    	public Dimension getFrameSize() {
    		
    		return size;
    		
    	}
    	
    	public void start() {
    		
    		if (hasMusic) {// 播放音乐
    			music.loop();// 循环播放
    		}
    		
    		for (int c = 0, s = 108; c < s; c++) {// 创建108条文字雨
    			newRain();
    		}
    		
    		isStart = true;
    		
    		for (Rain r: rains) {
    			new Thread(r).start();
    		}
    		
    		new Thread(new Runnable() {
    			
    			public void run() {
    			
    				while (isStart) {
    					panel.repaint();
    				}
    				
    			}
    		}).start();
    		
    	}
    	
    	/**
    	 * 创建文字雨面板
    	 *
    	 */
    	private final class RainPanel extends JPanel {
    		
    		private static final long serialVersionUID = 1L;
    
    		public RainPanel() {
    			
    		}
    
    		private final Color[] COLORS = new Color[] {// 文字雨文本颜色集合
    			new Color(255, 0, 0),
                new Color(255, 165, 0),
                new Color(255, 255, 0),
                new Color(0, 255, 0),
                new Color(0, 127, 0),
                new Color(0, 127, 255),
                new Color(139, 0, 255)
            };
    
    		@Override
    		public void paint(Graphics g) {
    			
    			if (isStart) {
    				BufferedImage img = new BufferedImage(size.width, size.height, 
    						BufferedImage.TYPE_INT_RGB);// 创建一个BufferedImage对象
    				Graphics2D g2 = (Graphics2D) img.getGraphics();// 获取Graphics2D对象
    				g2.setColor(background);// 设置颜色
    				g2.fillRect(0, 0, size.width, size.height);// 用预定的颜色填充一个矩形
    				g2.setColor(foreground);// 设置颜色
    				// 克隆所有文字雨信息到Collection中
    				@SuppressWarnings("unchecked")
    				Collection<Rain> collection = (Collection<Rain>) rains.clone();
    				for (Iterator<Rain> it = collection.iterator(); it.hasNext();) {
    					Rain r = it.next();
    					if (r.isEnd()) {// 该条文字雨已经结束
    						rains.remove(r);// 将该条文字雨从集合中移除
    						newRain();// 创建一条新的文字雨
    						continue;
    					}
    					if (isColorful) {// 颜色铺满
    						g2.setFont(rainFont.deriveFont(r.getSize()));// 设置文字雨文本大小
    						String[] ss = r.getRainChars();// 获取文字雨文本内容
    						int x = r.getX();// 获取文字雨X轴坐标
    						int y = r.getY() - ss.length * 15;// 获取文字雨Y轴坐标
    						for (int i = 0, sl = ss.length; i < sl; i++) {
    							if (i < 7) {
    								g2.setColor(COLORS[i]);
    							} else {
    								g2.setColor(COLORS[i % 7]);
    							}
    							g2.drawString(ss[i], x, y);
    							y += 15;
    						}
    					} else {
    						g2.setFont(rainFont.deriveFont(r.getSize()));// 设置文字雨文本大小
    						String[] ss = r.getRainChars();// 获取文字雨文本内容
    						int x = r.getX();// 获取文字雨X轴坐标
    						int y = r.getY() - ss.length * 15;// 获取文字雨Y轴坐标
    						for (String s: ss) {
    							g2.drawString(s, x, y);
    							y += 15;
    						}
    					}
    				}
    				g.drawImage(img, 0, 0, this);// 绘制指定图像
    			}
    			
    		}
    		
    	}
    	
    	/**
    	 * 通过线程创建一条条文字雨
    	 *
    	 */
    	private final class Rain implements Runnable {
    		
    		private int rainSpeed;// 下雨的速度
    		private final String[] rainChars;// 下雨的文本
    		private int rainX, rainY;// 文本坐标系
    		private float fontSize;// 文本大小
    
    		/**
    		 * 初始化一条文字雨
    		 * 
    		 * @param length
    		 * 					文字雨的文本长度
    		 * @param x
    		 * 					x坐标
    		 * @param y
    		 * 					y坐标
    		 * @param speed
    		 * 					下雨速度
    		 * @param size
    		 * 					文本大小
    		 */
    		public Rain(int length, int x, int y, int speed, float size) {
    			
    			if (speed < 1) {
    				throw new RuntimeException("The speed must be greater than or equal to 1.");
    			}
    			
    			if (length < 5) {
    				length = getRandomLength();
    			}
    			
    			if (size < 1.0f) {
    				size = 15.0f;
    			}
    			
    			rainChars = new String[length + 1];
    			for (int i = 0; i < length; i++) {
    				rainChars[i] = getRandomChar();
    			}
    			rainChars[length] = " ";
    			this.rainX = x;
    			this.rainY = y;
    			this.rainSpeed = speed;
    			this.fontSize = size;
    			
    		}
    
    		/**
    		 * 执行文字雨
    		 */
    		public void run() {
    			
    			while (isRaining() && rainY < getFrameSize().height + (rainChars.length + 1) * 15) {
    				if (rainSpeed <= 0) {// 文字雨的速度小于等于0,结束
    					break;
    				}
    				try {
    					Thread.sleep(rainSpeed);// 睡眠
    				} catch (InterruptedException ex) {
    					ex.printStackTrace();
    				}
    				rainY += 2;// 每次向下运动2
    			}
    			rainSpeed = -1;// 文字雨结束,速度置为-1
    			
    		}
    		
    		/**
    		 * 获取文本内容
    		 * 
    		 * @return
    		 */
    		public String[] getRainChars() {
    			
    			return rainChars;
    			
    		}
    		
    		/**
    		 * 获取文字雨X轴坐标
    		 * 
    		 * @return
    		 */
    		public int getX() {
    			
    			return rainX;
    			
    		}
    		
    		/**
    		 * 获取文字雨Y轴坐标
    		 * 
    		 * @return
    		 */
    		public int getY() {
    			
    			return rainY;
    			
    		}
    		
    		/**
    		 * 获取文字雨文本大小
    		 * 
    		 * @return
    		 */
    		public float getSize() {
    			
    			return fontSize;
    			
    		}
    		
    		/**
    		 * 判断文字雨是否结束
    		 * 
    		 * @return
    		 */
    		public boolean isEnd() {
    			
    			return rainSpeed <= 0;
    			
    		}
    		
    	}
    	
    	public static void main(String[] args) {
    		
    		BinaryRain rain = new BinaryRain();
    		rain.setVisible(true);
    		rain.start();
    		
    	}
    	
    }
    

    5.效果预览

    参考:http://blog.csdn.net/simon_world/article/details/41791277

  • 相关阅读:
    MVC3、如何应用EntityFramework 连接MySql 数据库 Kevin
    DEV EXPRESS Summary Footer 不显示 Kevin
    装饰模式 Kevin
    Dev 控件 GridControl 控件 二次绑定数据源的问题。 Kevin
    System.InvalidOperationException 异常 Kevin
    LINQ to XML Kevin
    代理模式——代码版“吊丝的故事” Kevin
    VS2012 中的设备 面板 Kevin
    maven 学习笔记(三)创建一个较复杂的 eclipse+android+maven 工程
    maven 学习笔记(一)eclipse+android+maven
  • 原文地址:https://www.cnblogs.com/jinjiyese153/p/7122281.html
Copyright © 2011-2022 走看看