© 版權聲明:本文為博主原創文章,轉載請注明出處
數字彩虹雨:
從上至下,隨機出現一串字符串,以不同的速度運行到底部;類似於黑客帝國里面的場景
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