前言
上一篇博客主要介绍了本次网上商城的实现过程中的一些用法,这篇博客主要就是说明每个功能模式的实现方法
Android学习之网上商城(上)
Android学习之网上商城(下)
源码下载:
链接:https://pan.baidu.com/s/1Z17xBHV9iq70LwdgXxwDIQ
提取码:Lin2
本博客内容原创,创作不易,转载请注明
本文链接
个人博客:https://ronglin.fun/?p=120
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/121876819
开发环境:
- Android Studio版本:(Android Studio Arctic Fox 2020.3.1 Patch 3)
- SDK版本:(Android 7.0 API24 Revision 2)
- Gradle版本:(7.0.2)
- Android Gradle Plugin版本:(7.0.3)
文件布局如下:
; 数据结构
Goods
对于每一个商品类,提供以下的成员变量
private ArrayList<bitmap> bitmaps;
private String goodsName;
private String describe;
private ArrayList<string> tags;
private int price;
public static ArrayList<goods> GOODSLIST =new ArrayList<>();
public static Goods DEULT_GOODS = new Goods();
</goods></string></bitmap>
简单介绍一下, price
变量是一个以分为单位的数,比如10000就是100.00元,这样就不会涉及到浮点数的精度问题, bitmaps
是一个图片的合集,用来保存商品的一些预览图,其他就见名知意了。然后 DEULT_GOODS
默认商品类就是一个有内存但是没有数据的类
构造方法如下:
public Goods(){
this.bitmaps = new ArrayList<>();
this.goodsName = "";
this.describe = "";
this.tags = new ArrayList<>();
this.price = -1;
}
public Goods(String goodsName, String describe, String[] tags, int price){...}
除了成员变量的getter方法和setter方法还有以下方法:
public boolean equals(Goods goods){
return this.getGoodsName().equals(goods.getGoodsName())
&& this.getTags().equals(goods.getTags())
&& this.getDescribe().equals(goods.getDescribe())
&& this.getPrice() == goods.getPrice();
}
public static String toPriceString(int price){
StringBuffer sb = new StringBuffer(String.valueOf(price));
//小于2位数
if (sb.length()<=2) { if (sb.length()="=" 0){ sb.insert(0,"0.00"); } else if(sb.length()="=" 1){ sb.insert(0,"0.0"); 2){ sb.insert(0,"0."); sb.insert(sb.length()-2,'.'); return sb.tostring(); public static void getdefaultgoodslist(context context) < code></=2)>
equals
方法就是判断两个Goods是否相等.
toPriceString
是因为 price
是以分为单位的数据,要把它变成以元为单位的,例如:
10000 -> 100.00;
0 -> 0.00;
10 -> 0.10;
getDefaultGoodsList
就是用来初始化商品列表的
Person
person类是一个封装用户信息的一个类
成员变量如下:
private String id;
private int num;
private String username;
private String password;
private int money =0 ;
构造方法如下
public Person(String name, String password){
this.username = name;
this.password = password;
}
需要注意的是,这个类中并没有对person的id和num进行初始化,所以说当要插入数据库时,要先在数据库中获取id和num值,对于这两个值的意义在数据库设计中会有说明.
除了成员变量的getter方法和setter方法还有以下方法:
public String toString(){
String t;
t ="{id:%s,num:%s,username:%s,password:%s,money:%s}";
t =String.format(t,this.id,this.num,this.username,this.password,this.money);
return t;
}
主要就是将一个person的数据输出
数据库
表格设计
数据库的设计是核心,因为采用本地数据存储,所有数据要保存在本地数据库中,接下来简单说明一下本次数据库设计
本次数据库一共新建了两个表格,如下
sqLiteDatabase.execSQL("create table person(id varchar(10) primary key,num integer,username varchar(30),password varchar(30),money integer)");
sqLiteDatabase.execSQL("create table shopping(id varchar(10),goodsName varchar(30),time bigint)");
第一个表格是person表,元组有id(主键),num、username、password,四个,id和num是一样的,但是数据类型不一样,主要是为了区分用户名相同的用户,num表示当前第多少个用户,例如是第一个注册的,那么id = num =1,第二个注册的就是id = num =2,以此类推。
第二个表格是shopping表,用来保存用户的历史订单的,元组有id、goodsName、time,其中id就是person表格中的id,goodsName是商品名称,time则是用户购买商品的时间戳,采用的是java的时间戳格式,是一个long类型的相对于1970年开始的毫秒值。因为一个用户可能购买多个同一商品,所以说要加一个time用来区分。
代码设计
主要设计了以下方法
public class Database {
private MySQLiteHelper mySQLiteHelper;
private SQLiteDatabase database;
public Database(MySQLiteHelper mySQLiteHelper){this.mySQLiteHelper = mySQLiteHelper;}
//将person插入数据库
public void insertPersonToSQLite(Person person){...}
//获取当前数据库中的最大Num
public int getPersonMaxNumFromSQLite(){...}
//根据id更新数据
public int updatePersonToSQLite(Person person){...}
//删除person
public int deletePersonToSQLite(Person person){...}
//此方法用来查询数据库中是否存在person
//优先查询id,id==null,查询username
//password为空时返回第一个username的信息 不为空时匹配用户名和密码
//当查询不到时返回null
public Person findPersonFromSQLite(String id,String username,String password){...}
//插入购物清单
public void insertGoodsToSQLite(Person person,Goods goods,long time){...}
//删除购物项
public int deleteGoodsToSQLite(Person person,Goods goods){...}
//查person的所有购物项
public ArrayList<string> findGoodsListFromSQLite(Person person){...}
}
</string>
注意要想插入person的时候,要先调用 getPersonMaxNumFromSQLite
方法获取MaxNum来设置person的id和num数据
注册登录
登录界面
效果如图:
这个界面主要是2个EditText和2个Button,注册按钮的监听方法就是点击后会跳转到注册的Activity,调用的是
startActivityForResult();
方法,因为需要注册界面把注册用户的信息返回回来,然后在 onActivityResult()
方法中将2个EditText的内容填好。登录按钮就是点击之后,先检查用户输入是否合理,例如:是否输入了密码等,如果没输入就提示一个Toast,如果输入合法,就在本地数据库中查询,如果不存在提示Toast,如果登录成功,则把当前的界面中的控件用 setVisibility()
方法隐藏,然后显示登录成功界面。
; 注册界面
登录界面比较简单,是一个单独的Activity,功能主要是,点击取消当前界面
finish()
,如果点击注册核对用户输入是否合法,合法的话,将数据插入数据库,插入之前要先检测一下是否在数据库中存在当前用户,标准是用户名和密码同时对应,如果存在提示一个Toast,不存在则插入数据库,然后将数据通过 setResult()
方法传回 MainActivity
。
登录成功界面
登录成功界面是和登录界面同时存在的,一开始是隐藏起来的一个线性布局,等登录成功之后,从数据库中获取用户的余额和历史清单,然后将数据显示出来,也是比较简单。
因为数据库中存储的是货物的名称,当获取名称之后,还要通过当前的货物List查询出来每一个货物,将String类型的数据转化为Goods类型。
充值按钮就是增加100元给当前账户,同时更新到数据库中,注销就是退出当前账户.但是实际上并没有退出,而是把当前界面隐藏然后显示登录界面.
然后购物清单就是一个从数据库中获取数据的一个ListView
; 商品展示
这是商品展示界面,布局主要是一个顶部栏和一个ListVeiw.还有响应点击会弹出一个有关商品的详细信息的对话框
顶部栏
顶部栏其中有三个控件,从左到右分别是 用户 搜索框 购物车,点击用户按钮则跳转到个人中心的Fragment,用法如下:
Navigation.findNavController(GoodsFragment.this.getView()).navigate(R.id.navigation_person);
搜索框实现了一个实时查询的功能,用到了 TextWatcher
类,关于这个类的用法在前边有详细说明,当用户输入一个字符之后就开始搜索,搜索范围为一个商品的字符成员变量,Tag goodsName,describe代码如下:
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String search = s.toString().trim();
if(search.isEmpty()){
goodsViewModel.setListGoods(list);
} else {
resultList.clear();
for (Goods t:list) {
if(t.getGoodsName().contains(search)){
resultList.add(t);
} else if (t.getTags().toString().contains(search)){
resultList.add(t);
} else if (t.getDescribe().contains(search)){
resultList.add(t);
}
}
goodsViewModel.setListGoods(resultList);
}
}
ListView
listView中的重点就是adapter类的书写,在前面有了详细说明
public class GoodsListAdapter extends BaseAdapter
同时有关点击每一个 item
弹出对话框也是在这个类里调用 setOnClickListener
方法实现.
还有点击”+”将当前货物添加到购物车中也是在 GoodsListAdapter
类中实现
同时还有一个延迟效果,点击”+”之后图标变成 “√” 然后经过1s之后变回”+”,在这一秒内用户无法添加货物到购物车,这延迟的实现方法用的是前面介绍的 单线程实现定时器
商品详细对话框
样式如上
一个自定义的对话框,代码框架如下
public class GoodsDialog extends Dialog implements View.OnClickListener {
private Goods goods;
private Context context;
private DialogGoodsBinding binding;
public GoodsDialog(@NonNull Context context, Goods goods) {
super(context);
this.goods = goods;
this.context = context;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DialogGoodsBinding.inflate(LayoutInflater.from(context));
setContentView(binding.getRoot());
//......
}
@Override
public void onClick(View view) {hide();}
}
就一个主要的数据就是一个Goods类,然后就是实现,点击任何地方隐藏当前对话框
购物车
主要界面如下
主要是设计为ListView加一个底边栏
; ListView
关于这个ListView的 BaseAdapter
类,我是这样定义的
public class ShoppingListAdapter extends BaseAdapter {
private ArrayList<goods> list_goods ;
private static ArrayList<integer> list_select = new ArrayList<>();
private int allPrice = 0;
private final Context context;
private FragmentShoppingBinding binding;
public ShoppingListAdapter(ArrayList<goods> list, Context context, FragmentShoppingBinding binding){
this.list_goods = list;
this.context = context;
this.binding = binding;
}
//......
}
</goods></integer></goods>
这个listView有一个难点就是要标记选中,因此我用了两个ArrayList,一个是当前的商品列表,还有一个是选中的项目,这个选中的项目列表存储的标号index,比如上图, list_select
中存储的就是 [0,1]
两个元素
第二个难点就是要计算出选中的项目的价格,这个是用 allPrice
变量维护的,然后当 list_select
变换的时候,就更新一些爱,然后通过传进来的 FragmentShoppingBinding
引用来显示到底边栏上
底边栏
难点是删除按钮的逻辑,我是这样设计的,现根据 ShoppingListAdapter
类中的 list_select
列表数据,将显示的 list_goods
中的Goods先设置成 DEULT_GOODS
,因为 DEULT_GOODS
是没有意义的,然后再用一个方法将 list_goods
中的所有的 DEULT_GOODS
删除,然后再刷新一次界面,就可以了.
删除功能的结果展示如下:
然后就是全选按钮,全选按钮中的逻辑比较简单,就是修改
ShoppingListAdapter
类中的 list_select
数据,将内容设置为全部,同时每一个item中的单选按钮根据 list_select
更改选中状态,即可
最后就是购买按钮,效果如下:
效果为点击购买,之后弹出一个对话框,对话框的内容为确定付款,显示用户当前所拥有的金额和购买商品所需要的金额,如果用户点击确定,则比较两个金额,用户余额不够的话提示一个Toast,够的话就在购物车中删除对应的item,然后将对应的Goods数据插入数据库中,将相应的金额扣除,更新数据库中的用户数据
; 总结
至此,一个简易的网上商城APP就做完了,难点在于新得框架的应用和一些功能的时间方法,还有界面设计的美观性和应对不同屏幕大小的适配.
总之,本次课设收获良多,但是无奈两周时间赶上3门考试,一边考试一边课设精力有限,界面简陋,功能简单,不过主要以学习为主.经过本次课设,对于Android开发过程有了大概的理解,获益良多,=w=
Original: https://blog.csdn.net/RongLin02/article/details/121876819
Author: 榕林子
Title: Android学习之网上商城(下)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/817151/
转载文章受原作者版权保护。转载请注明原作者出处!