目录
源码:https://github.com/Alex-Shen1121/SZU_Learning_Resource/tree/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%B8%8E%E8%BD%AF%E4%BB%B6%E5%AD%A6%E9%99%A2/%E7%A7%BB%E5%8A%A8%E8%AE%BE%E5%A4%87%E4%BA%A4%E4%BA%92%E5%BA%94%E7%94%A8/%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A
目的与要求
- 垃圾分类界面
请尽量模拟如下垃圾分类APP的功能,即参考如下的界面展示形式及功能模块:




-
具体要求
模拟图1所示垃圾分类APP,介绍垃圾分类与回收相关的一些知识点并能提供相应服务:- 建议包含的一些功能:活动之间的转换与数据传递;能适应不同的展示界面;有登录功能,强制下线功能;数据有多样化的持久化功能;能跨程序提供与共享数据;有展示一些多媒体的功能;
- 较好的实现了书本上介绍的一些较成熟的功能,并能较好的把这些功能融合在一个完整且无大bug的APP里;
- 能在此基础上构建自己的报告亮点,如实现了书本不一样的功能模块,或者为某个知识点找到一些新的应用场景,或者能解决同学们普遍存在的一些问题等;
4) 模拟的APP不局限于所参照APP的功能,即尽量模拟这些功能,不要求将每个功能都实现,如果某个功能不能体现已学知识点,可以不用考虑,当然如果能想办法实现出来,可以作为报告亮点;即不必与这些功能完全一样,可在这些功能基础上进行变通,达到类似的效果就可以;可以设计一些该APP没有的功能,并能清楚说明这些功能的实现方式、潜在的用途等;同时布局的设计也不必与参考APP完全一样,可根据自己需要适当调整; - 总体目标是灵活利用所学的知识点,做到每个功能各种实现方式的丰富化(如数据的持久化的三种实现方式都能在APP中有所体现),并且能体现不同实现方式的优劣,如果能在APP上体现会更好;
-
部分参考
- 功能实现参考:图1第2列图尽量参考第6章数据持久化技术的各个知识点;第3列尽量参考布局及活动之间的跳转,碎片的实现,多媒体展示功能;第4列可以利用数据持久化技术;
- 潜在的扩展功能:图1第4列尽量参考Android基于位置的服务;添加一个小功能,整合网络技术的应用,即将一个HTML网页文件中的文本与图片网址进行分离,并将文本与图片用不同文件夹分开保持;利用数据后台下载的功能;
- 可以借鉴的部分章节内容,第12章可以让你的APP界面变得更美观;第14章展示了一个大型的工程,可以学习下多个功能怎样在一个工程里体现;
-
其它要求
- 构建的APP要格式工整,美观;
- 实验报告中需要有功能的描述、实验结果的截屏图像及详细说明;结果展示要具体,图文交叉解释;代码与文本重点要突出;
- 也欢迎采用课程后续章节的知识点完成本次大作业,如果实现的功能言之合理,会考虑酌情加分;
- 每位同学在最后一次课都需要上台报告,并且最好能现场演示APP的功能等,没上台报告的同学分数会受一定的影响;
- 报告由个人独立完成。
-
评分标准
- APP协议完成度高,与参考APP有一定的相似度,功能完善、丰富。能实现活动的编写、自定义用户界面的开发、碎片开发、广播机制、数据持久化与共享技术、网络技术、后台服务的应用等。
-------------(60分) - 模拟APP结构合理,代码规范,界面美观易用。项目报告撰写规范、美观整齐,内容详实且能准备描述项目内容和设计思想、原理、框架等,项目报告要求5号字、除前两页外A4版面不低于10页的长度。
-------------(15分) - 提供程序源代码和可执行程序(或安装程序);报告文档采用单独的word文档,项目所有代码(不是整个工程文件,应该总共不超过5M)在第17周之前打包作为附件进行上传blackboard系统;纸质版交到任课老师处。
-------------(10分) - 项目报告能够详细,准确的描述项目内容,并在最后一堂课有较好的展示效果。
-------------(15分)
- APP协议完成度高,与参考APP有一定的相似度,功能完善、丰富。能实现活动的编写、自定义用户界面的开发、碎片开发、广播机制、数据持久化与共享技术、网络技术、后台服务的应用等。
实验过程和代码与结果
“我的垃圾分类APP”的构建过程及结果
注:测试环境为华为nova5 pro
具体项目构建过程可以参考github
URL:https://github.com/Alex-Shen1121/GarbageClassificationAndroidAPP
此部分将大致展示APP构建过程及效果图,具体代码实现及原理将在下一部分具体展示。
-
第一步做的任务是设计APP登录界面,首先是一个欢迎界面的设计。通过查询网上资料,完成动态动画设计。
-
第二步是登录与注册页面的设计,主要利用的技术来自书本数据持久化这一章节,完成了登录,注册,记住密码等功能。
-
第三步是开始设计应用的主要界面。可以通过点击APP下方的项目选项按钮,跳转到不同的访问界面
-
第四步是分类百科界面的设计。过程中利用了大量时间去学习如何使用Tablayout与ViewPager的结合,其中花费了许多时间在调试上,最终完成了与源程序大致相同的实现效果,实现了左右滑动切换fragment的效果。

-
第五步是设置界面设计。其中包括了多个界面,利用到了众多技术,也查询了很多资料,比如如何跳转到权限界面,应用商城等等。
-
第六步是首页设计。其中包括了数据的查询功能以及宣传视频的播放功能。其中数据库包含了4000+的数据,可以完成对于字符串的匹配搜索,完成大部分垃圾分类的查询工作。
最终整个APP从设计到完成大约花费一周的时间,实现了绝大部分目标APP的功能模拟,并加入了部分自己的理解,比较好的复现了APP。
请详细说明“我的垃圾分类APP”的功能、出现的关键问题及解决方案
以下部分按照此顺序介绍各部分功能:
- 欢迎界面
- 登录功能——注册功能
- 分类百科
- 设置——切换城市——应用打分——关于——权限管理——退出登录
- 首页——视频介绍——查询功能
欢迎界面
主要技术:ViewCompat,Activity,UI设计,Intent
首先将一张图片存放在ImageView之中撑满整个画面,然后调用ViewCompat.animate方法并设置监听,对图像做伸缩变换以及动态时长控制。最后在动画结束的时候退出并进入登陆界面。
1.//设置图片动画
2.ViewCompat.animate(imageView).apply {
3. //缩放,变成1.0倍
4. scaleX(1.0f)
5. scaleY(1.0f)
6. //动画时常1秒
7. duration = 1000
8. //动画监听
9. setListener(object : ViewPropertyAnimatorListener {
10. override fun onAnimationEnd(view: View?) { //动画结束
11. //进入主界面,并结束掉该页面
12. startActivity(Intent(this@WelcomeActivity, LoginActivity::class.java))
13. finish()
14. })
15. }
登录功能
主要技术:数据持久化,Activity,UI设计,AlertDialog,Intent
通过文件读写的方式,添加默认用户名及密码,方便用户第一次体验APP
1.val output = openFileOutput("account_password.txt", MODE_APPEND)
2. val writer = BufferedWriter(OutputStreamWriter(output))
3. writer.use {
4. it.write("admin\n")
5. it.write("123456\n")
6. }
登陆的时候,通过文件读写的方式获取APP的账户列表,后期可以通过连接云端服务器。
1.reader.forEachLine {
2. line += 1
3. if (line % 2 == 1)
4. account.add(it)
5. else if (line % 2 == 0)
6. password.add(it)
7.}
将正确的用户名密码以键值对的方式保存。
accountList[account[i]] = password[i]
与用户输入的用户名密码进行匹配,如果完全匹配则进入相对应界面,否则弹出错误提示,并删去密码,重新输入(更加符合使用习惯)。
匹配成功:效果是进入主界面
1.val intent = Intent(this, MainPage::class.java)
2.startActivity(intent)
匹配失败:效果是弹出登陆失败提示框
1. AlertDialog.Builder(this).apply {
2. setTitle("登陆失败")
3. setMessage("请重新检查用户名与密码。\n或者联系管理员。")
4. setCancelable(false)
5. setPositiveButton("OK") { _, _ -> }
6. show()
7.}
8.passwordEdit.text = null
记住密码功能。如果勾选了,用户在下一次登录(无论是强制退出或者重启APP)都可以免去重新输入账号密码的过程,利用的技术是SharePreferences数据持久化。
进入登录页面时,检查prefs中“remember_password”是否为true,true则将保存的用户名密码直接显示在输入框内,否则不做显示。
1.val prefs = getPreferences(Context.MODE_PRIVATE)
2. val isRemember = prefs.getBoolean("remember_password", false)
3. if (isRemember) {
4. val account = prefs.getString("account", "")
5. val password = prefs.getString("password", "")
6.
7. accountEdit.setText(account)
8. passwordEdit.setText(password)
9. rememberPass.isChecked = true
10. }
登录账号时将用户名密码以及是否记住密码选项存入SharePreferences为下次登录做准备。
1.if (rememberPass.isChecked) {
2. editor.putBoolean("remember_password", true)
3. editor.putString("account", account)
4. editor.putString("password", password)
5. }
6.editor.apply()
注册功能
主要技术:数据持久化,UI设计,Intent
注册过程会进行三层检查只有都通过了才会注册成功。
第一层:检查是否为空串,否则返回“请正确输入信息”
1. if (name == "" || password1 == "" || password2 == "") {
2. passwordEdit.text = null
3. passwordCheckEdit.text = null
4. Toast.makeText(this, "请正确输入信息", Toast.LENGTH_SHORT).show()
5.}
第二层:检查用户名是否存在,调用contains函数,否则返回“该用户名已存在,请重新输入”
1.if (accountList.contains(name)) {
2. Toast.makeText(this, "该用户名已存在,请重新输入", Toast.LENGTH_SHORT).show()
3. passwordEdit.text = null
4. passwordCheckEdit.text = null
5. accountEdit.text = null
6.}
第三层:检查密码与确认密码是否完全相同,否则返回“两次密码不一致,请重新输入”
1.if(password1!=password2){
2. Toast.makeText(this, "两次密码不一致,请重新输入", Toast.LENGTH_SHORT).show()
3. passwordEdit.text = null
4. passwordCheckEdit.text = null
5.}
当用户输入的账号及密码通过了这三层检查,就可以完成注册,即将用户输入的信息通过文件读取的方式,写入txt文本中,实现注册功能。例如在此处注册了test01-123456的账户。
1.val output = openFileOutput("account_password.txt", MODE_APPEND)
2. val writer = BufferedWriter(OutputStreamWriter(output))
3. writer.use {
4. it.write(name+'\n')
5. it.write(password1+'\n')
6. }
Toast.makeText(this, "注册成功", Toast.LENGTH_SHORT).show()
主界面
思路:主界面其实只包含了一个界面,其中在屏幕正中放置了三个LinearLayout并将属性设置为了android:visibility=“gone”,当需要展示时就点击下方按钮,就将对应的布局设置为可见,并刷新下方按钮的属性,实现不同界面的切换功能。
按钮属性切换:效果是将正在展示的界面变为彩色并且着重文字效果。
1.titleText.text = "垃圾分类"
2.button1.setImageResource(R.drawable.pic12)
3.text1.textSize = 20F
4.text1.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
界面切换:效果是展示不同的界面
1.column1.visibility = View.VISIBLE
分类百科
主要技术:TabLayout+ViewPager,UI设计,Fragment碎片
思路:
- 为viewpager编写适配器,传入supportFragmentManager对象,并重写getItem,getCount,getPageTitle,目的是设置上方滑动栏的数量。
1.viewpager.adapter = object : FragmentPagerAdapter(supportFragmentManager) {
2. override fun getItem(position: Int): Fragment {
3. return Lfragments[position]
4. }
5. override fun getCount(): Int {
6. return Lfragments.size
7. }
8. override fun getPageTitle(position: Int): CharSequence? {
9. return Ltitles[position]
10. }
11.}
- 为每一个tab设置图片,文字,以及被选中时的样式,效果是不同的tab对应不同的图片及文字,并且在被选中的时候都会将文字由黑色转变为紫色,更加瞩目。
1.mTabText.text = Ltitles[i]
2.mTabIcon.setImageResource(Limg[i])
3.//更改选中项样式
4.if (i === ItemWhat) {
5. mTabIcon.setImageResource(Limg[i])
6. mTabText.setTextColor(ContextCompat.getColor(this, R.color.purple_200))
7.}
8.//设置样式
9.tabs2.getTabAt(i)?.customView = view
- 设置默认选中页,宏定义
tabs2.getTabAt(ItemWhat)?.select()
- 将ViewPager与TabLayout进行绑定
1.tabs2.setupWithViewPager(viewpager)
最终效果就如下图
5. 为每一个tab编写一个fragment并重写其中的onActivityCreated以及onCreateView方法。以可回收垃圾为例。
1.override fun onActivityCreated(savedInstanceState: Bundle?) {
2. super.onActivityCreated(savedInstanceState)
3. picture.setImageResource(R.drawable.frag1)
4. title1.text="可回收物是指"
5. task1.text="废纸张、废塑料、废玻璃制品、废金属、废织物等适宜回收、可循环利用的生活废弃品。"
6. title2.text="可回收物投放要求"
7. task2.text="1. 轻投轻放\n2. 清洁干燥,避免污染\n3. 废纸尽量平整\n"
8. }
9.
10. override fun onCreateView(
11. inflater: LayoutInflater,
12. container: ViewGroup?,
13. savedInstanceState: Bundle?
14. ): View? {
15. return inflater.inflate(R.layout.fragment,container,false)
16. }
- 设置tab给选中时的动态效果,设置监听OnTabSelectedListener,并重写以下方法。
1.override fun onTabSelected(tab: TabLayout.Tab) {
2. //选中时进入,改变样式
3. ItemSelect(tab)
4. //onTabselected方法里面调用了viewPager的setCurrentItem 所以要想自定义OnTabSelectedListener,也加上mViewPager.setCurrentItem(tab.getPosition())就可以了
5. viewpager.currentItem = tab.position
6.}
7.
8.override fun onTabUnselected(tab: TabLayout.Tab) {
9. //未选中进入,改变样式
10. ItemNoSelect(tab)
11.}
被选中时,会调用ItemSelect函数;未被选中时,会调用ItemNoSelect函数,效果是刷新tab样式。ItemNoSelect类似,不做展示。
1.//某个项选中,改变其样式
2.vate fun ItemSelect(tab: TabLayout.Tab) {
3. val customView = tab.customView
4. val tabText = customView!!.findViewById<View>(R.id.item_text) as TextView
5. val tabIcon: ImageView = customView.findViewById<View>(R.id.item_img) as ImageView
6. tabText.setTextColor(ContextCompat.getColor(this, R.color.purple_200))
7. val stitle = tabText.text.toString()
8. for (i in Ltitles.indices) {
9. if (Ltitles[i] == stitle) {
10. tabIcon.setImageResource(Limg[i])
11. }
12. }
13. }
最终效果就是可以通过屏幕的左右滑动,实现fragment之间的平滑的切换。
设置
主要技术:UI设计,Activity,Intent
在页面中方式orientation属性为vertical的LinearLayout,在其中一次放置了6个选项,并且为每一个Layout设计Activity跳转事件。(以切换地区为例)
1.changePlace.setOnClickListener(){
2. val intent= Intent(this, PlaceActivity::class.java)
3. startActivity(intent)
4.}
切换地区
主要技术:数据持久化,UI设计,RecyclerView,Intent
- 编写RecyclerView的Adapter类,需要重写三个方法和一个内部类
1.inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
2. val placeName: TextView = view.findViewById(R.id.placeName)
3.}
onCreateViewHolder方法:
1.override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
2. val view = LayoutInflater.from(parent.context)
3. .inflate(R.layout.place_item, parent, false)
4. val viewHolder = ViewHolder(view)
5. return viewHolder
6.}
onBindViewHolder方法
1.override fun onBindViewHolder(holder: ViewHolder, position: Int) {
2. val placename = place_List[position]
3. holder.placeName.text = placename
4.}
getItemCount方法
1.override fun getItemCount() = place_List.size
- 然后为界面内的recyclerView设置LayoutManager与Adapter,其中placeList是一个包含城市信息的的列表
1.val layoutManager = LinearLayoutManager(this)
2.recyclerView.layoutManager = layoutManager
3.val adapter1 = place_Adapter(placeList)
4.recyclerView.adapter = adapter1
最终效果:
- 为RecyclerView设置点击事件,在onCreateViewHolder方法设置监听事件。
获取点击的地址名称信息,将其保存在sharedPreferences中,并作出反馈,以便后期查询。
1.val position: Int = viewHolder.layoutPosition
2.val placeName = place_List[position]
3.val prefs = parent.context.getSharedPreferences("current_place", Context.MODE_PRIVATE)
4.val editor = prefs.edit()
5.editor.putString("place", placeName)
6.editor.apply()
7.Toast.makeText(parent.context,"已成功修改为$placeName",Toast.LENGTH_SHORT).show()
- 点击屏幕上方的返回按钮,finish当前的Activity,并且从sharedPreferences中获取place对应的键值,刷新软件的位置信息。
1.val prefs = getSharedPreferences("current_place", Context.MODE_PRIVATE)
2.val current_place = prefs.getString("place", null)
3.currentPlace.text = current_place
4.finish()
给我们评分
主要技术:Activity,Intent
效果是从手机APP跳转到手机应用商城之中为APP打分。经过上网查询,获得了以下各个常见手机商城的对应Intent跳转目录。
地址 | 应用 |
---|---|
com.android.vending | Google Play |
com.tencent.android.qqdownloader | 应用宝 |
com.qihoo.appstore | 360手机助手 |
com.baidu.appsearch | 百度手机助手 |
com.xiaomi.market | 小米应用商店 |
com.wandoujia.phoenix2 | 豌豆荚 |
com.huawei.appmarket | 华为应用市场 |
com.taobao.appcenter | 淘宝手机助手 |
com.hiapk.marketpho | 安卓市场 |
cn.goapk.market | 安智市场 |
从APP跳转到其他商城APP,可以通过调用launchAppDetail(),并且传入两个参数,第一个是appPkg,第二个是marketPkg。因为这个APP并没有上架商城,所以查询不到。
1.launchAppDetail("com.xxxxxx", "com.huawei.appmarket")
关于我们
主要技术:Activity,Intent
跳转到编写了《关于我们》页面的活动
1.val intent= Intent(this, AboutUsActivity::class.java)
2.startActivity(intent)
用户协议
主要技术:Activity,Intent,ScrollView
ScrollView控件之中的内容可以无限长,并且通过上下滑动的方式扩展页面
权限管理
主要技术:Activity,Intent
APP权限页面跳转目录为ACTION_APPLICATION_DETAILS_SETTINGS
1.val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
2.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
3.val uri = Uri.fromParts("package", packageName, null)
4.intent.data = uri
5.startActivity(intent)
退出登录
主要技术:广播机制,继承类
- 首先编写BaseActivity与ActivityCollecor。ActivityCollecor是为了将所有的已打开的Activity存入栈中,方便退出时一次性finish,回到登陆页面。BaseActivity是所有Activity的基类,并在其中设置监听事件,这样在任何一个活动之中监听到了“退出登录”的广播事件,都可以做出反馈。
ActivityCollecor类:
添加活动:
1.activities.add(activity)
移除活动:
2.activities.remove(activity)
结束所有活动:
1.for (activity in activities) {
2. if (!activity.isFinishing) {
3. activity.finish()
4. }
5.}
6.activities.clear()
BaseActivity类:
作为Activity类的基类:
1.open class BaseActivity : AppCompatActivity()
设置监听事件:
1.override fun onResume() {
2. super.onResume()
3. val intentFilter = IntentFilter()
4. intentFilter.addAction("com.example.experiment3.FORCE_OFFLINE")
5. receiver = ForceOfflineReceiver()
6. registerReceiver(receiver, intentFilter)
7.}
设置监听到广播时的反馈事件(弹出下线的窗口,关闭所有活动并跳转回登陆界面):
1.android.app.AlertDialog.Builder(context).apply {
2. setTitle("Warning")
3. setMessage("请重新登录。")
4. setCancelable(false)
5. setPositiveButton("OK") { _, _ ->
6. ActivityCollector.finishAll()
7. val i = Intent(context, LoginActivity::class.java)
8. context.startActivity(i)
9. }
10. show()
11.}
首页
思路:首页之中包含了两个部分,第一个部分是垃圾分类的查询功能,第二个部分是一个垃圾分类的科普视频。
视频介绍
主要技术:VideoView
- 视频来自于B站上一个垃圾分类的科普视频,首先将视频下载下来并放置在/res/raw文件夹之下。
- 将视频地址导入VideoView控件,并设置焦点
1.val uri = Uri.parse("android.resource://$packageName/${R.raw.video}")
2.videoView.setVideoURI(uri)
3.videoView.requestFocus()
- 添加视频的进度条功能
1.videoView.setMediaController(MediaController(this))
查询功能
主要技术:数据持久化,SQLite,UI设计,RelativeLayout
- 垃圾分类数据的收集
我在网上搜索到了一份垃圾分类的excel表格,其中包含了近4000条垃圾的分类信息。
- 创建数据库
创建myDatabaseHelper类基于SQLiteOpenHelper。建表语句如下。
创建了TrashTable表,其中包含了两个属性,垃圾名称与垃圾属性,垃圾名称为主码
1.private val createTrashTable="create table TrashTable ("+
2. " name text primary key,"+
3. "type text)"
4.
5.override fun onCreate(db: SQLiteDatabase?) {
6. db?.execSQL(createTrashTable)
7.}
- 插入数据
1.val trashList1= arrayListOf<String>(...)
2.for(i in trashList1){
3. val value=ContentValues().apply {
4. put("name",i)
5. put("type","干垃圾")
6. }
7. db.insert("TrashTable",null,value)
8.}
以此类推插入四组数据
- 查询数据
查询语句:
1.select * from TrashTable where name like '%$targetItem%'
可以返回所有包含字符串的,已查询纸盒为例
- 刷新页面
将查询到的数据,通过cursor光标移动,传递给RecyclerView。RecyclerView设置方法同城市切换,不做展示。
1.if(cursor.moveToFirst()){
2. do{
3. val name=cursor.getString(cursor.getColumnIndex("name"))
4. val type=cursor.getString(cursor.getColumnIndex("type"))
5. searchList.add(search_result(name,type))
6. }while (cursor.moveToNext())
7.}
其中,每一栏的图片根据传进来的type数据,判断应该采用哪一张图片
1.when(Type){
2. "可回收物"->holder.typeimage.setImageResource(R.drawable.trash1)
3. "湿垃圾"->holder.typeimage.setImageResource(R.drawable.trash2)
4. "干垃圾"->holder.typeimage.setImageResource(R.drawable.trash3)
5. "有害垃圾"->holder.typeimage.setImageResource(R.drawable.trash4)
6.}
6. 设置recyclerView的点击事件
根据传进来的type数据,判断AlertDialog应该发出什么提示信息。
实验总结
本次实验在完成基本要求的基础上,加入了自己的一些理解与创新。
实验过程中遇到了两个比较大的问题是
-
添加默认账号列表时会有多次重复添加的问题
一开始时的思路时在打开应用时,每次都向指定文件中加入默认账号信息。由于提取信息是通过map键值对的方式,所以并没有影响,也没有做修改。但是当用户使用次数逐渐增多时,文件内容越积越多,显然不合理。
所以经过查询,发现可以通过查询文件是否已经存在,来判断是否要加入新信息。即通过if (!file.exists())来判断,从而提高效率。 -
利用Intent传输活动间信息间信息丢失
一开始时通过intent.putExtraString()的方式向下一个活动传递用户名,身份信息。但是随着开发的进行,发现当活动通过其他方式被唤醒时会出来没有intent传递信息的情况,导致获取到空串。
所以我选择将用户信息通过Share Preference的方式进行存储,这样就可以随时随地获取账号信息了。 -
在布局文件中的EditText中添加属性android:singleLine=“true”,防止用户输入回车导致形成多行文字输入。
-
EditText中添加属性android:inputType=“textPassword”,输入的文字会以···显示,防止密码泄露。
-
切换页面视频播放
如果页面在播放就切换了页面,是不符合逻辑的。所以在每一次切换前,都加入一层逻辑判断
1.if (videoView.isPlaying) {
2. videoView.pause()
3.}
这样就可以保证切换页面时,不会在传出视频声音。
- 数据持久化对比
本次实验用到书本提到的三种数据持久化方法。通过使用我发现文件读写是效率最低的方式,并不适合大数据的存储,只适合小数据的暂时存储。Share Preference适合一对一的键值对的数据存储方式,可以提高数据查询效率,并且代码编写点简单。SQLite数据库适合大数据的增删改查,同时它也支持SQL的语句查询,所以可以支持较复杂的查询。本次实验的数据库较为简单,所以体现不出优势,但SQLite应该是最好的数据持久化方式之一。
总体而言,本次实验结合了各种技术,比较好的完成了垃圾分类APP的复现任务。