一、期末大作业的目的与要求:
1. 垃圾分类界面
请尽量模拟如下垃圾分类APP的功能,即参考如下的界面展示形式及功能模块:
2. 具体要求
模拟图1所示垃圾分类APP,介绍垃圾分类与回收相关的一些知识点并能提供相应服务:
1) 建议包含的一些功能:活动之间的转换与数据传递;能适应不同的展示界面;有登录功能,强制下线功能;数据有多样化的持久化功能;能跨程序提供与共享数据;有展示一些多媒体的功能;
2) 较好的实现了书本上介绍的一些较成熟的功能,并能较好的把这些功能融合在一个完整且无大bug的APP里;
3) 能在此基础上构建自己的报告亮点,如实现了书本不一样的功能模块,或者为某个知识点找到一些新的应用场景,或者能解决同学们普遍存在的一些问题等;
4) 模拟的APP不局限于所参照APP的功能,即尽量模拟这些功能,不要求将每个功能都实现,如果某个功能不能体现已学知识点,可以不用考虑,当然如果能想办法实现出来,可以作为报告亮点;即不必与这些功能完全一样,可在这些功能基础上进行变通,达到类似的效果就可以;可以设计一些该APP没有的功能,并能清楚说明这些功能的实现方式、潜在的用途等;同时布局的设计也不必与参考APP完全一样,可根据自己需要适当调整;
5) 总体目标是灵活利用所学的知识点,做到每个功能各种实现方式的丰富化(如数据的持久化的三种实现方式都能在APP中有所体现),并且能体现不同实现方式的优劣,如果能在APP上体现会更好;
3. 部分参考
1)功能实现参考:图1第2列图尽量参考第6章数据持久化技术的各个知识点;第3列尽量参考布局及活动之间的跳转,碎片的实现,多媒体展示功能;第4列可以利用数据持久化技术;
2)潜在的扩展功能:图1第4列尽量参考Android基于位置的服务;添加一个小功能,整合网络技术的应用,即将一个HTML网页文件中的文本与图片网址进行分离,并将文本与图片用不同文件夹分开保持;利用数据后台下载的功能;
3)可以借鉴的部分章节内容,第12章可以让你的APP界面变得更美观;第14章展示了一个大型的工程,可以学习下多个功能怎样在一个工程里体现;
4. 其它要求
1)构建的APP要格式工整,美观;
2)实验报告中需要有功能的描述、实验结果的截屏图像及详细说明;结果展示要具体,图文交叉解释;代码与文本重点要突出;
3)也欢迎采用课程后续章节的知识点完成本次大作业,如果实现的功能言之合理,会考虑酌情加分;
4)每位同学在最后一次课都需要上台报告,并且最好能现场演示APP的功能等,没上台报告的同学分数会受一定的影响;
5)报告由个人独立完成。
5. 评分标准
APP协议完成度高,与参考APP有一定的相似度,功能完善、丰富。能实现活动的编写、自定义用户界面的开发、碎片开发、广播机制、数据持久化与共享技术、网络技术、后台服务的应用等。
-------------(60分)
模拟APP结构合理,代码规范,界面美观易用。项目报告撰写规范、美观整齐,内容详实且能准备描述项目内容和设计思想、原理、框架等,项目报告要求5号字、除前两页外A4版面不低于10页的长度。
-------------(15分)
提供程序源代码和可执行程序(或安装程序);报告文档采用单独的word文档,项目所有代码(不是整个工程文件,应该总共不超过5M)在第17周之前打包作为附件进行上传blackboard系统;纸质版交到任课老师处。
-------------(10分)
项目报告能够详细,准确的描述项目内容,并在最后一堂课有较好的展示效果。
-------------(15分)
二、实验过程和代码与结果
1. 实验亮点:
①通过数据持久化与共享技术使用SQLite完成了历史搜索的功能;
②通过调用API网络接口完成对垃圾类别的判断;
③完成创新性功能——图片识别和语音识别,以及对应权限的申请;
④使用广播系统对网络进行判断,给出有无网络的提示;
⑤代码文件进行分文件夹分层存储,代码有易懂的结构;
⑥通过动态绘图进行布局,对不同种设备进行自适应匹配;
⑦使用更精美的UI动画(例如加载动画);
⑧通过代码托管平台实现了程序的版本更新;
⑨通过链接APPStore实现了对APP进行评分的可行性;
⑩完成了更细节的优化(如连续两次返回才退出,并在第一次时提示再滑动一次以退出);
⑪通过使用本地创建的秘钥将文件打包成APK;
⑫所有代码都有大量的注释,方便又易懂;
⑬实现了各个Adapter与Json处理工具,极大方便了开发过程;
⑭通过使用Intent进行Activity传值完成垃圾详情的查看;
⑮使用Fragment完成垃圾详情的查看;
⑯通过Logcat完成对整个项目的调试;
⑰采用LinearLayout进行布局。
2. “我的垃圾分类APP”的构建过程及结果
(1)欢迎页面的实现:
在日常使用中,为了使用APP更美观,并给APP内部数据加载预留时间,应设置一欢迎界面。在本次大作业中也有体现。
通过自动计时两秒,两秒后实现Activity的自动跳转。
public class WelcomeActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_welcome); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); getWindow().setStatusBarColor(Color.TRANSPARENT); } RxCountDown.countdown(2).doOnSubscribe(disposable -> { }).subscribe(new Observer<Integer>() { @Override public void onSubscribe(Disposable d) {} @Override public void onNext(Integer integer) {} @Override public void onError(Throwable e) {} @Override public void onComplete() { startActivity(new Intent(WelcomeActivity.this, HomeActivity.class)); finish(); } }); }
采用了ConstraintLayout进行布局:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.activity.WelcomeActivity"> </android.support.constraint.ConstraintLayout>
(2)垃圾分类的实现:
为了实现垃圾分类,我使用了一种互联网技术——API接口,并通过接口与服务器的数据交互完成对垃圾分类的判断。实现分获取API数据,显示和查询详细三个连续操作,下面分开介绍。
①获取API数据:
private void loadData() { String serachStr = mEtSearch.getText().toString(); if (StringUtils.isEmpty(serachStr)) { showToast("请输入搜索内容", mRelTops); } else { if (OrcLoadUtils.isWifiProxy(RkApplication.getInstance())) { showToast("请先关闭代理等", mRelTops); } else { showStatus(mViewLottie, new LoadAbstract(this)); OrcLoadUtils.searchName(serachStr).subscribe(o -> { try { hideStatus(mViewLottie); TrashResultEntity entity = (TrashResultEntity) o; if (entity != null) { if (entity.Ok) { if (entity.Data != null && entity.Data.size() > 0) { String serachStr2 = mEtSearch.getText().toString(); if (!StringUtils.isEmpty(serachStr2)) { setRestData(entity.Data); } } else { loadDataTwo(serachStr); } } else { loadDataTwo(serachStr); } } } catch (Exception e) { loadDataTwo(serachStr); e.printStackTrace(); } }); } } }
在获取API数据时,定义了TrashResultEntity类用以接受返回的结果,并将各个获取的数据写回到该类中。
②进行数据展示:
要显示数据即在对应的Adapter中写入数据即可,通过函数传参进入即可。此外,如果查询结果为空,则需要进行特判,并显示未查询到结果。
private void setRestData(List<TrashResultEntity.DataBean> data) { mTypeRecycle.setVisibility(View.GONE); mResAdapter.setHeaderView(headerView); mResAdapter.setNewData(data); } private void setRestData(String search, String message) { if (StringUtils.isEmpty(message)) { message = "未查询到结果"; } List<TrashResultEntity.DataBean> list = new ArrayList<>(); TrashResultEntity.DataBean data = new TrashResultEntity.DataBean(); data.Kind = 1008; data.msg = message; data.Name = search; list.add(data); mTypeRecycle.setVisibility(View.GONE); mResAdapter.setHeaderView(headerView); mResAdapter.setNewData(list); }
③查询详细信息:
此时只需判断传入的垃圾的种类,如果为合法种类,则通过intent传值打开对应的Activity即可。
private void openDetails(int type) { if (type == 1 || type == 2 || type == 3 || type == 4) { Intent intent = new Intent(this, RefuseDetailActivity.class); intent.putExtra("type", type); startActivity(intent); } }
(3)语音识别:
在本次大作业中,为了方便各种用户不同的数据输入需求,我引用百度语音来进行语音识别并进行垃圾分类。进行语音识别的顺序大致如下:
①首先申请录音权限:
如果获得了录音权限,则将百度语音就绪;若无法获得录音权限,则弹出提示“权限不足”。
private void openBdAudio() { PermissionUtils.permission(Manifest.permission.RECORD_AUDIO, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.INTERNET, Manifest.permission.READ_PHONE_STATE, Manifest.permission.WRITE_EXTERNAL_STORAGE) .callback(new PermissionUtils.FullCallback() { @Override public void onGranted(List<String> permissionsGranted) { mLottigSound.setVisibility(View.VISIBLE); mImSound.setVisibility(View.GONE); asr.send(SpeechConstant.ASR_START, "{}", null, 0, 0); mTvSoundTips.setText("按下结束说话"); } @Override public void onDenied(List<String> permissionsDeniedForever, List<String> permissionsDenied) { LogUtils.e("权限不足"); showToast("权限不足"); } }).request(); }
②判断服务器是否就绪,若就绪则可以获取用户语音输入:
if(name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)){ // 引擎就绪,可以说话,一般在收到此事件后通过UI通知用户可以说话了 LogUtils.e("引擎就绪,可以说话,一般在收到此事件后通过UI通知用户可以说话了"); mLottigSound.setVisibility(View.VISIBLE); mImSound.setVisibility(View.GONE); }
③当用户语音输入时,要提示出开始录音与录音结束
此处判断是否识别结束,如果已经识别结束,则给出提示“识别结束”,并将按钮的文字重新写为“按下说话”。
if(name.equals(SpeechConstant.CALLBACK_EVENT_ASR_FINISH)){ // 识别结束 LogUtils.e("识别结束"); mLottigSound.setVisibility(View.GONE); mTvSoundTips.setText("按下说话"); mImSound.setVisibility(View.VISIBLE); }
④当用户输入完成时,及判断是否为有效输入
如果输入有效,则调用API对垃圾的类别进行判断。在判断数据是否有效输入时,可以判断数据长度不为空且字符数组长度大于0
if(name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)){ LogUtils.e(params); RecogResult recogResult = RecogResult.parseJson(params); // 识别结果 String[] results = recogResult.getResultsRecognition(); if (null != results && results.length > 0) { mEtSearch.setText(results[0]); } //LogUtils.e(results); }