我们的上篇文章:点我访问
(。・∀・)ノ゙嗨,又来到了新的一篇文章啦,先来一份喜报。我们的星闪通讯软件,截止2024.7.6日,已经在GitHub上获得了40颗星星啦。也就是今天,我们的软件版本成功来到了1.3版本。

第三个版本,我们的侧重点偏向了UI方面,当然也会跟前面的文章一样,在接下来的内容里我会继续讲述我们UI的修改。
我们的第一个功能更新:新聊天页面UI
首先就是我们的聊天页面,1.2版本时,我们的页面还是仅仅停留在文字的阶段,这对开发时期的软件还好,但绝不是长久之计。如图所示我们的1.2的剪贴板版本UI页面:

先跟大家讲述一下做一份UI聊天页面的所需吧,让各位都有一份这方面的概念:
- 一个recyclerView,这个控件是Android上的,RecyclerView可以让我们轻松高效地显示大量数据,可提高性能和应用响应能力。
- 一个ChatAdapter类,这个类负责初始化消息列表、根据消息的发送者类型分配不同视图类型、创建+绑定数据View
- 一个ChatMessageQueueUpdater类,这个类负责消息滚动支持在RecyclerView控件上,用消息队列的做法
- 俩个drawable绑定到Layout文件的气泡布局设计,这个是负责分配不同视图类型时绑定气泡布局
- Edittext文本输入控件和按钮等等…
那么,先讲讲RecyclerView背后的ChatAdapter类吧(因为控件没啥好讲的,反而这个类都是干货)在这个函数方法里,多数都是Override重构函数,也就是说只要我们调用了自己的类,就会执行自己写好的想要的操作。
然后就是分辨接收消息和发送消息了,这下面的代码,可以支持我们直接在该类里做出判断:
至于消息滚动,我们在上一篇文章有写相关的内容了,这里就不废话了,想要了解最终代码的话我们Github也有注释,可以前往访问查阅:点我前往GitHub
接下来让我讲讲布局的写法吧,其实本来我们的布局是气泡样式。但是在我最近研究了微信这款APP后,我决定借鉴一波微信的原版聊天气泡,如下是我微信的气泡写法:
效果呢?如图所示:

这中间,其实还有我们自己的小插曲,其实我们1.3版本内测时我们有在GitHub公开过我们的版本。但由于太多的BUG,和各种奇奇怪怪的小问题,于是就在后面我们把UI功能给回撤了。
现在的UI版本,我们还多了一款字体功能,既然写了新UI,用些免费商用版本的字体,提升聊天气泡的美感也是一件好事!代码如下:
我们的第二个功能更新,也算是我们之前功能的进阶版了:剪贴板不再仅验证码了。
现在我们把剪贴板的功能做到了如今的ChatProcessorForExtract
类里,代码片段如下:
现在剪贴板功能,支持了诸如验证码、网页链接、电子邮件、电话号码(仅国内电话号码)。这些功能绝大多数情况下可以满足大家的使用了,而且我们还对如上的信息,做隐私保护,设定好了定时器,达到规定时间后删除提取复制的剪贴板内容。代码如下:
来到我们第三个功能了,针对开发者的:星闪串口日志全部获取,串口对方MAC地址打印在UI上。
为我们后续的串口Log功能做铺垫,所以这个功能我目前仅仅做了一个提取MAC地址。星闪硬件侧端仓库代码,对星闪MAC地址分配的有些问题,我们提取并不好做,代码如下:
相关开发者的串口Log功能,我们后续还会继续做补充和针对优化,敬请期待这项功能,能让大家和开发者们满意。
最后的大功能,也就是现在很多功能设置,可以在UI上自由决定了。
目前我们的功能不多,仅仅开放设置如下功能:
- 聊天保存(SQLite)
- 历史设备记录(SQLite)
- 聊天文本进入剪贴板
功能虽少,但给未来的代码的基础,我们省不了一点,今天写的该复杂就复杂,代码如下:
CompoundButton.OnCheckedChangeListener SettingsChangeListener = new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if (compoundButton.isEnabled()) {
if (compoundButton.getId() == id.cbSettingsForShowLog) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForSaveSQL) {
if (isChecked) {
ChatUtils.setSqlitemanager(true);
SnackBarToastForDebug(context,"您已开始保存您的聊天记录啦!","目前为" + ChatUtils.isSqlitemanager(),0,Snackbar.LENGTH_SHORT);
} else {
if (ChatUIAlertDialog.show(compoundButton.getContext(), "聊天保存(SQLite)", "您确定要停止保存聊天数据吗?停止保存您的聊天,将会在接下来聊天时无法保存内容,可能会造成聊天记录丢失。", compoundButton))
ChatUtils.setSqlitemanager(false);
SnackBarToastForDebug(context,"已为您取消保存聊天记录!","目前为" + ChatUtils.isSqlitemanager(),0,Snackbar.LENGTH_SHORT);
}
} else if (compoundButton.getId() == id.cbSettingsForDelSQL) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForHistory) {
if (isChecked) {
ChatUtils.setSqliteHistory(true);
SnackBarToastForDebug(context,"您已开始展示您的聊天记录啦!","目前为" + ChatUtils.isSqliteHistory(),0,Snackbar.LENGTH_SHORT);
} else {
if (ChatUIAlertDialog.show(compoundButton.getContext(), "历史设备记录(SQLite)", "您确定要停止展示聊天数据在UI上吗?", compoundButton))
ChatUtils.setSqliteHistory(false);
SnackBarToastForDebug(context,"已为您取消保存聊天记录!","目前为" + ChatUtils.isSqliteHistory(),0,Snackbar.LENGTH_SHORT);
}
} else if (compoundButton.getId() == id.cbSettingsForClearSCR) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForEncryption) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForClip) {
if (isChecked) {
ChatUtils.setClipMessages(true);
SnackBarToastForDebug(context,"您已开启剪贴板功能!","目前为" + ChatUtils.isClipMessages(),0,Snackbar.LENGTH_SHORT);
} else {
if (ChatUIAlertDialog.show(compoundButton.getContext(), "聊天文本进入剪贴板", "您确定要停止剪贴板吗?剪贴板功能可以帮您自动按规则捕获内容,可以很大程度上帮助到您手动任务耗时的情况,取消则需要您自行处理屏幕上的UI信息。", compoundButton))
ChatUtils.setClipMessages(false);
SnackBarToastForDebug(context,"已为您取消剪贴板功能!","目前为" + ChatUtils.isClipMessages(),0,Snackbar.LENGTH_SHORT);
}
} else if (compoundButton.getId() == id.cbSettingsForPush) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForBackground) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForBackup) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
} else if (compoundButton.getId() == id.cbSettingsForNFC) {
SnackBarToastForDebug(context,"敬请期待!","如有不适,那没办法,做的慢怪我咯o(*^@^*)o",0,Snackbar.LENGTH_SHORT);
}
}
}
};
SettingsForShowLog = findViewById(id.cbSettingsForShowLog);
SettingsForShowLog.setEnabled(false);
SettingsForShowLog.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForSaveSQL = findViewById(id.cbSettingsForSaveSQL);
SettingsForSaveSQL.setEnabled(true);
SettingsForSaveSQL.setChecked(true);
SettingsForSaveSQL.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForDelSQL = findViewById(id.cbSettingsForDelSQL);
SettingsForDelSQL.setEnabled(false);
SettingsForDelSQL.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForHistory = findViewById(id.cbSettingsForHistory);
SettingsForHistory.setEnabled(true);
SettingsForHistory.setChecked(false);
SettingsForHistory.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForClearSCR = findViewById(id.cbSettingsForClearSCR);
SettingsForClearSCR.setEnabled(false);
SettingsForClearSCR.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForEncryption = findViewById(id.cbSettingsForEncryption);
SettingsForEncryption.setEnabled(false);
SettingsForEncryption.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForClip = findViewById(id.cbSettingsForClip);
SettingsForClip.setEnabled(true);
SettingsForClip.setChecked(true);
SettingsForClip.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForPush = findViewById(id.cbSettingsForPush);
SettingsForPush.setEnabled(false);
SettingsForPush.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForBackground = findViewById(id.cbSettingsForBackground);
SettingsForBackground.setEnabled(false);
SettingsForBackground.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForBackup = findViewById(id.cbSettingsForBackup);
SettingsForBackup.setEnabled(false);
SettingsForBackup.setOnCheckedChangeListener(SettingsChangeListener);
SettingsForNFC = findViewById(id.cbSettingsForNFC);
SettingsForNFC.setEnabled(false);
SettingsForNFC.setOnCheckedChangeListener(SettingsChangeListener);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
首先,我直接在Init()初始化里,完成好每个Checkbox们的勾选监听方法,这样只需要每个Checkbox调用该方法就可以了,给那些无法设置和没做出来的先写上setEnabled(false)
,能用的上的写好setEnabled(true)
再完善方法里的判断和AlertDialog对话框提示。效果如下:

这个对话框我们还设置好了防止外部点击消失的做法,需要确认取消好了才能取消掉哦。
剩下的小功能和其余更新,诸如:
- 给星闪LOGO做个“闪烁效果”在右上角按钮
- 调整发送机制目前做到单行输入+输入法或键盘Enter即发送操作
- 完善串口日志全部获取
- 修复星闪网络随机无法接收聊天信息的Bug
- 修复消息滚动随机的Bug
- 修复剪贴板问题
- 优化代码结构,利于维护
其余的更新我就不多介绍了~
距离我们先前俩篇文章的发布,已经过去10天多了。我们过去向大家承诺的功能,都已经做出来了,接下来的功能,做得出来测试完了,我们会继续写文章给大家讲解,敬请期待下一篇文章和接下来的1.4更新。
本项目还是那样的承诺给大家,我们参加了海思社区的星闪开发者体验官活动,我们只要入选该活动,对软件的开发我们将继续保持下去。希望51CTO的大家能多多支持一些我们,您们的支持就是我坚持开发最大的动力。
Github仓库链接
Gitee仓库链接
硬件侧端链接
可以跟鸿蒙手机如meta 60系类通讯吗?