Android實現(xiàn)字母雨的效果
首先來看效果:
一、實現(xiàn)原理
在實現(xiàn)過程中,主要考慮整個界面由若干個字母組成的子母線條組成,這樣的話把固定數(shù)量的字母封裝成一個字母線條,而每個字母又封裝成一個對象,這樣的話,就形成了如下組成效果:
字母對象--》字母線條對象--》界面效果
每個字母都應(yīng)該知道自己的位置坐標(biāo),自己上面的字母、以及自己的透明度:
class HackCode{ Point p = new Point();//每一個字母的坐標(biāo) int alpha = 255;//透明度值 默認(rèn)255 String code = "A";//字母的值 }
而每個子母線條對象都有自己這條線條的初始底部起點,內(nèi)部的多個字母都是根據(jù)線條的初始底部起點依次排列,包含多個字母對象集合,以及這條線條的唯一標(biāo)示:
class HackLine{ public int NUM = 0;//用于記錄這列的標(biāo)示 private Point p = new Point();//線的初始位置 List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一條線 }
在初始化的時候創(chuàng)建所有子母線條對象以及字母對象存入集合中:
@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);//用于開啟循環(huán) 線條滾動 } 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); } }
接下來要滾動顯示由Handler
發(fā)送一個延時100毫秒的消息開始:
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; } } } }
讓整個線條往下走其實也就只用將線條的底部初始值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); } } }
之后我們要考慮在合適的時間移除掉不需要的字母線條并增加新的子母線條,這里我是判斷如果線條底部超過屏幕高度的一半時就移除當(dāng)前線條并根據(jù)唯一標(biāo)示添加新的線條:
/** * 刪除一列 同時添加初始化一列 * @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); }
最后,在控件移除屏幕的時候終止消息循環(huán),運(yùn)行時記得將根布局設(shè)置背景為黑色:
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.removeCallbacksAndMessages(null);//停止刷新 }
OKOK,字母雨已經(jīng)出來啦~~ 思路清晰之后還是很簡單的哦~
二、實現(xiàn)代碼
整個代碼也不算很長,就直接貼上了:
/** * 字母雨 * @author zhang * */ public class HackView extends View { /** 文字的畫筆 */ 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); } } } /** * 刪除一列 同時添加初始化一列 * @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); } } /** * 每條線 包含多個字母 **/ class HackLine{ public int NUM = 0;//用于記錄這列的標(biāo)示 private Point p = new Point();//線的初始位置 List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一條線 } /** * 每個字母 */ class HackCode{ Point p = new Point();//每一個字母的坐標(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>
以上就是基于Android實現(xiàn)字母雨的效果全部內(nèi)容,效果很好,有需要的小伙伴們可以參考學(xué)習(xí)。
相關(guān)文章
Android使用Notification在狀態(tài)欄上顯示通知
這篇文章主要為大家詳細(xì)介紹了Android使用Notification在狀態(tài)欄上顯示通知,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12Android網(wǎng)頁H5 Input選擇相機(jī)和系統(tǒng)相冊
這篇文章主要為大家詳細(xì)介紹了Android網(wǎng)頁H5 Input選擇相機(jī)和系統(tǒng)相冊,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10flutter Container容器實現(xiàn)圓角邊框
這篇文章主要為大家詳細(xì)介紹了flutter Container容器實現(xiàn)圓角邊框,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07Android 文件存儲與SharedPreferences存儲方式詳解用法
SharedPreferences是安卓平臺上一個輕量級的存儲類,用來保存應(yīng)用的一些常用配置,比如Activity狀態(tài),Activity暫停時,將此activity的狀態(tài)保存到SharedPereferences中;當(dāng)Activity重載,系統(tǒng)回調(diào)方法onSaveInstanceState時,再從SharedPreferences中將值取出2021-10-10Android ViewDragHelper實現(xiàn)京東、淘寶拖拽詳情功能的實現(xiàn)
這篇文章主要介紹了Android ViewDragHelper實現(xiàn)京東、淘寶拖拽詳情,實現(xiàn)這種效果大概分為三種方式,具體哪三種方式大家通過本文了解下吧2018-04-04