2017了,回家前 "年末" 分享:下雨,飘雪,红包雨,碰撞球,自定义View

(本博客为原创:https://cloud.tencent.com/developer/user/1148436/activities)

目录:

  效果展示

  感想

  代码拆解

  开源地址

效果展示

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

  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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏KK的小酒馆

开发Android太阳能手电筒

作为一个世上有名的发明家,发明些对普通人类非常有用的日常用品,也是一个科学家的职责。

614
来自专栏wblearn

Java小程序祝国庆快乐祝大家国庆节快乐

上面是用java的JFrame为基础实现的效果,下面用面向对象的思考分析并实现此效果:

731
来自专栏信安之路

Pin-in-CTF 学习整理记录

这次打 qctf,做到了一个 ollvm,控制流平坦化的题,虽然不是很明白原理(但这么叫感觉很 6 批)。听师傅们说可以用 pin 解决,于是先学习一下 pin...

920
来自专栏流浪猫的golang

MongoDB 中文的全文索引

MongoDB 从3.2 版本以后添加了对中文索引的支持: 官网链接:https://docs.mongodb.com/manual/reference/t...

743
来自专栏hbbliyong

使用线程新建WPF窗体(公用进度条窗体)

使用线程新建窗体 项目中需要一个公用的进度条窗体.大家知道在wpf中,有两个线程,一个是UI线程,另一个是监听线程(一直监听用户的输入).如果我们后台有阻塞UI...

37010
来自专栏自由而无用的灵魂的碎碎念

关于Serif与Sans-Serif字体

在西方国家罗马字母阵营中,字体分为两大种类:Sans Serif和 Serif,打字机体虽然也属于 Sans Serif,但由于是等宽字体,所以另外独立出 Mo...

853
来自专栏微信公众号:Java团长

Java打飞机小游戏(附完整源码)

技术源于分享,所以今天抽空把自己之前用java做过的小游戏整理贴出来给大家参考学习。java确实不适合写桌面应用,这里只是通过这个游戏让大家理解oop面向对象编...

1452
来自专栏从零开始学 Web 前端

11 - JavaSE之GUI

PS: Panel 的 setBounds 方法中设置的位置大小是相对于相对装入的 Frame 窗口位置和大小的。

965
来自专栏信安之路

必知必会的安全工具

渗透测试中手工测试固然重要,但是测试工具也是必不可少的,一个好的工具可以让我们在渗透测试中事半功倍,俗话说,工欲善其事必先利其器,所以工具是很重要的,本文就主要...

590
来自专栏工科狗和生物喵

【图】Dijkstra算法

953

扫码关注云+社区