下雨,飘雪,红包雨,碰撞球,自定义 View

4,493 阅读7分钟

效果展示

  有没有兴趣继续看下去,直接看下"颜值"是第一步了。依次对应:下雨,飘雪,红包雨,碰撞球

   

  上面是图片,这里再发个视频链接: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 }
View Code

   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 }
View Code

  由于我们的景色里面的个体可能是各种各样,那么为了使他们都能具备一些公共的属性,需要再抽象一个基础的个体类,共不同的景色个体继承。

 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,就可以了。

开源地址

  我的:github.com/af913337456…

  

如果您认为这篇文章还不错或者有所收获,您可以通过扫描一下下面的支付宝二维码 打赏我一杯咖啡【物质支持】,也可以点击右下角的【推荐】按钮【精神支持】,因为这两种支持都是我继续写作,分享的最大动力