(本博客為原創:http://www.cnblogs.com/linguanh/)
目錄:
效果展示
感想
代碼拆解
開源地址
效果展示
有沒有興趣繼續看下去,直接看下"顏值"是第一步了。依次對應:下雨,飄雪,紅包雨,碰撞球
上面是圖片,這里再發個視頻鏈接:http://pan.baidu.com/s/1miyPn76。
感想
16年總算過去了,跟各位猿友有說句祝福吧,新的一年少加點班,身體健康,錢能賺多少就盡量賺。
之前看博客園,很多發純感想的,都被推薦了好幾天,說實話,我幾乎一拉到底,就看有沒有點↓的。
公司放假時間是在23號,也就明天了,大四實習到現在,一直很忙,這段時間也是我在編程層面上學到了比較多東西的階段,上面的自定義View是Android的,完成它們是在實習上班期間擠出時間做的,最初的初衷是想把第四個碰撞球的效果加入到畢設里面,現在總算是實現了,過程遇到很多問題,球體的碰撞處理比想象中麻煩很多,前三個比較簡單,也是微信,QQ的下表情原理吧,我猜應該是。。。
畢設最終也會開源,還請大家留意我 GitHbub,這將會是一個集合非第三方IM和仿朋友圈+golang制作服務端等等知識的社交APP。
代碼拆解
如果你仔細看了上面的四張效果圖,你會發現,前三張是沒碰撞效果處理的,而第四張是具備的。這也是我要區分實現的效果,目的是為了表明,不僅可以不碰撞還可以選擇碰。
同時,飄雪和紅包雨,事實也僅僅是圖片的不同,這就對了。你只需要修改圖片就能實現完全自定義,愛下什么下什么。
言歸正傳,整體使用了 適配器設計模式。代碼是很簡練易懂的,可以看看我的目錄結構。
基類是一個暴露繪制和邏輯抽象方法的View子類,所有自定義View需要繼承它。子類只需要關注自己要繪制什么,以及我要繪制的東西怎么去不斷地改變,邏輯改變設計在一個 Thread 線程里面,采用 postInvalidate 通知 UI 刷新。

1 /** 2 * Created by LinGuanHong on 2017/1/15. 3 * 4 * My GitHub : https://github.com/af913337456/ 5 * 6 * My Blog : http://www.cnblogs.com/linguanh/ 7 * 8 */ 9 10 public abstract class BaseView extends View { 11 12 protected String TAG = "zzzzz"; 13 private static final int sleepTime = 30; 14 private RefreshThread refreshThread = null; 15 16 public BaseView(Context context) { 17 super(context); 18 } 19 20 public BaseView(Context context, AttributeSet attrs) { 21 super(context, attrs); 22 } 23 24 public abstract void drawSub(Canvas canvas); 25 26 public abstract void baseInit(int width,int height); 27 28 public abstract void logic(); 29 30 @Override 31 protected final void onDraw(Canvas canvas) { 32 if(refreshThread == null){ 33 refreshThread = new RefreshThread(); 34 refreshThread.start(); 35 }else{ 36 drawSub(canvas); 37 } 38 } 39 40 @Override 41 protected void onDetachedFromWindow() { 42 running = false; 43 super.onDetachedFromWindow(); 44 } 45 46 private boolean running = true; 47 private class RefreshThread extends Thread{ 48 49 @Override 50 public void run() { 51 baseInit(getWidth(),getHeight()); 52 while (running){ 53 try{ 54 logic(); 55 postInvalidate(); 56 Thread.sleep(sleepTime); 57 }catch (Exception e){ 58 Log.d(TAG,e.toString()); 59 } 60 } 61 } 62 } 63 }
BaseView 已經是一個可以直接繼承的父類了,如果僅僅只是繪制一些比較簡單邏輯的自定義View,繼承它足以,但是要實現上述的效果,還需要添加多一個抽象類,無論是雨景還是雪景,它們都是個體的集合,也就是說,我們需要一個"景"的抽象。
ShowView 是一個具備泛型的抽象類,它是景色的制作者,至於是什么景,由你來決定,也就是泛型的傳入。例如,我要制造雨景,那么我就傳入雨點,雪景就是雪塊

1 /** 2 * Created by LinGuanHong on 2017/1/15. 3 * 4 * My GitHub : https://github.com/af913337456/ 5 * 6 * My Blog : http://www.cnblogs.com/linguanh/ 7 * 8 */ 9 10 public abstract class ShowView<T extends BaseItem> extends BaseView { 11 12 protected List<T> itemList = new ArrayList<>(); 13 protected int size = 1; 14 15 public ShowView(Context context) { 16 super(context); 17 } 18 19 /** 子類實現布局必須要重寫這個構造方法 */ 20 public ShowView(Context context, AttributeSet attrs) { 21 super(context, attrs); 22 } 23 24 @Override 25 public void drawSub(Canvas canvas) { 26 for(T t:itemList){ 27 t.draw(canvas); 28 } 29 } 30 31 @Override 32 public void logic() { 33 beforeLogicLoop(); 34 for(T t:itemList){ 35 t.move(); 36 } 37 } 38 39 public abstract void beforeLogicLoop(); 40 public abstract T getItem(int width, int height,Resources resources); 41 public abstract int getCount(); 42 43 @Override 44 public void baseInit(int width, int height) { 45 size = getCount(); 46 Resources resources = getResources(); 47 for(int i = 0; i< size; i++){ 48 itemList.add(getItem(width,height,resources)); 49 } 50 } 51 52 }
由於我們的景色里面的個體可能是各種各樣,那么為了使他們都能具備一些公共的屬性,需要再抽象一個基礎的個體類,共不同的景色個體繼承。
1 /** 2 * Created by LinGuanHong on 2017/1/15. 3 * 4 * My GitHub : https://github.com/af913337456/ 5 * 6 * My Blog : http://www.cnblogs.com/linguanh/ 7 * 8 * 公共的屬性和行為 9 * 10 */ 11 12 public abstract class BaseItem { 13 14 protected int width,height; /** 景內寬高 */ 15 protected Resources resources; 16 17 public BaseItem(int width,int height,Resources resources){ 18 this.width = width; 19 this.height = height; 20 this.resources = resources; 21 } 22 23 public abstract void draw(Canvas canvas); /** 顯示 */ 24 public abstract void move(); /** 運動 */ 25 26 }
OK,到這里基礎的類都搞定了,為什么說是適配器模式呢,其實 BaseItem 就是 ViewHolder,ShowView 是 BaseAdapter,下面放下雨的Item 類和雨景。
注意注釋,代碼很簡練易懂!
1 /** 2 * Created by LinGuanHong on 2017/1/15. 3 * 4 * 造雨,造多少個,160個,具體是什么雨,交給 item 實現 5 * 6 */ 7 8 public class RainView extends ShowView<RainItem> { 9 10 11 public RainView(Context context) { 12 super(context); 13 } 14 15 public RainView(Context context, AttributeSet attrs) { 16 super(context, attrs); 17 } 18 19 @Override 20 public void beforeLogicLoop() { 21 22 } 23 24 @Override 25 public RainItem getItem(int width, int height, Resources resources) { 26 return new RainItem(width,height,resources); /** 要造的雨,是什么雨就在這里傳入 */ 27 } 28 29 @Override 30 public int getCount() { /** 要制作的雨數目 */ 31 return 160; 32 } 33 }
我要制作的雨,隨機數可以自定義。
1 /** 2 * Created by LinGuanHong on 2017/1/15. 3 */ 4 5 public class RainItem extends BaseItem { 6 7 private float opt; 8 private int sizeX,sizeY; /** 充當角度 */ 9 private int startX,startY,stopX,stopY; 10 private Paint paint; 11 private Random random; 12 13 public RainItem(int width, int height, Resources resources) { 14 super(width,height,resources); 15 init(); 16 loopInit(); 17 } 18 19 @Override 20 public void move() { 21 startX += sizeX * opt; 22 stopX += sizeX * opt; 23 24 startY += sizeY * opt; 25 stopY += sizeY * opt; 26 if(startY > height){ 27 loopInit(); 28 } 29 } 30 31 @Override 32 public void draw(Canvas canvas) { 33 Log.d("zzzzz","drawView "+startX+" "+startY+" "+stopX+" "+stopY); 34 canvas.drawLine(startX,startY,stopX,stopY,paint); 35 } 36 37 private void loopInit(){ 38 sizeX = 1 + random.nextInt(10); 39 sizeY = 10 + random.nextInt(20); 40 41 startX = random.nextInt(width ); 42 startY = random.nextInt(height); 43 44 opt = 0.2f + random.nextFloat(); 45 46 stopX = startX + sizeX; 47 stopY = startY + sizeY; 48 } 49 50 private void init(){ 51 paint = new Paint(Paint.ANTI_ALIAS_FLAG); /** 抗鋸齒 */ 52 paint.setColor(0xffffffff); /** a,r,g,b 255,255,255,255 */ 53 54 random = new Random(); 55 } 56 57 }
到這里總結一下,如果你想實現自己的自定義View,不妨直接繼承 BaseView,然后寫你自己的 Item,就可以了。
開源地址
我的:https://github.com/af913337456/XView