注册

情绪宣泄App:十年前IT男编程撩妹纪实

阅读本文,你将收获以下内容:

1、通过观察11年前的Android代码,了解安卓开发生态近十年间的演进。

2、通过了解这款创意App的功能,对IT男该如何运用技术做出反思。

3、不幸看到作者大学时期的照片,形象极其猥琐、狼狈、不堪……够了,谁在动键盘?!

前言

因为在掘金的创作者群里比嘻哈,有人觉得我经常信口开河,尤其我写了那篇《我裁完兄弟们辞职了,转行做了一名小职员》后,有掘友评论:“这篇文章艺术成分很高”、“感觉是编故事”等等。

bb03996a8d204043861e33ad85d6e99a~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

其中,我文章里提到过一句:我大学期间搞过Android的APP去运作。

36ec808751884c39baba9a49698b9f55~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

今天,就说说这件事,一来展现下创意作品,二来给自己挽回一些微信……违心……维新……不要了。

女朋友

回到2010年,那时我上大二,谈了一个女朋友,她现在是我孩子的妈妈。

女朋友哪里都好,漂亮、温柔、大方,唯一一点就是脾气太大。

因为我不会制造浪漫,所以女朋友经常对我发脾气,一发脾气就不理我。

我一想,如果她不理我的时间设为N,如果N值无限大,那就相当于分手了,这感情不就黄了吗?

情侣在吵架冷战期间,如何才能不见面也能如同见面一般宣泄情绪,从而刷存在感呢?

可以做一款App来解决这个问题。

冷战红娘App

这款App我取名叫“冷战红娘”,意思是情侣在冷战期间调和关系的红娘媒介,并且我还亲自设计了LOGO和启动页。

a478b6eee0214f4f83718e02e728a39e~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

一个安卓小机器人,手里拿着玫瑰花,表示示好,头顶两个触角间发出电波,表示科技和智能。

那一年,我才19岁。不行了,我膨胀得快要爆掉了。

功能简介

本软件主要有三大功能:涂鸦对方照片、写情绪日记、告知情绪结果。

下面是首页的效果:

7956c611949043f88c26c932492a7bea~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

下面是实现代码的目录结构:

481ae4fc37654486a84854e90c7d9777~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

女朋友名字中带“冰”,我名字中带“磊”,因此项目名是LoveBing,包名是com.lei.lovebing01

有安卓小伙伴看到目录结构可能会发现少文件,说我在糊弄你,起码你的build.gradle得有吧。

朋友,这是2010年的安卓项目,那时的版本号是SdkVersion="8",也就是Android 2.2,现在最新版本已经到了API 32, Android 12了。从互联网时代来看,就好像是现在和清朝的区别。

那时还没有动态权限请求,存取文件也不用FileProvider,你可以随意读取其他程序的内部数据,应用层就可以静默发送短信和获取定位,开发者可以更好地实现自己的想法,不必受到很多限制。当然,这在现在看来是不安全的。所以,任何事物的成熟都是有周期的。

那时候也没有现在这么多的第三方框架,基本都是调用Android原生的API,操作数据库需要直接使用android.database.sqlite.SQLiteDatabase,SQL语句要自己写,操作异常要自己处理。

下面,就让我们跟随功能,结合代码,一起去剖析一下这款App吧。

涂鸦

女朋友生气不理我了,短信不回,电话不接,女生宿舍我又进不去。但是,她又有怨气没地方宣泄。这时,她就会打开这个功能,可以把我的头像摆出来,然后进行各种攻击,支持花色涂抹,支持往我的照片上放小虫子、扔臭鸡蛋、使用炸弹爆破效果等等。天啊,我这是怎么了……不但有这种想法,而且还开发出了功能。

49502879d7a342eebcf7f6b9452765fb~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

其实,要实现这个功能,非常简单。

首先,整个页面的数据,是通过配置完成的,各种颜色,工具,以及图标,需要事先声明好。

//信手涂鸦里的数据=====================================================
//工具的名字
public final static String[] colortext={"红色", "黄色","绿色",
"粉色", "黑色", "紫色", "蓝色", "浅绿", "棕色"};
//工具的图片
public final static int[] colorpic = {
R.drawable.color1, R.drawable.color2,
R.drawable.color3, R.drawable.color4,
R.drawable.color5, R.drawable.color6,
R.drawable.color7, R.drawable.color8,
R.drawable.color9 };

//信手涂鸦颜色选择
public  static final int red = 0;
public  static final int yellow = 1;
public  static final int green = 2;
public  static final int pink = 3;
public  static final int black = 4;
public  static final int purple = 5;
public  static final int blackblue = 6;
public  static final int lightgreen = 7;
public  static final int orange = 8;

//使用工具里的数据=====================================================
public final static String[] toolstext={"鸡蛋", "炸弹","生物","喷溅"};//工具的名字
public final static int[] toolspic = {//工具的图片
R.drawable.dao, R.drawable.zhadan01,R.drawable.tool,R.drawable.penjian
};

public final static int[][] toolspic_01 = {//工具的图片使用后
{R.drawable.dao_01, R.drawable.dao_02, R.drawable.dao_03,R.drawable.dao_01}
,{ R.drawable.baozha01, R.drawable.baozha02, R.drawable.baozha03, R.drawable.baozha04}
,{R.drawable.tools_01, R.drawable.tools_02, R.drawable.tools_03, R.drawable.tools_04}
,{R.drawable.penjian01, R.drawable.penjian02, R.drawable.penjian03, R.drawable.penjian04}
};

通过配置的方式,有一个极大的好处,那就是以后你增加新的工具,不用修改代码,直接修改配置文件即可。

下面一步就是使用一个Canvas画板,把上面的配置画到画布上,并响应用户的交互,为此我新建了一个CanvasView,它继承了View

public class CanvasView extends View{

   public Bitmap[][] bitmapArray;
   private Canvas  mCanvas;

   public CanvasView(Context context) {
       super(context);
       bitmapArray=new Bitmap[MyData.toolspic.length][4];//实例化工具数组
       //载入工具需要的图像
       InputStream bitmapis;
       for(int i=0; i<MyData.toolspic_01.length; i++){
           for(int j=0; j<MyData.toolspic_01[i].length; j++){
               bitmapis = getResources().openRawResource(MyData.toolspic_01[i][j]);
               bitmapArray[i][j] = BitmapFactory.decodeStream(bitmapis);
          }
      }
       // 使用mBitmap创建一个画布
       mCanvas = new Canvas(mBitmap);
       mCanvas.drawColor(0xFFFFFFFF);//背景为白色
  }
     
   //在用户点击的地方画使用的工具
   public void drawTools(int bitmapID){
       Random rand = new Random();
       int myrand = rand.nextInt(bitmapArray[bitmapID].length);
       mCanvas.drawBitmap(bitmapArray[bitmapID][myrand], mX-bitmapArray[bitmapID]
          [myrand].getWidth()/2, mY-juli-bitmapArray[bitmapID][myrand].getHeight()/2, null);
  }
   ……
}

上面只是部分关键代码,主要展示了如何加载图片,以及如何响应用户的操作,基本无难点。

女朋友毕竟花费了一番功夫,作品肯定要给她保留,因为她可能要展示给我看:你看,昨天你惹我多严重,我把你画成这样!

acabcf6cc20241ae9702a2853a52175b~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

涂鸦完成之后,文件保存到SD卡目录即可。权限管理方面,在AndroidManifest.xml中注册一下就行,除此之外,再无其他操作。

<!--  向SD卡写入数据的权限  -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

当然,现在不可以了,Android 6.0以后,你得动态申请权限了。

值得一说的是,上面的图片浏览控件叫ImageSwitcher,这是个老控件了,很简单就可以实现幻灯片浏览的效果。

日记

如果涂鸦无法完全解气,为了能让女朋友把生气的原因表述明白,我特意增加了这个生气日记的功能。效果等同于她面对面骂我,期望她写完了气也就消了。

7d63d4d0756f42ae89d841ca7201d0d4~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

你觉得数据应该保存到哪里?我首选的是Android内嵌的Sqlite3数据库。

Android中最原始的sqlite数据库操作是这样的,先利用官方的SQLiteOpenHelper创建数据库和数据表。

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper {

private static final String DATABASENAME = "angrydiary.db"; //数据库名称
private static final int DATABASEVERSION = 1;//数据库版本

public DBOpenHelper(Context context) {
super(context, DATABASENAME, null, DATABASEVERSION);
}

public void onCreate(SQLiteDatabase db) {
//建数据表 message 日记
db.execSQL("CREATE TABLE message (message_id integer primary key autoincrement,
                   message_title varchar(50), message_text varchar(500), message_state integer)");
}

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS message");
onCreate(db);
}
}

然后再写上操作数据表数据的方法。就拿生气日记信息的数据处理举例子。

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class MessageServiceDB {

private DBOpenHelper dbOpenHelper;
//构造函数
public MessageServiceDb(Context context) {
this.dbOpenHelper = new DBOpenHelper(context);
}
//保存数据
public void save(Message message){
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("message_title", message.getTitle());
values.put("message_text", message.getText());
db.insert("message", null, values);
db.close();
}
//获得数据
public List<Message> getScrollData(Integer offset, Integer maxResult){
List<Message> messageList = new ArrayList<Message>();
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor = db.query("message", null, null, null, null, null, "message_id desc", offset+","+ maxResult);
while(cursor.moveToNext()){
Integer message_id = cursor.getInt(cursor.getColumnIndex("message_id"));
String message_title = cursor.getString(cursor.getColumnIndex("message_title"));
String message_text = cursor.getString(cursor.getColumnIndex("message_text"));
Message message = new TvMessage(message_id,message_title, message_text);
messageList.add(message);
}
cursor.close();
db.close();
return messageList;
}
//删除一项数据
public void delete(Integer id){
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
db.delete("message", "message_id=?", new String[]{id.toString()});
db.close();
}  
}

利用之前构造好的数据库帮助类dbOpenHelper,然后调用增删改查进行数据处理。这里面的增删改查,有两种方式实现,一种直接写Sql语句,另一种支持对象操作。我基本上都是用的对象操作,比如删除直接就是db.delete(xx)

日志列表我也是煞费苦心,为了便于了解女朋友还对哪些事情生气,我特意开发了生气事件黑白名单功能。一个事件可以反复标记是否原谅我了。这样可以了解女朋友心中还有哪些心结,可以让我逐个攻破。

a8eb4524f68e4022b421fa6976830c43~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

此处调用一个修改方法就可以了,其实就是取出message对象后重新setState一个新值就可以了。

//修改信息状态
public void update(Message message){
   SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
   ContentValues values = new ContentValues();
   values.put("message_state", message.getState());
   db.update("message", values, "message_id=?", new String[]{message.getId().toString()});
   db.close();
}

需要注意的是,对于每次打开数据库或者Cursor,都要记得关闭,不关闭不会影响功能,但是会带来风险。现在你使用各种框架的话,完全不用考虑这些操作,因为他们都帮你做了。

安卓开发,以前和现在,用SDK和用第三方框架,就像是汽车的手动挡和自动挡,其中的优劣,自己体会。虽然我是从老手动挡过来的,但是我站自动挡这边,因为我不会傻到逆着发展趋势去行走。

反馈

女朋友也涂鸦了,也写了生气日记,最后应该也累了。为了缓解她的疲劳,我特意开发了一个看图发愣的功能。只需要点开看图发愣,就会随机出现一个唯美的动态图,并且伴随着唰唰的雨声,可以让她看上几个小时,仔细思考人生,思考我这个男朋友值不值得交往。

54e58ae27154496d863b77eb7fc5d318~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

对于如何展示gif动态图,前端可能要骂人了,因为gif还需要动代码吗?浏览器不全给解释了。但是,当时我需要自己去解析,安卓原生的图片控件是无法展示动图的。所以,你看老一辈的程序员面临多少困难,新一代的程序员完全不用考虑这些。所以,你们应该把更多精力放在更高级的研究上,因为我相信你们也有你们的困难点。

这个图很美,我单独拿出来了,朋友们可以保存下来,自己看。或者,你试试自己去解析一下。

0532bf76c5ea46f4afbaae3dd1941264~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

好了,现在总该不生气了吧。针对于此时的场景,我又开发了一个快捷短信功能,女朋友可以选择短信模板,快速给我发送短信息,给我一个台阶,让我及时去哄她。她可以说不是她发的,是我开发的软件发的,这样可以避免“她先联系的我”这类不利立场的产生。

1458aa1b7b61452287518232e73d835d~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

我一贯执行配置策略,短信模板也是写到value文件夹下的xml文件中的。

<string-array name="message_ex"> 
   <item>不要生气了,我错了!</item>  
   <item>我不生气了,你快点陪我逛街!</item>  
   <item>讨厌,还不给我打电话!</item>  
   <item>我错了,我不该对你发火的!</item>  
   <item>三个小时内给我打电话!</item>  
   <item>快给我给我买爆米花!</item>    
</string-array>

关于发送短信,这里面有两点细节。

SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(DuanxinActivity.this, 0, new Intent(), 0);
//如果字数超过70,需拆分成多条短信发送
if (text.length() > 70) {
   List<String> msgs = smsManager.divideMessage(text);
   for (String msg : msgs) {
   smsManager.sendTextMessage(phone, null, msg, sentIntent, null);                        
  }
} else {
   smsManager.sendTextMessage(phone, null, text, sentIntent, null);
}

第一,为什么不调用Intent(Intent.ACTION_SEND)让系统去发送短信?一开始确实是这样的。但是,后来改良为调用代码应用内发送了。因为我不想让女朋友离开我的软件,跳到第三方系统应用,避免用户跳出场景,因为有时候女朋友会夺命连环发短信(告知对方问题很严重,提高优先级),需要来回切程序,这样用着不爽,就更生气了。而且自己发送我还能捕获到发送结果,给出“发送成功”的温馨提示,但是交给第三方应用发送你是获取不到的。

第二,关于字数超过70,需要拆成多条短信,这也是经过实践得来的。满满的都是痛。

有同学不明白为什么要发短信,因为那时候还没有微信,微信是2011年才出来的。

后记

后来,经过不断反馈和改良,这款App越来越完善。

最后,女朋友看我这么用心地对待这份感情,尤其对于她反馈的软件问题,我询问地非常仔细(复现场景、发生时间、前后操作流程),修改地也非常及时(改完了就让她再试试看),她感觉我是一个靠谱和细心的人,于是她也慢慢地不再那么容易生气了。

再后来,有一个全国高校的大学生IT技能比赛,我的老师就让我拿这个作品参赛了,最后去了北京大学进行了决赛。

虽然这款App技术含量不高,但它是一款身经百战的App,它经过了多次迭代,因为用户体验和创意比较好,我最终获得全国第七名的成绩,荣获二等奖。

下面是证书,教育部的证书我是不敢造假的,我用Photoshop(是的,我也会做UI设计)做了简单的遮挡和放大,主要想让大家看一下日期确实是2011年,和我文章里描述的一样(我没有编故事)。

f7fd5544f8874f14b862773029658f8b~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?

这款App真的没有任何技术含量,无外乎控件的罗列、画板的绘制、数据的存储。我想现在的每一个大学生都能做的到。但是不夸张地讲,它看起来却是很强大的样子。而究其根源,我想应该就是它运用技术手段尝试去解决生活中的问题,让效果得到了放大,使它具备了生命力。

直至今天,我依然在做类似(用技术解决生活中的问题)的事情。

最后,我想借助掘金App的标语结束本文,这也是我每天都会看到的一句话:

相信技术,传递价值。

作者:TF男孩
来源:juejin.cn/post/7123985353878274056

0 个评论

要回复文章请先登录注册