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.\n" + 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM