前言:
说到MVP
的时候其实大家都不陌生,但是涉及到实际项目中使用,还是有些无从下手。因此这里小编带着大家一步步地如何用MVP
去搭建购物车模块。
首先还是按照惯例,用一张实现的动态图来说明吧:
看图其实可以看得出来咱们这块的功能主要有:
- 单个店面的选择
- 某个店面下对某个商品的选择
- 对某个店面里某个商品数量的增减
- 最下面的商品全选
- 对选中的商品价格的计算
- 对选中商品进行结算(主要给服务器那边)
分析:
说完了要实现的功能,紧接着就要去分析,咱们的MVP
的架子该如何去分析呢:
首先来看M层,大家都知道M层是数据层,是操作数据的关键层,那咱们这块主要有获取商品、单个商品的增减、单个商品的选中取消、单个店的选中取消、所有商品的全选取消,V层就是显示这层了,这里就定义了几种情况,成功获取到商品、显示修改时显示某个商品、显示错误页面、显示空的页面,P层就是两个层的桥梁了
对view层进行回调显示了。
这里画一张结构图,来说明MVP
的特点:
从图中大家可以看得出来,首先是定义好咋们的IMode
接口,接口里面只有一个获取所有的商品方法:
//需要传入数据的类型,该类承担着从网络或本地获取数据的部分
public interface IMode {
void loadList();
}
紧接着就是咋们的ShopMode
实现类了:
//当前每次需要回调的价格变量
private double price;
private List select_list = new ArrayList<>();//传到结算页面的商品数据
private List allShopCarBean = new ArrayList<>();//传到结算页面的商品数据
//获取的数据源部分,从Asset目录下面的shopcartdata.json获取,获取成功后,将数据交给了回调接口,并且将获取到的数据放到allShopCarBean集合里面
@Override
public void loadList() {
StringBuilder stringBuilder = new StringBuilder();
try {
AssetManager assetManager = context.getAssets();
BufferedReader bf = new BufferedReader(new InputStreamReader(assetManager.open("shopcartdata.json")));
String line;
while ((line = bf.readLine()) != null) {
stringBuilder.append(line);
}
String json = stringBuilder.toString();
Gson gson = new Gson();
List list = gson.fromJson(json, new TypeToken>() {
}.getType());//对于不是类的情况,用这个参数给出
listener.loadSuccess(list);
allShopCarBean.addAll(list);
} catch (Exception e) {
e.printStackTrace();
}
}
//点击了某个店面下的某个商品的数量减少, parent_position:店面的id, child_position:商品的id
public void numberReduce(int parent_position, int child_position) {
ShopCartBean bean = allShopCarBean.get(parent_position);
List goodsList = bean.getGoods();
GoodsBean goodsBean = goodsList.get(child_position);
String goods_num = goodsBean.getGoods_number();
int goodsNum = Integer.parseInt(goods_num);
boolean canReduce = false;
if (goodsNum > 1) {
canReduce = true;
}
//通过id获取相应的商品
GoodsBean selectGoodsBean = goodsNumChange(2, parent_position, child_position);
Log.d(TAG, "goodsBean.number:" + goodsBean.getGoods_number());
if (selectGoodsBean.isCheck() && canReduce) {
//价格需要在前面的基础上减去单个商品的价格,相当于数量减少了一个
price -= Double.parseDouble(selectGoodsBean.getGoods_price());
Log.d(TAG, "price:" + price);
listener.onNumberReduce(price, select_list);
}
}
//商品数量的增减并且返回选中的GoodsBean
private GoodsBean goodsNumChange(int type, int parent_position, int child_position) {
ShopCartBean bean = allShopCarBean.get(parent_position);
List goodsList = bean.getGoods();
GoodsBean goodsBean = goodsList.get(child_position);
String goods_num = goodsBean.getGoods_number();
int goodsNum = Integer.parseInt(goods_num);
if (type == 1) {
goodsNum = goodsNum + 1;
} else {
if (goodsNum > 1) {
goodsNum = goodsNum - 1;
}
}
goodsBean.setGoods_number(String.valueOf(goodsNum));
ShopCartBean selectBean = new ShopCartBean();
//对当前的选中的ShopCartBean进行重新给值
selectBean.clearGoods(bean, select_list);
//如果之前在select_list中存在,移除之前的,将新的放到该集合中
int index = isContainsShopBean(select_list, selectBean);
if (index != -1) {
select_list.remove(index);
}
select_list.add(selectBean);
listener.onNumberChange(parent_position);
return goodsBean;
}
//判断当前的shopCartBean是否在之前选中的集合中
private int isContainsShopBean(List existShopBeanList, ShopCartBean shopCartBean) {
for (int i = 0; i < existShopBeanList.size(); i++) {
ShopCartBean selectBean = existShopBeanList.get(i);
Log.d(TAG, "selectBean.getSupplier_id" + selectBean.getSupplier_id());
Log.d(TAG, "shopCartBean.getSupplier_id" + shopCartBean.getSupplier_id());
if (selectBean.getSupplier_id().equals(shopCartBean.getSupplier_id())) {
return i;
}
}
return -1;
}
//点击了某一个店面,此时就是全选当前店面下的商品或是全消店面下面的商品
public void itemChildClick(int position) {
ShopCartBean bean = allShopCarBean.get(position);
//如果之前存在当前的ShopCartBean则进行移除操作
int index = isContainsShopBean(select_list, bean);
if (index != -1) {
select_list.remove(index);
}
boolean isSelected;
boolean checkAll;
//选中与未选中做取反操作
if (bean.isCheck()) {
isSelected = false;
} else {
isSelected = true;
}
//保存店铺点击状态
bean.setCheck(isSelected);
//通知全选CheckBox的选择状态,看是不是全选的
if (allSelect() == allShopCarBean.size()) {
checkAll = true;
} else {
checkAll = false;
}
//这里如果是选中了某一个店,需要对这个店下面的商品总价格加操作
if (isSelected) {
for (int i = 0; i < bean.getGoods().size(); i++) {
//只有在没选中的情况下才会去修改状态以及总价格
if (!bean.getGoods().get(i).isCheck()) {
bean.getGoods().get(i).setCheck(true);
price += Double.parseDouble(bean.getGoods().get(i).getGoods_number()) * Double.parseDouble(bean.getGoods().get(i).getGoods_price());
}
}
select_list.add(bean);
} else {
// 解决点击取消选择商品时,店铺全选按钮取消选择状态,不会不变成全不选
if (allChildSelect(position) == bean.getGoods().size()) {
for (int i = 0; i < bean.getGoods().size(); i++) {
//只有在选中情况下才会去修改状态以及总价格
if (bean.getGoods().get(i).isCheck()) {
bean.getGoods().get(i).setCheck(false);
price -= Double.parseDouble(bean.getGoods().get(i).getGoods_number()) * Double.parseDouble(bean.getGoods().get(i).getGoods_price());
}
}
select_list.remove(bean);
}
}
listener.onItemChildClick(price, checkAll, select_list, position);
}
//对某一个商品进行选中与未选中
public void childClick(int parent_position, int child_position) {
ShopCartBean bean = allShopCarBean.get(parent_position);
ShopCartBean selectBean = new ShopCartBean();
selectBean.clearGoods(bean, select_list);
List goodsList = bean.getGoods();
GoodsBean goodsBean = goodsList.get(child_position);
boolean isSelected;
boolean checkAll;
if (goodsBean.isCheck()) {
isSelected = false;
price -= Double.parseDouble(goodsBean.getGoods_number()) * Double.parseDouble(goodsBean.getGoods_price());
selectBean.getGoods().remove(goodsBean);
} else {
isSelected = true;
price += Double.parseDouble(goodsBean.getGoods_number()) * Double.parseDouble(goodsBean.getGoods_price());
selectBean.getGoods().add(goodsBean);
}
//保存商品点击状态
goodsBean.setCheck(isSelected);
//通知店铺选择的状态
if (allChildSelect(parent_position) == goodsList.size()) {
bean.setCheck(true);
selectBean.setCheck(true);
} else {
bean.setCheck(false);
selectBean.setCheck(false);
}
int index = isContainsShopBean(select_list, selectBean);
if (index != -1) {
select_list.remove(index);
}
select_list.add(selectBean);
//通知全选CheckBox的选择状态
if (allSelect() == allShopCarBean.size()) {
checkAll = true;
} else {
checkAll = false;
}
listener.onItemChildClick(price, checkAll, select_list, parent_position);
}
//所有的店面下面所有的商品选中的操作
public void selectAll() {
price = 0;
select_list.clear();
for (int i = 0; i < allShopCarBean.size(); i++) {
ShopCartBean shopCartBean = allShopCarBean.get(i);
//选择店铺
if (!shopCartBean.isCheck()) {
shopCartBean.setCheck(true);
}
for (int j = 0; j < shopCartBean.getGoods().size(); j++) {
//选择店铺的商品
if (!shopCartBean.getGoods().get(j).isCheck()) {
shopCartBean.getGoods().get(j).setCheck(true);
Log.d(TAG, "数量:" + shopCartBean.getGoods().get(j).getGoods_number());
}
price += Double.parseDouble(shopCartBean.getGoods().get(j).getGoods_number()) * Double.parseDouble(shopCartBean.getGoods().get(j).getGoods_price());
}
select_list.add(shopCartBean);
}
listener.onSelctAll(price, select_list);
}
//取消全选的操作
public void unSelectAll() {
if (allSelect() == allShopCarBean.size()) {
for (int i = 0; i < allShopCarBean.size(); i++) {
ShopCartBean shopCartBean = allShopCarBean.get(i);
if (shopCartBean.isCheck()) {
shopCartBean.setCheck(false);
}
for (int j = 0; j < shopCartBean.getGoods().size(); j++) {
if (shopCartBean.getGoods().get(j).isCheck()) {
shopCartBean.getGoods().get(j).setCheck(false);
}
}
}
select_list.clear();
price = 0;
listener.onUnSelectAll(price, select_list);
}
}
//某个店面下,某个商品数量加的操作
public void numberAdd(int parent_position, int child_position) {
GoodsBean goodsBean = goodsNumChange(1, parent_position, child_position);
if (goodsBean.isCheck()) {
price += Double.parseDouble(goodsBean.getGoods_price());
listener.onNumberAdd(price, select_list);
}
}
关于mode
层的业务逻辑就是这么多了,下面就是搭建p层了,看下p层的接口:
public interface Presenter {
public void presenterList();
}
这里就定义了一个方法,主要是去看下它的子类:
public class ShopCarPresenter implements IPresenter, ShopLoaderListener {
//持有view层的接口,需要v层传进来
IView view;
//持有mode层的接口,此处在该类直接生成
Mode mode;
public ShopCarPresenter(Context context, IView view) {
this.view = view;
this.mode = new ShopMode(context, this);
}
}
其实对于p层有两种操作,一种是不对view层进行回调的操作,一种是需要对view层进行回调,由于这里分两种情况,因此这里就举例说明:
//看到没就是这么简单的一句,不带回调到view层的
@Override
public void presenterList() {
mode.loadList();
}
//也是一句,调用了ShopCartFragment的方法
@Override
public void onNumberAdd(double price, List select_list) {
if (view instanceof ShopCartFragment) {
((ShopCartFragment) view).numberAdd(price, select_list);
}
}
总的来说,p层是我们最简单的一层,因为它只是建立view层和mode层的桥梁,持有他们的实例。
下面再来看看view层的定义,看下接口:
public interface IView {
public void showSuccessPage(List list);
public void showSuccessPage(T t);
public void showErrorPage();
public void showEmptyPage();
}
这里方法就根据自己业务写方法了,其实我这里也是没必要定义那么多方法的,真正用到了就上面两个方法。
咋们这里view层的实例就是ShopCartFragment了,咋们可以看看它的定义:
其实就是对Iview实现,然后在不同的实现方法里面处理view,所以在view的实现类里面不会出现处理业务的代码,只会跟view相关的代码。
项目结构图:
代码传送门