目录
前言
1 实验题目
2 实验目的
3 实验内容
3.1 要求
3.2 模块介绍
3.3 设计步骤或关键代码
3.3.1 SQlite数据库
3.2.2 创建店铺和商品的json文件,以及对应的Service解析类
3.2.3 读取并解析shop.json文件
3.2.4 根据shop.json文件中的brandStrId读取对应raw文件夹下的商品.json文件
3.2.5 ListView控件
4 实验结果与分析
5 代码
5.1 Android代码
5.2 Idea服务器代码
前言
本实验为Android课程设计内容,最初的原型是书上的给宠物购买装备的项目,因为那里面有页面跳转的实现,后面因为有一个仿美团外卖大作业的需求,就按照老师给的ppt里面的界面一点一点的自己写布局文件,尽量搞的像了一点,再后来就是到了Android课设了,需要更多的功能以及登陆注册就把书上一个注册登录和Android应用市场的项目也整合进来了,同时,新增了结算和历史订单查询的界面。该博客的许多内容为部分报告内容,仅为大家提供参考,请勿直接抄袭。另外,本次实验所用平台是Android Studio 2022.3.1版本,服务器端是Idea,因为我之前写mvc结构感觉有点麻烦,暑假就学了点springboot,所以本次服务器采用的是springboot框架
最开始的本版用户和订单数据是存放在文件里面的,因为我写的慢,听说别人搞了本地SQLite,我写了SQLite存储的版本,后面又听说别人搞了与本地服务器交互的存储在mysql的,所以我最后是打算搞一个mysql和SQLite同步的,但是由于时间问题,服务器写完了,但是Android这边没写完,只写了User数据同步的,其他的订单和商店信息并没有实现,有能力的同学可以尝试写完整,并且,我就是只做了要求的,而且还没全部实现,没有自己加功能,希望大家能搞定的,尽量自己想写新功能,比如商店管理,菜品管理等一些功能,尽量搞点创意,这样才能拿个好成绩
在运行项目时,必须先把后端服务器先启动,因为我在Login和Register两个Activity里面是写了和服务器同步的,如果不想搞复杂,可以找到两个Activity里面的和服务器相关的代码注释掉,这部分我基本上注释掉了,还是比较简单理解的
1 实验题目
网上商城/外卖小助手
2 实验目的
(1)掌握 Android 中的菜单及导航框架。
(2)掌握自定义布局。
(3)掌握 Android 中的数据存储。
3 实验内容
3.1 要求
(1)要求实现商品展示、商品详细介绍、下订单、购物车。
(2)要求实现用户注册、登录、查看历时订单。
(3)数据:可以采用静态的固定的数据来模拟(如果动手能力较强,可以尝试自己动手搭后台,利用 Android 网络编程)。
3.2 模块介绍
(1)注册模块:注册用户。
(2)登录模块:用户登录平台。
(3)店铺浏览模块:查看平台已有店铺,注意是提前卸载文件里面的,时间问题没有做到SQLite存储,大家可以自己尝试,比较简单。
(4)个人中心模块:在店铺浏览模块点击个人中心的图标后会进入,可以充值和查看历史订单。
(5)点单模块:点击进入店铺后可以下单。
(6)结算模块:点击结算之后,进入结算界面。
3.3 设计步骤或关键代码
在这个部分,我主要讲一些我认为比较重要的地方
3.3.1 SQlite数据库
(1)设计一个数据库操作类MyHelper,然后利用该类创建网上商城/外卖小助手的Android Studio自带的SQLite数据库takeaway1.db,利用MySQL的可视化工具Navicat创建网上商城/外卖小助手后端服务器的需要的数据库infodb。然后分别在两个数据库中创建相应的表项,如用户表user和历史订单表historical_order等。
(2)代码主要是在这里面主要是需要让MyHelper类继承SQLiteOpenHelper类,然后重写相应方法即可,可以参考书上SQLite数据库部分
package com.wx.mytakeout.utils; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class MyHelper extends SQLiteOpenHelper { private static final String Tag = "MyHelper"; public MyHelper(Context context){ super(context,"takeaway1.db",null,1); Log.i(Tag,"创建数据库 takeaway1"); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table user(_id integer primary key autoincrement," + "username varchar(20),password varchar(20),money int)"); db.execSQL("create table historical_order(_id integer primary key autoincrement," + "orderId int,username varchar(20),userTel varchar(11),userAddress varchar(20)," + "goodsId integer,goodsName varchar(30),goodsCount int,goodsPrice int,deliveryCost int, orderPrice int)"); db.execSQL("CREATE TABLE shop (id integer primary key autoincrement," + " brandStrId varchar(10) ,brandName varchar(20) ," + " brandSales varchar(20) ,startDeliveryCost int ," + " deliveryCost int , brandEvaluation varchar(30) ," + " deliveryTime int ,announcement varchar(30) )"); db.execSQL("CREATE TABLE goods (id integer primary key autoincrement," + " brandStrId varchar(10) ,goodsStrId varchar(10) ," + " goodsName varchar(30) ,goodsSales varchar(20) ," + " goodsFavourableComment varchar(20) , goodsEvaluation varchar(20) ," + " goodsPrice int )"); Log.i(Tag,"创建表 user 和 historical_order"); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { } }
3.2.2 创建店铺和商品的json文件,以及对应的Service解析类
(1)创建对应店铺或者商品的json文件,只给出shop.json和mxbc.json,在后面会全部上传可以自行下载
1)shop.json
[ {"brandStrId":"mxbc", "brandName":"蜜雪冰城","brandSales":"月销2023","startDeliveryCost":15,"deliveryCost":5,"brandEvaluation":"性价比最高,没有之一","deliveryTime":30,"announcement":"价格实惠"}, {"brandStrId":"tst", "brandName":"塔斯汀","brandSales":"月销1023","startDeliveryCost":25,"deliveryCost":2,"brandEvaluation":"中国汉堡","deliveryTime":45,"announcement":"周四疯狂日"}, {"brandStrId":"yht", "brandName":"益禾堂","brandSales":"月销1500","startDeliveryCost":20,"deliveryCost":4,"brandEvaluation":"广外大街饮品任期第3名","deliveryTime":35,"announcement":"奶茶味道不错"}, {"brandStrId":"rxkf", "brandName":"瑞幸咖啡","brandSales":"月销1333","startDeliveryCost":22,"deliveryCost":3,"brandEvaluation":"酱香拿铁,你值得拥有","deliveryTime":30,"announcement":"全场8折"}, {"brandStrId":"bxlxd", "brandName":"百香林西点","brandSales":"月销1533","startDeliveryCost":12,"deliveryCost":2,"brandEvaluation":"蛋糕很好吃,奶油新鲜不腻,水果新鲜","deliveryTime":45,"announcement":"可定制蛋糕"} ]
2)mxb.json
[ {"goodsStrId":"bxnms", "goodsName":"冰鲜柠檬水","goodsSales":"月销336","goodsFavourableComment":"好评度100%","goodsEvaluation":"门店销量第1","goodsPrice":14}, {"goodsStrId":"mtsjc", "goodsName":"蜜桃四季春(升级版)","goodsSales":"月销259","goodsFavourableComment":"好评度86%","goodsEvaluation":"门店销量第2","goodsPrice":17}, {"goodsStrId":"mbbxg", "goodsName":"满杯百香果","goodsSales":"月销228","goodsFavourableComment":"好评度96%","goodsEvaluation":"门店销量第3","goodsPrice":20}, {"goodsStrId":"cmbb", "goodsName":"草莓啵啵","goodsSales":"月销236","goodsFavourableComment":"好评度90%","goodsEvaluation":"门店销量第4","goodsPrice":18} ]
(2)代码
1)解析shop.json的ShopService
package com.wx.mytakeout.service; import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.wx.mytakeout.pojo.ShopInfo; import java.io.InputStream; import java.lang.reflect.Type; import java.util.List; public class ShopService { private static final String Tag = "ShopService"; //获取JSON文件返回shop信息集合 public static List getShopInfosFromJSON(InputStream is) throws Exception { byte[] buffer = new byte[is.available()]; is.read(buffer); String json = new String(buffer,"utf-8"); Log.i(Tag,"shopsJson:"+json); //使用Gson库解析JSON数据 Gson gson = new Gson(); Type listType = new TypeToken(){}.getType(); List shopInfos = gson.fromJson(json,listType); return shopInfos; } //将 string 转为 ShopInfo 类 public static ShopInfo getShopInfoFromStr(String str) throws Exception { Log.i(Tag,"ShopInfoStr:"+str); //使用Gson库解析JSON数据 Gson gson = new Gson(); Type strType = new TypeToken(){}.getType(); ShopInfo shopInfo = gson.fromJson(str,strType); return shopInfo; } }
2)解析mxbc.json的GoodsService
package com.wx.mytakeout.service; import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.wx.mytakeout.pojo.GoodsInfo; import com.wx.mytakeout.pojo.ShopInfo; import java.io.InputStream; import java.lang.reflect.Type; import java.util.List; public class GoodsService { private static final String Tag = "GoodsService"; //获取JSON文件返回goods信息集合 public static List getGoodsInfosFromJSON(InputStream is) throws Exception { byte[] buffer = new byte[is.available()]; is.read(buffer); String json = new String(buffer,"utf-8"); Log.i(Tag,"goodsJson:"+json); //使用Gson库解析JSON数据 Gson gson = new Gson(); Type listType = new TypeToken(){}.getType(); List GoodsInfos = gson.fromJson(json,listType); return GoodsInfos; } //将 string 转为 GoodsInfo 数组 public static List getGoodsInfosFromStr(String str) throws Exception { Log.i(Tag,"GoodsInfoStr:"+str); //使用Gson库解析JSON数据 Gson gson = new Gson(); Type listType = new TypeToken(){}.getType(); List GoodsInfos = gson.fromJson(str,listType); return GoodsInfos; } }
3.2.3 读取并解析shop.json文件
读取shop.json文件,并利用对应json解析函数就行数据解析
1)在onCreate函数
//读取 shop.json 文件 InputStream is = this.getResources().openRawResource(R.raw.shop); //把每个商店的信息集合存到 shopInfos 中 shopInfos = ShopService.getShopInfosFromJSON(is); for (ShopInfo shopInfo : shopInfos) { //Log.i(Tag, "before shopInfo:" + shopInfo); shopInfo.setBrandId(getDrawableId(shopInfo.getBrandStrId())); Log.i(Tag, "after shopInfo:" + shopInfo); }
2)getDrawableId函数:根据shop.json文件中的brandStrId获取对应drawable文件夹下的店铺图片资源
/** * 通过 sId 获得对应的 R.drawable 对应的资源 */ public Integer getDrawableId(String sId){ Integer id = null; try { Field field = R.drawable.class.getField(sId); id =field.getInt(field.getName()); //Log.i(Tag, "sId:"+sId+",id:" + id); } catch (Exception e) { Log.i(Tag, "解析id失败,sId:"+sId); e.printStackTrace(); }finally { return id; } }
3.2.4 根据shop.json文件中的brandStrId读取对应raw文件夹下的商品.json文件
(1)在3.2.2中的shop.json文件中存储了每个店铺对应的商品存放文件名或图片资源名brandStrId,该部分主要是根据brandStrId在raw文件夹中找到相应的文件,并读取里面的数据,同时利用相应的json解析函数解析数据
(2)代码
1)在onCreate函数中,其中的getDrawableId函数是利用某一商品.json文件,如mxbc.json文件中的goodsStrId,获取在drawable文件夹下的图片资源。
Integer rawId = getRawId(shopInfo.getBrandStrId()); Log.i(Tag, "rawId:" + rawId); InputStream is = this.getResources().openRawResource(rawId); //把每个商品的信息集合存到 goodsInfos 中 goodsInfos = GoodsService.getGoodsInfosFromJSON(is); for (GoodsInfo goodsInfo : goodsInfos) { //Log.i(Tag, "before shopInfo:" + shopInfo); goodsInfo.setGoodsId(getDrawableId(goodsInfo.getGoodsStrId())); Log.i(Tag, "after goodsInfo:" + goodsInfo); }
2)getRawId函数
/** * 通过 brandStrId 获得对应的 R.raw 对应的资源 */ public Integer getRawId(String brandName) { Integer id = null; try { Field field = R.raw.class.getField(brandName); id = field.getInt(field.getName()); //Log.i(Tag, "sId:"+sId+",id:" + id); } catch (Exception e) { Log.i(Tag, "解析id失败,brandName:" + brandName); e.printStackTrace(); } finally { return id; } }
3.2.5 ListView控件
(1)布局文件和效果图
1)ListView对应的配置文件
2)ListView对应的效果图
3)ListView条目对应的布局文件
4)条目对应的效果图
(2)数据适配器
1)在onCreate函数中
//初始化 ListView 控件 shopListView = (ListView) findViewById(R.id.lv_brand); //创建一个 Adapter 的实例 MyBaseAdapter myBaseAdapter = new MyBaseAdapter(); //设置 Adapter shopListView.setAdapter(myBaseAdapter); //设置 ListView 监听事件,注意是 setOnItemClickListener 不是 setOnClickListener shopListView.setOnItemClickListener(this);
2)适配器MyBaseAdapter 继承 BaseAdapter
/** * 初始化 shop 列表 */ class MyBaseAdapter extends BaseAdapter { //得到 item 总数 @Override public int getCount() { //Log.i(Tag, "当前view数量:"+String.valueOf(shopInfos.size())); return shopInfos.size(); } //得到 item 对象 @Override public Object getItem(int position) { Log.i(Tag, "当前 shopListViewItem 对象:"+shopInfos.get(position)); return shopInfos.get(position); } //得到 item 的 id @Override public long getItemId(int position) { Log.i(Tag, "当前 shopListViewItem 的 id:"+position); return position; } //得到 item 的 View 视图 /*@Override public View getView(int position, View convertView, ViewGroup parent) { //将list_item.xml文件找出来并转换为View对象 View view = View.inflate(MainActivity.this,R.layout.list_item,null); TextView mTextView = (TextView) view.findViewById(R.id.item_tv); mTextView.setText(names[position]); ImageView imageView = (ImageView) view.findViewById(R.id.item_image); imageView.setBackgroundResource(icons[position]); return view; }*/ @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; //将 shop_list_item.xml 文件找出来并转换为 View 对象 if (convertView == null) { convertView = LayoutInflater.from( getApplicationContext()).inflate(R.layout.shop_list_item, parent, false); holder = new ViewHolder(); holder.mTextView1 = (TextView) convertView.findViewById(R.id.item_tv_brand_name); holder.mTextView2 = (TextView) convertView.findViewById(R.id.item_tv_brand_sales); holder.mTextView3 = (TextView) convertView.findViewById(R.id.item_tv_start_delivery_cost); holder.mTextView4 = (TextView) convertView.findViewById(R.id.item_tv_delivery_cost); holder.mTextView5 = (TextView) convertView.findViewById(R.id.item_tv_brand_evaluation); holder.mTextView6 = (TextView) convertView.findViewById(R.id.item_tv_delivery_time); holder.imageView = (ImageView) convertView.findViewById(R.id.item_image_brand); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.mTextView1.setText(shopInfos.get(position).getBrandName()); holder.mTextView2.setText(shopInfos.get(position).getBrandSales()); holder.mTextView3.setText("起送¥"+shopInfos.get(position).getStartDeliveryCost()); holder.mTextView4.setText("|配送¥"+shopInfos.get(position).getDeliveryCost()); holder.mTextView5.setText(shopInfos.get(position).getBrandEvaluation()); holder.mTextView6.setText("配送约"+shopInfos.get(position).getDeliveryTime()+"分钟"); holder.imageView.setBackgroundResource(shopInfos.get(position).getBrandId()); return convertView; } class ViewHolder { TextView mTextView1; TextView mTextView2; TextView mTextView3; TextView mTextView4; TextView mTextView5; TextView mTextView6; ImageView imageView; } }
3)条目点击事件监听函数onItemClick,因为要判断是哪个店铺被点击从而进入对应的店铺查看其所有商品,并且在onCreate函数中已经设置了监听事件,后面的在某个店铺点击某个商品是类似的实现,只需要在给每个条目里面的按钮设置点击事件就可以了,具体不给出,但是会把所有资源上传,需要的自行下载
@Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { Toast.makeText(MainActivity.this,"第"+position+"个 shopListViewItem 被点击了,"+"id:"+id,Toast.LENGTH_SHORT).show(); Log.i(Tag,"当前被点击的 shopListViewItem 对象:"+shopInfos.get(position)); //创建Intent对象,TakeawayActivity Intent intent = new Intent(this, TakeawayActivity.class); intent.putExtra("shopInfo", String.valueOf(shopInfos.get(position))); intent.putExtra("userInfo", userInfo); startActivityForResult(intent,1); intent=null; Log.i(Tag,"clearIntentInMainActivityOnItemClick"); }
4 实验结果与分析
所有已实现的功能展示均在这个视频里面
Android课设-网上商城/外卖小助手
5 代码
5.1 Android代码
安卓-Android Studio-仿美团外卖Android全部资源
5.2 Idea服务器代码
服务器-Spring Boot-仿美团外卖服务器全部资源