有段時(shí)間沒(méi)寫(xiě)博文了,前段時(shí)間比較忙梯捕,這幾天閑下來(lái)匙瘪,想著寫(xiě)點(diǎn)東西铆铆,腦袋一下就閃過(guò)以前學(xué)習(xí)Android的時(shí)候見(jiàn)到的別人實(shí)現(xiàn)的黑客帝國(guó)的字母雨效果蝶缀,當(dāng)時(shí)對(duì)于小菜鳥(niǎo)的自己,那叫一個(gè)膜拜啊薄货,時(shí)隔幾年翁都,自己實(shí)現(xiàn)一下,算是對(duì)以前的自己一個(gè)交代吧菲驴。
【csdn:http://blog.csdn.net/zhangke3016/article/details/51994167]
先看效果:
一荐吵、實(shí)現(xiàn)原理
在實(shí)現(xiàn)過(guò)程中骑冗,主要考慮整個(gè)界面由若干個(gè)字母組成的子母線條組成赊瞬,這樣的話把固定數(shù)量的字母封裝成一個(gè)字母線條,而每個(gè)字母又封裝成一個(gè)對(duì)象贼涩,這樣的話巧涧,就形成了如下組成效果:
字母對(duì)象--》字母線條對(duì)象--》界面效果
每個(gè)字母都應(yīng)該知道自己的位置坐標(biāo),自己上面的字母遥倦、以及自己的透明度:
class HackCode{
Point p = new Point();//每一個(gè)字母的坐標(biāo)
int alpha = 255;//透明度值 默認(rèn)255
String code = "A";//字母的值
}
而每個(gè)子母線條對(duì)象都有自己這條線條的初始底部起點(diǎn)谤绳,內(nèi)部的多個(gè)字母都是根據(jù)線條的初始底部起點(diǎn)依次排列,包含多個(gè)字母對(duì)象集合袒哥,以及這條線條的唯一標(biāo)示:
class HackLine{
public int NUM = 0;//用于記錄這列的標(biāo)示
private Point p = new Point();//線的初始位置
List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一條線
}
在初始化的時(shí)候創(chuàng)建所有子母線條對(duì)象以及字母對(duì)象存入集合中:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();//獲取控件寬高
mHeight = getMeasuredHeight();
mHackLines.clear();//清空集合
initPlayData();//初始化播放數(shù)據(jù)
}
/**
* 初始化播放數(shù)據(jù)
*/
public void initPlayData(){
initHackLine(mWidth/9, mHeight/12);
initHackLine(mWidth/9, mHeight/7);
HackLine hl;
for (int i = 3; i < 9; i++) {
hl= new HackLine();
hl.p.x = mWidth/9*(i+1);
hl.p.y = mHeight/7*(9-i);
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = hl.p.x;
hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
hl.hcs.add(hc);
}
mHackLines.add(hl);
hl.NUM = mHackLines.size();
}
}
然后在onDraw方法中繪制:
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < mHackLines.size(); i++) {
drawText(i, canvas);
}
mHandler.sendEmptyMessageDelayed(WHAT, 100);//用于開(kāi)啟循環(huán) 線條滾動(dòng)
}
public void drawText(int nindex,Canvas canvas){
HackLine hackLine = mHackLines.get(nindex);
for (int i = 0; i < hackLine.hcs.size(); i++) {
HackCode hackCode = hackLine.hcs.get(i);
mPaint.setAlpha(hackCode.alpha);
canvas.drawText(hackCode.code, hackCode.p.x, hackCode.p.y, mPaint);
}
}
接下來(lái)要滾動(dòng)顯示由Handler發(fā)送一個(gè)延時(shí)100毫秒的消息開(kāi)始:
class WeakHandler extends Handler{
WeakReference<Activity> mActivity;
public WeakHandler(Activity activity){
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
if(mActivity.get() != null){
switch (msg.what) {
case WHAT:
nextPlay(dip2px(getContext(), 20));
for (int i = 0; i < mHackLines.size(); i++) {
if(mHackLines.get(i).p.y >= mHeight/2*3){
addHackLine(mHackLines.get(i));
}
}
invalidate();
break;
}
}
}
}
讓整個(gè)線條往下走其實(shí)也就只用將線條的底部初始值Y坐標(biāo)不斷增加缩筛,內(nèi)部字母隨之更新位置就可以了:
/**
* 下一幀播放
* @param Nnum 每次下移多遠(yuǎn) 距離
*/
public void nextPlay(int Nnum){
for (int i = 0; i < mHackLines.size(); i++) {
List<HackCode> hcs = mHackLines.get(i).hcs;
hcs.clear();
mHackLines.get(i).p.y+=Nnum;
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = mHackLines.get(i).p.x;
hc.p.y = mHackLines.get(i).p.y-dip2px(getContext(), 25)*j;
hcs.add(hc);
}
}
}
之后我們要考慮在合適的時(shí)間移除掉不需要的字母線條并增加新的子母線條,這里我是判斷如果線條底部超過(guò)屏幕高度的一半時(shí)就移除當(dāng)前線條并根據(jù)唯一標(biāo)示添加新的線條:
/**
* 刪除一列 同時(shí)添加初始化一列
* @param hackLine
*/
public void addHackLine(HackLine hackLine){
if(hackLine == null){
return;
}
int num = hackLine.NUM;
mHackLines.remove(hackLine);//如果存在 刪除 重新添加
HackLine hl;
hl= new HackLine();
hl.p.x = mWidth/9*(num-1);
hl.p.y = mHeight/12*(7-(num-1));
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = hl.p.x;
hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
hl.hcs.add(hc);
}
hl.NUM = num;
mHackLines.add(hl);
}
最后堡称,在控件移除屏幕的時(shí)候終止消息循環(huán)瞎抛,運(yùn)行時(shí)記得將根布局設(shè)置背景為黑色:
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mHandler.removeCallbacksAndMessages(null);//停止刷新
}
OKOK,字母雨已經(jīng)出來(lái)啦~~ 思路清晰之后還是很簡(jiǎn)單的哦~
二、實(shí)現(xiàn)代碼
整個(gè)代碼也不算很長(zhǎng)却紧,就直接貼上了:
/**
* 字母雨
* @author zhang
*
*/
public class HackView extends View {
/** 文字的畫(huà)筆 */
private Paint mPaint;
/** 控件的寬 */
private int mWidth;
/** 控件的高 */
private int mHeight;
/** 所有字母 */
private static final String[] CODES = {
"A","B","C","D","E","F","G","H","I","J","K",
"L","M","N","O","P","Q","R","S","T","U","V",
"W","K","Y","Z"
};
private static final int WHAT = 1;
/** 所有的HackLine組合 */
private List<HackLine> mHackLines = new ArrayList<HackView.HackLine>();
private WeakHandler mHandler;
public HackView(Context context) {
this(context,null);
}
public HackView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public HackView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
mHandler = new WeakHandler((Activity) context);
}
class WeakHandler extends Handler{
WeakReference<Activity> mActivity;
public WeakHandler(Activity activity){
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
if(mActivity.get() != null){
switch (msg.what) {
case WHAT:
nextPlay(dip2px(getContext(), 20));
for (int i = 0; i < mHackLines.size(); i++) {
if(mHackLines.get(i).p.y >= mHeight/2*3){
addHackLine(mHackLines.get(i));
}
}
invalidate();
break;
}
}
}
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setTextSize(dip2px(getContext(), 20));
mPaint.setStrokeCap(Cap.ROUND);
mPaint.setStrokeWidth(dip2px(getContext(), 5));
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();//獲取控件寬高
mHeight = getMeasuredHeight();
mHackLines.clear();//清空集合
initPlayData();
}
/**
* 下一幀播放
* @param Nnum 每次下移多遠(yuǎn) 距離
*/
public void nextPlay(int Nnum){
for (int i = 0; i < mHackLines.size(); i++) {
List<HackCode> hcs = mHackLines.get(i).hcs;
hcs.clear();
mHackLines.get(i).p.y+=Nnum;
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = mHackLines.get(i).p.x;
hc.p.y = mHackLines.get(i).p.y-dip2px(getContext(), 25)*j;
hcs.add(hc);
}
}
}
/**
* 刪除一列 同時(shí)添加初始化一列
* @param hackLine
*/
public void addHackLine(HackLine hackLine){
if(hackLine == null){
return;
}
int num = hackLine.NUM;
mHackLines.remove(hackLine);//如果存在 刪除 重新添加
HackLine hl;
hl= new HackLine();
hl.p.x = mWidth/9*(num-1);
hl.p.y = mHeight/12*(7-(num-1));
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = hl.p.x;
hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
hl.hcs.add(hc);
}
hl.NUM = num;
mHackLines.add(hl);
}
/**
* 初始化每一行數(shù)據(jù)
* @param x
* @param y
*/
public void initHackLine(int x,int y){
HackLine hl;
for (int i = 0; i < 9; i++) {
hl= new HackLine();
hl.p.x = x*i;
hl.p.y = y*(7-i);
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = hl.p.x;
hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
hl.hcs.add(hc);
}
mHackLines.add(hl);
hl.NUM = mHackLines.size();
}
}
/**
* 初始化播放數(shù)據(jù)
*/
public void initPlayData(){
initHackLine(mWidth/9, mHeight/12);
initHackLine(mWidth/9, mHeight/7);
HackLine hl;
for (int i = 3; i < 9; i++) {
hl= new HackLine();
hl.p.x = mWidth/9*(i+1);
hl.p.y = mHeight/7*(9-i);
for (int j = 0; j < 7; j++) {
HackCode hc = new HackCode();
hc.alpha -= 30*j;
hc.code = CODES[new Random().nextInt(CODES.length)];
hc.p.x = hl.p.x;
hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
hl.hcs.add(hc);
}
mHackLines.add(hl);
hl.NUM = mHackLines.size();
}
}
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < mHackLines.size(); i++) {
drawText(i, canvas);
}
mHandler.sendEmptyMessageDelayed(WHAT, 100);
}
public void drawText(int nindex,Canvas canvas){
HackLine hackLine = mHackLines.get(nindex);
for (int i = 0; i < hackLine.hcs.size(); i++) {
HackCode hackCode = hackLine.hcs.get(i);
mPaint.setAlpha(hackCode.alpha);
canvas.drawText(hackCode.code, hackCode.p.x, hackCode.p.y, mPaint);
}
}
/**
* 每條線 包含多個(gè)字母
**/
class HackLine{
public int NUM = 0;//用于記錄這列的標(biāo)示
private Point p = new Point();//線的初始位置
List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一條線
}
/**
* 每個(gè)字母
*/
class HackCode{
Point p = new Point();//每一個(gè)字母的坐標(biāo)
int alpha = 255;//透明度值 默認(rèn)255
String code = "A";//字母的值
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mHandler.removeCallbacksAndMessages(null);//停止刷新
}
/**
* 根據(jù)手機(jī)的分辨率從 dip 的單位 轉(zhuǎn)成為 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context=".MainActivity" >
<com.zk.hack.HackView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>