編輯:關於Android編程
首先來看效果:
一、實現原理
在實現過程中,主要考慮整個界面由若干個字母組成的子母線條組成,這樣的話把固定數量的字母封裝成一個字母線條,而每個字母又封裝成一個對象,這樣的話,就形成了如下組成效果:
字母對象--》字母線條對象--》界面效果
每個字母都應該知道自己的位置坐標,自己上面的字母、以及自己的透明度:
class HackCode{ Point p = new Point();//每一個字母的坐標 int alpha = 255;//透明度值 默認255 String code = "A";//字母的值 }
而每個子母線條對象都有自己這條線條的初始底部起點,內部的多個字母都是根據線條的初始底部起點依次排列,包含多個字母對象集合,以及這條線條的唯一標示:
class HackLine{ public int NUM = 0;//用於記錄這列的標示 private Point p = new Point();//線的初始位置 List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一條線 }
在初始化的時候創建所有子母線條對象以及字母對象存入集合中:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth();//獲取控件寬高 mHeight = getMeasuredHeight(); mHackLines.clear();//清空集合 initPlayData();//初始化播放數據 } /** * 初始化播放數據 */ 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);//用於開啟循環 線條滾動 } 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
發送一個延時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坐標不斷增加,內部字母隨之更新位置就可以了:
/** * 下一幀播放 * @param Nnum 每次下移多遠 距離 */ 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); }
最後,在控件移除屏幕的時候終止消息循環,運行時記得將根布局設置背景為黑色:
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.removeCallbacksAndMessages(null);//停止刷新 }
OKOK,字母雨已經出來啦~~ 思路清晰之後還是很簡單的哦~
二、實現代碼
整個代碼也不算很長,就直接貼上了:
/** * 字母雨 * @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 每次下移多遠 距離 */ 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); } /** * 初始化每一行數據 * @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(); } } /** * 初始化播放數據 */ 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;//用於記錄這列的標示 private Point p = new Point();//線的初始位置 List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一條線 } /** * 每個字母 */ class HackCode{ Point p = new Point();//每一個字母的坐標 int alpha = 255;//透明度值 默認255 String code = "A";//字母的值 } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.removeCallbacksAndMessages(null);//停止刷新 } /** * 根據手機的分辨率從 dip 的單位 轉成為 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實現字母雨的效果全部內容,效果很好,有需要的小伙伴們可以參考學習。
針對移動端 Android 的測試, adb 命令是很重要的一個點,必須將常用的 adb 命令熟記於心, 將會為 Android 測試帶來很大的方便,其中很多命令將會用
QQ是大家離不開的聊天工具,方便既實用,自從qq更新至6.0之後,側滑由原來的劃出後主面板縮小變成了左右平滑,在外觀上有了很大的提升,於是我就是嘗試理解下裡面的各種邏輯,
混淆簡單配置build.gradle的android節點下添加:buildTypes { release { minifyEnable
以前看了很多人介紹的Android事件派發流程,但最近使用那些來寫代碼的時候出現了不少錯誤。所以回顧一下整個流程,簡單介紹從手觸摸屏幕開始到事件在View樹派發。從源碼上