实验目的

1、熟悉Android五种主要存储方式的用法,包括共享参数SharedPreferences、数据库SQLite、SD卡文件、App的全局内存;
2、熟悉重要组件之一的应用Application的基本概念与常见用法,以及四大组件之一的内容提供器ContentProvider的基本概念与常见用法;

实验内容

“购物车”的设计与实现(参考效果图)

  1. 初始效果

  2. 手机商场的商品列表

  3. 商品详情页面

  4. 添加商品后的购物车

实验过程(实验的设计思路、关键源代码等)

源代码:https://gitee.com/shentuzhigang/mini-project/tree/master/android-shopping

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#ffffdd"android:orientation="vertical" ><include layout="@layout/activity_shopping_title" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content" ><FrameLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:padding="5dp" ><LinearLayoutandroid:id="@+id/ll_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="gone" ><LinearLayoutandroid:id="@+id/ll_cart"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical" ></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="10dp" ><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center|right"android:text="总金额:"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:id="@+id/tv_total_price"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="2"android:gravity="center|left"android:textColor="@color/red"android:textSize="25sp" /><Buttonandroid:id="@+id/btn_settle"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="结算"android:textColor="@color/black"android:textSize="20sp" /></LinearLayout></LinearLayout><LinearLayoutandroid:id="@+id/ll_empty"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="gone" ><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="100dp"android:layout_marginTop="100dp"android:gravity="center"android:text="哎呀,购物车空空如也,快去选购商品吧"android:textColor="@color/black"android:textSize="17sp" /><Buttonandroid:id="@+id/btn_shopping_channel"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="逛逛手机商场"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></FrameLayout></ScrollView></LinearLayout>
package io.shentuzhigang.demo.shoppingimport android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.util.TypedValue
import android.view.*
import android.view.ContextMenu.ContextMenuInfo
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import io.shentuzhigang.demo.shopping.MainApplication
import io.shentuzhigang.demo.shopping.bean.CartInfo
import io.shentuzhigang.demo.shopping.bean.GoodsInfo
import io.shentuzhigang.demo.shopping.database.CartDBHelper
import io.shentuzhigang.demo.shopping.database.GoodsDBHelper
import io.shentuzhigang.demo.shopping.util.FileUtil
import io.shentuzhigang.demo.shopping.util.SharedUtil
import java.util.*/*** Created by ouyangshen on 2017/10/1.*/
@SuppressLint("SetTextI18n")
class ShoppingCartActivity : Activity(), View.OnClickListener {private lateinit var iv_menu: ImageViewprivate lateinit var tv_count: TextViewprivate lateinit var tv_total_price: TextViewprivate lateinit var ll_content: LinearLayoutprivate lateinit var ll_cart: LinearLayoutprivate lateinit var ll_empty: LinearLayoutprivate var mCount // 购物车中的商品数量= 0private var mGoodsHelper // 声明一个商品数据库的帮助器对象: GoodsDBHelper? = nullprivate var mCartHelper // 声明一个购物车数据库的帮助器对象: CartDBHelper? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)requestWindowFeature(Window.FEATURE_NO_TITLE)setContentView(R.layout.activity_shopping_cart)iv_menu = findViewById(R.id.iv_menu)val tv_title = findViewById<TextView>(R.id.tv_title)tv_count = findViewById(R.id.tv_count)tv_total_price = findViewById(R.id.tv_total_price)ll_content = findViewById(R.id.ll_content)ll_cart = findViewById(R.id.ll_cart)ll_empty = findViewById(R.id.ll_empty)iv_menu.setOnClickListener(this)findViewById<View>(R.id.btn_shopping_channel).setOnClickListener(this)findViewById<View>(R.id.btn_settle).setOnClickListener(this)iv_menu.setVisibility(View.VISIBLE)tv_title.text = "购物车"}// 显示购物车图标中的商品数量private fun showCount(count: Int) {mCount = counttv_count!!.text = "" + mCountif (mCount == 0) {ll_content!!.visibility = View.GONEll_cart!!.removeAllViews()ll_empty!!.visibility = View.VISIBLE} else {ll_content!!.visibility = View.VISIBLEll_empty!!.visibility = View.GONE}}override fun onClick(v: View) {if (v.id == R.id.iv_menu) { // 点击了菜单图标openOptionsMenu()} else if (v.id == R.id.btn_shopping_channel) { // 点击了“商场”按钮// 跳转到手机商场页面val intent = Intent(this, ShoppingChannelActivity::class.java)startActivity(intent)} else if (v.id == R.id.btn_settle) { // 点击了“结算”按钮val builder = AlertDialog.Builder(this)builder.setTitle("结算商品")builder.setMessage("客官抱歉,支付功能尚未开通,请下次再来")builder.setPositiveButton("我知道了", null)builder.create().show()}}override fun onCreateOptionsMenu(menu: Menu): Boolean {// 从menu_cart.xml中构建菜单界面布局menuInflater.inflate(R.menu.menu_cart, menu)return true}override fun onOptionsItemSelected(item: MenuItem): Boolean {val id = item.itemIdif (id == R.id.menu_shopping) { // 点击了菜单项“去商场购物”// 跳转到商场页面val intent = Intent(this, ShoppingChannelActivity::class.java)startActivity(intent)} else if (id == R.id.menu_clear) { // 点击了菜单项“清空购物车”// 清空购物车数据库mCartHelper!!.deleteAll()ll_cart!!.removeAllViews()// 把最新的商品数量写入共享参数SharedUtil.Companion.getIntance(this)!!.writeShared("count", "0")// 显示最新的商品数量showCount(0)mCartGoods.clear()mGoodsMap.clear()Toast.makeText(this, "购物车已清空", Toast.LENGTH_SHORT).show()} else if (id == R.id.menu_return) { // 点击了菜单项“返回”finish()}return true}// 声明一个根据视图编号查找商品信息的映射private val mCartGoods: HashMap<Int, CartInfo> = HashMap<Int, CartInfo>()// 声明一个触发上下文菜单的视图对象private var mContextView: View? = nulloverride fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenuInfo) {// 保存该商品行的视图,以便删除商品时一块从列表移除该行mContextView = v// 从menu_goods.xml中构建菜单界面布局menuInflater.inflate(R.menu.menu_goods, menu)}override fun onContextItemSelected(item: MenuItem): Boolean {val info: CartInfo? = mCartGoods[mContextView!!.id]val id = item.itemIdif (id == R.id.menu_detail) { // 点击了菜单项“查看商品详情”// 跳转到查看商品详情页面goDetail(info!!.goods_id)} else if (id == R.id.menu_delete) { // 点击了菜单项“从购物车删除”val goods_id: Long = info!!.goods_id// 从购物车删除商品的数据库操作mCartHelper!!.delete("goods_id=$goods_id")// 从购物车列表中删除该商品行ll_cart!!.removeView(mContextView)// 更新购物车中的商品数量var left_count: Int = mCount - info!!.countfor (i in mCartArray!!.indices) {if (goods_id == mCartArray!![i].goods_id) {left_count = mCount - mCartArray!![i].countmCartArray!!.removeAt(i)break}}// 把最新的商品数量写入共享参数SharedUtil.Companion.getIntance(this)!!.writeShared("count", "" + left_count)// 显示最新的商品数量showCount(left_count)Toast.makeText(this, "已从购物车删除" + mGoodsMap[goods_id]!!.name, Toast.LENGTH_SHORT).show()mGoodsMap.remove(goods_id)refreshTotalPrice()}return true}// 跳转到商品详情页面private fun goDetail(rowid: Long) {val intent = Intent(this, ShoppingDetailActivity::class.java)intent.putExtra("goods_id", rowid)startActivity(intent)}override fun onResume() {super.onResume()// 获取共享参数保存的购物车中的商品数量mCount = SharedUtil.Companion.getIntance(this)!!.readShared("count", "0")!!.toInt()showCount(mCount)// 获取商品数据库的帮助器对象mGoodsHelper = GoodsDBHelper.Companion.getInstance(this, 1)// 打开商品数据库的写连接mGoodsHelper!!.openWriteLink()// 获取购物车数据库的帮助器对象mCartHelper = CartDBHelper.Companion.getInstance(this, 1)// 打开购物车数据库的写连接mCartHelper!!.openWriteLink()// 模拟从网络下载商品图片downloadGoods()// 展示购物车中的商品列表showCart()}override fun onPause() {super.onPause()// 关闭商品数据库的数据库连接mGoodsHelper!!.closeLink()// 关闭购物车数据库的数据库连接mCartHelper!!.closeLink()}// 声明一个起始的视图编号private val mBeginViewId = 0x7F24FFF0// 声明一个购物车中的商品信息队列private var mCartArray: ArrayList<CartInfo>? = ArrayList<CartInfo>()// 声明一个根据商品编号查找商品信息的映射private val mGoodsMap: HashMap<Long, GoodsInfo?> = HashMap<Long, GoodsInfo?>()// 展示购物车中的商品列表private fun showCart() {// 查询购物车数据库中所有的商品记录mCartArray = mCartHelper!!.query("1=1")Log.d(TAG, "mCartArray.size()=" + mCartArray!!.size)if (mCartArray == null || mCartArray!!.size <= 0) {return}// 移除线性视图ll_cart下面的所有子视图ll_cart.removeAllViews()// 创建一个标题行的线性视图ll_rowvar ll_row = newLinearLayout(LinearLayout.HORIZONTAL, ViewGroup.LayoutParams.WRAP_CONTENT)ll_row.addView(newTextView(0, 2f, Gravity.CENTER, "图片", Color.BLACK, 15))ll_row.addView(newTextView(0, 3f, Gravity.CENTER, "名称", Color.BLACK, 15))ll_row.addView(newTextView(0, 1f, Gravity.CENTER, "数量", Color.BLACK, 15))ll_row.addView(newTextView(0, 1f, Gravity.CENTER, "单价", Color.BLACK, 15))ll_row.addView(newTextView(0, 1f, Gravity.CENTER, "总价", Color.BLACK, 15))// 把标题行添加到购物车列表ll_cart.addView(ll_row)for (i in mCartArray!!.indices) {val info: CartInfo = mCartArray!![i]// 根据商品编号查询商品数据库中的商品记录val goods: GoodsInfo? = mGoodsHelper!!.queryById(info.goods_id)Log.d(TAG, "name=" + goods!!.name + ",price=" + goods.price + ",desc=" + goods.desc)mGoodsMap[info.goods_id] = goods// 创建该商品行的水平线性视图,从左到右依次为商品小图、商品名称与描述、商品数量、商品单价、商品总价。ll_row = newLinearLayout(LinearLayout.HORIZONTAL, ViewGroup.LayoutParams.WRAP_CONTENT)// 设置该线性视图的编号ll_row.id = mBeginViewId + i// 添加商品小图val iv_thumb = ImageView(this)val iv_params = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 2F)iv_thumb.layoutParams = iv_paramsiv_thumb.scaleType = ScaleType.FIT_CENTERiv_thumb.setImageBitmap(MainApplication.instance?.mIconMap?.get(info.goods_id))ll_row.addView(iv_thumb)// 添加商品名称与描述val ll_name = LinearLayout(this)val params = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 3F)ll_name.layoutParams = paramsll_name.orientation = LinearLayout.VERTICALll_name.addView(newTextView(-3, 1f, Gravity.LEFT, goods.name, Color.BLACK, 17))ll_name.addView(newTextView(-3, 1f, Gravity.LEFT, goods.desc, Color.GRAY, 12))ll_row.addView(ll_name)// 添加商品数量、单价和总价ll_row.addView(newTextView(1, 1f, Gravity.CENTER, "" + info.count, Color.BLACK, 17))ll_row.addView(newTextView(1,1f,Gravity.RIGHT,"" + goods.price.toInt(),Color.BLACK,15))ll_row.addView(newTextView(1,1f,Gravity.RIGHT,"" + (info.count * goods.price).toInt(),Color.RED,17))// 给商品行添加点击事件ll_row.setOnClickListener { goDetail(info.goods_id) }// 给商品行注册上下文菜单,为防止重复注册,这里先注销再注册unregisterForContextMenu(ll_row)registerForContextMenu(ll_row)mCartGoods[ll_row.id] = info// 往购物车列表添加该商品行ll_cart.addView(ll_row)}// 重新计算购物车中的商品总金额refreshTotalPrice()}// 重新计算购物车中的商品总金额private fun refreshTotalPrice() {var total_price = 0for (info in mCartArray!!) {val goods: GoodsInfo? = mGoodsMap[info.goods_id]total_price += (goods!!.price * info.count).toInt()}tv_total_price.text = "" + total_price}// 创建一个线性视图的框架private fun newLinearLayout(orientation: Int, height: Int): LinearLayout {val ll_new = LinearLayout(this)val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height)ll_new.layoutParams = paramsll_new.orientation = orientationll_new.setBackgroundColor(Color.WHITE)return ll_new}// 创建一个文本视图的模板private fun newTextView(height: Int,weight: Float,gravity: Int,text: String,textColor: Int,textSize: Int): TextView {val tv_new = TextView(this)if (height == -3) {  // 垂直排列val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, weight)tv_new.layoutParams = params} else {  // 水平排列val params = LinearLayout.LayoutParams(0,if (height == 0) ViewGroup.LayoutParams.WRAP_CONTENT else ViewGroup.LayoutParams.MATCH_PARENT,weight)tv_new.layoutParams = params}tv_new.text = texttv_new.setTextColor(textColor)tv_new.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat())tv_new.gravity = Gravity.CENTER or gravityreturn tv_new}private var mFirst: String? = "true" // 是否首次打开// 模拟网络数据,初始化数据库中的商品信息private fun downloadGoods() {// 获取共享参数保存的是否首次打开参数mFirst = SharedUtil.Companion.getIntance(this)!!.readShared("first", "true")// 获取当前App的私有存储路径val path: String = MainApplication.instance?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString().toString() + "/"if (mFirst == "true") { // 如果是首次打开val goodsList: ArrayList<GoodsInfo> = GoodsInfo.defaultListfor (i in goodsList.indices) {val info: GoodsInfo = goodsList[i]// 往商品数据库插入一条该商品的记录val rowid: Long = mGoodsHelper!!.insert(info)info.rowid = rowid// 往全局内存写入商品小图val thumb = BitmapFactory.decodeResource(resources, info.thumb)MainApplication.instance?.mIconMap?.put(rowid, thumb)val thumb_path = path + rowid + "_s.jpg"FileUtil.saveImage(thumb_path, thumb)info.thumb_path = thumb_path// 往SD卡保存商品大图val pic = BitmapFactory.decodeResource(resources, info.pic)val pic_path = "$path$rowid.jpg"FileUtil.saveImage(pic_path, pic)pic.recycle()info.pic_path = pic_path// 更新商品数据库中该商品记录的图片路径mGoodsHelper!!.update(info)}} else { // 不是首次打开// 查询商品数据库中所有商品记录val goodsArray: ArrayList<GoodsInfo> = mGoodsHelper!!.query("1=1")for (i in goodsArray.indices) {val info: GoodsInfo = goodsArray[i]// 从指定路径读取图片文件的位图数据val thumb = BitmapFactory.decodeFile(info.thumb_path)// 把该位图对象保存到应用实例的全局变量中MainApplication.instance?.mIconMap?.put(info.rowid, thumb)}}// 把是否首次打开写入共享参数SharedUtil.Companion.getIntance(this)!!.writeShared("first", "false")}companion object {private const val TAG = "ShoppingCartActivity"}
}

实验结果(实验最终作品截图说明)



实验心得

1、熟悉Android五种主要存储方式的用法,包括共享参数SharedPreferences、数据库SQLite、SD卡文件、App的全局内存;
2、熟悉重要组件之一的应用Application的基本概念与常见用法,以及四大组件之一的内容提供器ContentProvider的基本概念与常见用法;

参考文章

《移动项目实践》实验报告——Android数据存储相关推荐

  1. Android数据存储安全实践

    0x00 数据安全 Android操作系统自问世以来凭借其开放性和易用性成为当前智能手机的主流操作系统之一,作为与人们关系最密切的智能设备,越来越多的通讯录.短信.视频等隐私数据以明文的方式保存在手机 ...

  2. 《Python程序设计》实验四 Python综合实践实验报告

    <Python程序设计>实验四 Python综合实践实验报告 1.实验内容 Python综合应用:爬虫.数据处理.可视化.机器学习.神经网络.游戏.网络安全等. 在华为ECS服务器(Ope ...

  3. Android 数据存储 利用SQLiteDatabase实现简单的学生管理

    转载请注明出处:明桑Android 这是作为上一篇Android 数据存储 如何搞定SQLite Database的实例练习,之所以单独列出来是因为除了数据库方面的知识,还涉及其它方面的知识,所以就写 ...

  4. Android数据存储——2.文件存储_B_资源文件

    今天学习Android数据存储--文件存储_资源文件 把资源文件mybook.txt放入项目目录下的res资源文件夹下的raw文件夹下(没有则新建),PS:mybook.txt存为UTF-8编码. X ...

  5. Android数据存储之GreenDao 3.0 详解

    前言: 今天一大早收到GreenDao 3.0 正式发布的消息,自从2014年接触GreenDao至今,项目中一直使用GreenDao框架处理数据库操作,本人使用数据库路线 Sqlite----> ...

  6. 20155220 实验三 敏捷开发与XP实践 实验报告

    20155220 实验三 敏捷开发与XP实践 实验报告 实验内容 XP基础 XP核心实践 相关工具 实验要求 没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim ...

  7. 20155226 实验三 敏捷开发与XP实践 实验报告

    20155226 实验三 敏捷开发与XP实践 实验报告 实验内容 XP基础 XP核心实践 相关工具 实验要求 没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim ...

  8. 计算机数据表示实验报告,实验报告二数据的表示

    <实验报告二数据的表示>由会员分享,可在线阅读,更多相关<实验报告二数据的表示(4页珍藏版)>请在人人文库网上搜索. 1.计算机系统基础实验报告学院 信电学院 专业 计算机科学 ...

  9. 软件测试与维护实验报告,软件测试技术与实践实验报告

    软件测试技术与实践实验报告 (11页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.90 积分 北华大学计算机科学技术学院实 验 报 告课程名称 软件测 ...

最新文章

  1. iis7下站点日志默认位置
  2. 机器人建图、感知和交互的语义研究综述
  3. onethink后台编辑器 提示:未检测到兼容版本的flash
  4. 公钥和私钥的简单通俗说明
  5. Spring事务的实现方式和实现原理
  6. java安全编码指南之:文件IO操作
  7. python2d 平滑插值处理_python中平滑的、通用的2D线性插值
  8. 【Swin Transformer Block】的整体流程如下:
  9. 最实用的APP界面设计知识,有温度的APP设计(转)
  10. 去除dataframe中的空行_Python数据分析中的处理与数值加速技巧简介
  11. 大数据之-Hadoop3.x_MapReduce_排序概述---大数据之hadoop3.x工作笔记0114
  12. 冒泡排序的C语言实现
  13. C语言排序的几种算法
  14. c 语言查询条件并列怎么用,关于C# if语句中并列条件的执行
  15. Pandas RuntimeWarning: More than 20 figures have been opened. Figures created plt.close()也不起作用
  16. android根目录无权访问,关于android:已注册为Git根目录,但未在其中找到任何Git存储库...
  17. Exception】Chrome浏览器提示:此网页正试图从未经验证的来源加载脚本
  18. 2022 极术通讯-安谋科技“星辰”STAR-MC2处理器初探
  19. 字节后端开发工程师-基础架构 二面面经
  20. 《前端》JavaScript总结

热门文章

  1. Android 系统构架(转)
  2. linux 内核日志等级,Linux系统中日志级别详情
  3. 掩膜区域内像素值_基于颜色空间采样的抠图算法
  4. linux c语言 readline,Linux C代码实现读取配置文件示例
  5. 华为云设计语言_《好设计,有方法:我们在搜狐做产品体验设计》 —2.2 设计语言带来的好处...
  6. 异步和同步区别是什么_一次相亲经历,我彻底搞懂了什么叫阻塞非阻塞,同步异步...
  7. python语言的变量随时_Python参数注解
  8. 九十六、双指针和滑动窗口算法模板
  9. 六十八、SpringBoot连接MongoDB操作
  10. 三十五、Scrapy 中的杂知识总结和代理池的编写