购物车组件
购物车组件是基于一级购物车、二级购物车、行业专区购物车等页面代码抽取的组件,该组件提供了获取普通购物车/行业专区Fragment、跳转到普通/行业专区二级购物车页面、刷新购物车图标数量、加入购物车等等服务。
一、使用方式
1、添加依赖
api project(':shoppingcart')
2、调用方式
ServiceFactory.getShoppingCartService().xxx();
//如: 获取购物车Fragment.
ServiceFactory.getShoppingCartService().getShoppingCartFragment();
二、功能介绍
1、获取用于首页展示的Fragment;
/**
* 获取一级购物车Fragment
* @return ShoppingCartFragment.
*/
EmptyShoppingCartFragment getShoppingCartFragment();
2、获取用于行业专区展示的Fragment;
/**
* 获取行业专区一级购物车Fragment
* @return IndustryShoppingCartFragment.
*/
EmptyShoppingCartFragment getIndustryShoppingCartFragment();
3、刷新购物车图标数量;
/**
* 刷新购物车图标数量
*/
void refreshShoppingCartIcon();
4、刷新行业专区购物车图标数量;
/**
* 刷新行业专区购物车图标数量
*/
void refreshIndustryShoppingCartIcon();
5、跳转到二级购物车页面;
/**
* 跳转到二级购物车页面
*/
void startShoppingCartActivity();
6、跳转到行业专区二级购物车页面;
/**
* 跳转到行业专区二级购物车页面
*/
void startIndustryAreaShoppingCart();
7、添加商品到购物车;
/**
* 添加商品到购物车
* @param ydBaseView LifecycleView.
* @param requestEntity goodsInfo.
* @param listener response.
*/
void addGoodsToShoppingCart(YDBaseView ydBaseView, ShoppingCartRequestEntity requestEntity, UpdateShoppingCartListener listener);
三、组件架构
该组件使用星链友店项目统一架构:MVP+VM实现
1、组件包结构
2、组件架构图
四、购物车页面设计思路
购物车作为一个业务逻辑非常复杂的页面,从编写之初就考虑如何拆分它,主要从两个方面着手:1、页面UI层面,把购物车列表拆分成店铺标题信息栏、满减活动信息栏以及商品信息栏这三个不同ItemType的Item,修改其中某一Item,而不影响其他Item;2、业务
逻辑上把大的业务逻辑拆分成小的业务逻辑,小的业务逻辑封装到一个个函数,把计算包邮、满减价格的复杂逻辑抽取到一个函数,使整个页面多个需要计算包邮、满减价格的地方调用的是同一个函数,便于维护。
页面UI拆分对应代码中的ViewModel
五、购物车页面数据加载流程
1、在ShoppingCartPresenter类的onAttachView()调用refreshShoppingCartGoodsList()执行接口请求购物车列表数据;
2、通过Retrofit+RxJava发起接口请求得到数据源:Json字符串–解析–>ShoppingCartEntity实体类;
3、把ShoppingCartEntity实体类通过ShoppingCartFunction转化成ViewModel;
4、把ViewModel通过DataBinding绑定到View上;
六、购物车页面主要交互逻辑代码
1、请求购物车页面商品列表数据;
/**
* 请求购物车页面商品列表数据
*/
private void requestShoppingCartGoodsList() {
if(isRunningRefresh){
return;
}
isRunningRefresh = true;
mRequestModel.getShoppingCartGoodsList(source, String.valueOf(pageNum))
.compose(new LifecycleTransformer<ShoppingCartEntity>(this))
.map(mShoppingCartFunction)
.subscribe(new YDLoadingSubscriber<ShoppingCartViewObject>(mShoppingCartView) {
@Override
public void onNext(ShoppingCartViewObject viewObject) {
isRunningRefresh = false;
isLastPage = viewObject.isLastPage;
mShoppingCartView.requestComplete();
if (viewObject.list.isEmpty() && isRefresh) {
if (!mShoppingCartVM.getIsNormalModel()) {
onClickTopActionButton(mShoppingCartVM);
}
mShoppingCartVM.setToolbarActionTextVisible(View.GONE);
mShoppingCartView.showErrorPage(EmptyPageType.EMPTY_PAGE);
} else {
mShoppingCartView.showNormalView();
mShoppingCartVM.setToolbarActionTextVisible(View.VISIBLE);
mShoppingCartView.setShoppingCartGoodsList(viewObject.list, isRefresh);
}
checkNumberInCart(viewObject.allNumberInCart);
}
@Override
public void onError(int code, String message) {
super.onError(code, message);
isRunningRefresh = false;
mShoppingCartView.requestComplete();
mShoppingCartVM.setToolbarActionTextVisible(View.GONE);
mShoppingCartVM.setBottomLayoutVisible(View.GONE);
mShoppingCartView.showErrorPage(EmptyPageType.SERVER_ERROR, ShoppingCartPresenter.this);
}
});
}
2、右上角编辑/完成点击事件;
/**
* 点击右上角编辑/完成按钮
*
* @param shoppingCartVM layout viewModel
*/
public void onClickTopActionButton(ShoppingCartVM shoppingCartVM) {
shoppingCartVM.setIsNormalModel(!shoppingCartVM.getIsNormalModel());
boolean isNormalModel = shoppingCartVM.getIsNormalModel();
if (isNormalModel) {
int checkedSize = 0;
int goodsItemSize = 0;
int size = mViewObject.list.size();
for (int i = 0; i < size; i++) {
BindingAdapterItemType bindingAdapterItemType = mViewObject.list.get(i);
if (bindingAdapterItemType instanceof ShoppingCartGoodsItemVM) {
ShoppingCartGoodsItemVM shoppingCartGoodsItemVM = (ShoppingCartGoodsItemVM) bindingAdapterItemType;
String uniqueId = shoppingCartGoodsItemVM.getUniqueId();
Boolean checkBoxEnable = shoppingCartVM.getEnabledMap().get(uniqueId);
if(checkBoxEnable != null) {
shoppingCartGoodsItemVM.setCheckBoxEnable(checkBoxEnable);
}
Boolean checkBoxIsChecked = shoppingCartVM.getCheckedMap().get(uniqueId);
shoppingCartGoodsItemVM.setCheckBoxIsChecked(checkBoxIsChecked != null ? checkBoxIsChecked : false);
if (shoppingCartGoodsItemVM.getCheckBoxEnable()) {
goodsItemSize++;
if (shoppingCartGoodsItemVM.getCheckBoxIsChecked()) {
checkedSize++;
}
}
} else if(bindingAdapterItemType instanceof GoodsKindViewModel){
GoodsKindViewModel goodsKindViewModel = (GoodsKindViewModel) bindingAdapterItemType;
goodsKindViewModel.setCheckBoxEnable(goodsKindViewModel.getCheckBoxRealEnable());
goodsKindViewModel.setCheckBoxIsChecked(goodsKindViewModel.getCheckBoxRealChecked());
goodsKindViewModel.setIsFreePostage(TextUtils.equals(goodsKindViewModel.getPlatformFullFlag(), "1"));
goodsKindViewModel.setPostageVisibility(goodsKindViewModel.getIsFreePostage() ? View.VISIBLE : View.GONE);
} else if(bindingAdapterItemType instanceof FullReduceViewModel){
((FullReduceViewModel) bindingAdapterItemType).setRightVisibility(View.VISIBLE);
}
}
shoppingCartVM.getEnabledMap().clear();
shoppingCartVM.getCheckedMap().clear();
//更新合计金额、总额、立省金额
shoppingCartVM.setAllSelectCbEnable(shoppingCartVM.getRealAllSelectCbEnable());
shoppingCartVM.setAllSelectCbChecked(checkedSize == goodsItemSize && checkedSize != 0);
shoppingCartVM.setToolbarActionText("编辑");
shoppingCartVM.setBottomActionText(checkedSize > 0 ? "结算(" + checkedSize + ")" : "结算");
shoppingCartVM.setBottomActionTextEnable(checkedSize > 0);
shoppingCartVM.setAllSelectTextVisible(View.GONE);
shoppingCartVM.setSummationTextVisible(View.VISIBLE);
//更新满减、包邮信息
foreachCalcPostageAndFullReduce();
} else {
for (BindingAdapterItemType bindingAdapterItemType : mViewObject.list) {
if (bindingAdapterItemType instanceof ShoppingCartGoodsItemVM) {
ShoppingCartGoodsItemVM shoppingCartGoodsItemVM = (ShoppingCartGoodsItemVM) bindingAdapterItemType;
String uniqueId = shoppingCartGoodsItemVM.getUniqueId();
shoppingCartVM.getEnabledMap().put(uniqueId, shoppingCartGoodsItemVM.getCheckBoxEnable());
shoppingCartVM.getCheckedMap().put(uniqueId, shoppingCartGoodsItemVM.getCheckBoxIsChecked());
shoppingCartGoodsItemVM.setCheckBoxEnable(true);
shoppingCartGoodsItemVM.setCheckBoxIsChecked(false);
} else if (bindingAdapterItemType instanceof GoodsKindViewModel) {
GoodsKindViewModel goodsKindViewModel = (GoodsKindViewModel) bindingAdapterItemType;
goodsKindViewModel.setCheckBoxRealEnable(goodsKindViewModel.getCheckBoxEnable());
goodsKindViewModel.setCheckBoxEnable(true);
goodsKindViewModel.setCheckBoxRealChecked(goodsKindViewModel.getCheckBoxIsChecked());
goodsKindViewModel.setCheckBoxIsChecked(false);
goodsKindViewModel.setPostageVisibility(View.GONE);
goodsKindViewModel.setPostageTagVisible(View.GONE);
goodsKindViewModel.setRightArrowVisible(View.GONE);
} else if(bindingAdapterItemType instanceof FullReduceViewModel){
((FullReduceViewModel) bindingAdapterItemType).setRightVisibility(View.GONE);
}
}
shoppingCartVM.setAllSelectCbEnable(true);
shoppingCartVM.setAllSelectCbChecked(false);
shoppingCartVM.setToolbarActionText("完成");
shoppingCartVM.setBottomActionText("删除");
shoppingCartVM.setBottomActionTextEnable(false);
shoppingCartVM.setAllSelectTextVisible(View.VISIBLE);
shoppingCartVM.setSummationTextVisible(View.GONE);
}
}
3、底部结算/删除按钮点击事件;
/**
* 点击底部结算/删除按钮
*/
public void onClickActionButton(ShoppingCartVM viewModel) {
//构建选中的商品Id+SpecId+amount的JsonString
List<ShoppingCartSettleParam> paramEntities = new ArrayList<>();
for (BindingAdapterItemType bindingAdapterItemType : mViewObject.list) {
if(bindingAdapterItemType instanceof ShoppingCartGoodsItemVM){
ShoppingCartGoodsItemVM goodsItemVM = (ShoppingCartGoodsItemVM) bindingAdapterItemType;
if(goodsItemVM.getCheckBoxIsChecked()){
paramEntities.add(new ShoppingCartSettleParam(goodsItemVM.getGoodsId(), goodsItemVM.getSpecId()));
}
}
}
final String goodsListJson = JsonUtils.toJson(paramEntities);
if (viewModel.getIsNormalModel()) { //普通模式:结算逻辑
if (multiGoodsKind()) {
CenterOneDialog oneDialog = CenterOneDialog.newInstance()
.setContent("当前选中项包含多个专区商品,暂不支持跨专区商品合并下单,请重新勾选商品提交")
.setRight("我知道了", null);
oneDialog.show(getContext());
return;
}
loading(true);
mRequestModel.settle(goodsListJson, "", "", "", mShoppingCartView.getSource())
.compose(new LifecycleTransformer<JSONObject>(this))
.subscribe(new YDLoadingSubscriber<JSONObject>(mShoppingCartView) {
@Override
public void onNext(JSONObject jsonObject) {
int goodsStatusResult = 0;
try {
goodsStatusResult = jsonObject.getIntValue("goodsStatusResult");
} catch (Exception e) {
e.printStackTrace();
}
if (goodsStatusResult == 1) {
PlatformSettleRequest settleRequest = new PlatformSettleRequest(goodsListJson, "", "");
Bundle bundle = new Bundle();
settleRequest.source = mShoppingCartView.getSource();
bundle.putSerializable("schema", settleRequest);
ServiceFactory.getOrderService().startConfirmOrderActivity(bundle);
} else {
showToast(jsonObject.getString("msg"));
}
}
@Override
public void onError(int code, String message) {
super.onError(code, message);
if (code == 3822001) {
PlatformSettleRequest settleRequest = new PlatformSettleRequest(goodsListJson, "", "");
Bundle bundle = new Bundle();
settleRequest.source = mShoppingCartView.getSource();
bundle.putSerializable("schema", settleRequest);
ServiceFactory.getOrderService().startConfirmOrderActivity(bundle);
} else if (code == 3829010 || code == 3829011 || code == 3829012) {
Bundle bundle = new Bundle();
bundle.putString("error_code_key", String.valueOf(code));
ARouter.getInstance().build(Constants.PERSON_CERTIFICATION_ACTIVITY).with(bundle).navigation();
}
}
});
} else { //编辑模式:删除逻辑
CenterTwoDialog dialog = CenterTwoDialog.newInstance();
dialog.setContent("确定要删除这些商品吗?")
.setRight(new View.OnClickListener() {
@Override
public void onClick(View v) {
deleteGoods(goodsListJson);
}
}).show(mContext);
}
}
4、点击删除商品;
/**
* 删除商品
* @param ids delete
*/
private void deleteGoods(String ids) {
loading(true);
mRequestModel.selfBatchDeleteCart(ids, mShoppingCartView.getSource())
.compose(new LifecycleTransformer<>(this))
.subscribe(new YDLoadingSubscriber<Object>(mShoppingCartView) {
@Override
public void onNext(@io.reactivex.annotations.NonNull Object value) {
super.onNext(value);
showToast("删除成功");
//刷新购物车
mShoppingCartView.refreshShoppingCart();
}
});
}
5、点击商品Item;
/**
* 商品Item点击事件
*
* @param viewModel goods viewModel
*/
@Override
public void onItemClick(ShoppingCartGoodsItemVM viewModel) {
String source = mShoppingCartView.getSource();
ServiceFactory.getGoodsDetailFictitiousService().entryGoodsDetail(viewModel.getGoodsId(), TextUtils.equals(viewModel.getIsShopBag(), "1"), source);
}
6、长按商品Item;
/**
* 商品Item长按事件
*
* @param viewModel goods viewModel
*/
@Override
public boolean onItemLongClick(final ShoppingCartGoodsItemVM viewModel) {
XDialog boxDialog = XDialog.buildDialog(getContext(), R.layout.dialog_shopping_cart_delete, new XDialog.OnClickListener() {
@Override
public void onClick(Dialog dialog, View view) {
dialog.dismiss();
if (view.getId() == R.id.delete_tv) {
List<ShoppingCartSettleParam> paramEntities = new ArrayList<>();
ShoppingCartSettleParam entity = new ShoppingCartSettleParam(viewModel.getGoodsId(), viewModel.getSpecId());
paramEntities.add(entity);
String params = JsonUtils.toJson(paramEntities);
deleteGoods(params);
}
}
});
boxDialog.show();
return true;
}
7、点击京东优选包邮标签;
/**
* 点击京东优选包邮标签
*/
public void onClickFreePostageTag(GoodsKindViewModel viewModel){
if (mPostageExplainDialog == null) {
mPostageExplainDialog = new FreePostageExplainDialog(mContext);
}
mPostageExplainDialog.setDialogContent(viewModel.getPlatformFullText());
mPostageExplainDialog.show();
}
8、点击去凑单(京东优选);
/**
* 点击去凑单(京东优选)
*/
public void onClickAddMoreGoods(GoodsKindViewModel viewModel){
if (TextUtils.equals(viewModel.getKindId(), "1")) { //京东优选
double diffFreePostage = viewModel.getDiffFreePostage();
if (diffFreePostage > 0) {
Bundle bundle = new Bundle();
bundle.putDouble(ShoppingCartConstant.KEY_DIFF_PRICE_FREE_POSTAGE, diffFreePostage);
bundle.putDouble(ShoppingCartConstant.KEY_JD_TOTAL_PRICE, viewModel.getShopGoodsTotalPrice());
boolean fromMain = false;
if(mContext instanceof YDBaseActivity){
YDBaseActivity baseActivity = (YDBaseActivity) mContext;
String simpleName = baseActivity.getClass().getSimpleName();
if(simpleName.contains("MainActivity")){
fromMain = true;
}
}
bundle.putBoolean(ShoppingCartConstant.KEY_FROM_MAIN, fromMain);
ServiceFactory.getShoppingCartService().goToMakeOrderActivity((Activity) mContext, bundle, REQUEST_CODE);
}
}
}
9、点击去凑单(友店商城);
/**
* 点击去凑单(友店商城)
*/
public void onClickGatherTogether(FullReduceViewModel viewModel){
ARouter.getInstance().build(Constants.SEARCH_MONEY_OFF_ACTIVITY_PATH)
.withString("full_reduce_id", viewModel.getFullReduceId())
.navigation();
}
10、点击编辑商品数量;
/**
* 点击编辑商品数量
* @param viewModel
*/
@Override
public void onClickEditGoodsQuantity(final ShoppingCartGoodsItemVM viewModel){
if(mContext instanceof YDBaseActivity) {
if (mEditGoodsQuantityDialog == null) {
mEditGoodsQuantityDialog = new EditGoodsQuantityDialog(mContext, mShoppingCartView.getSource());
}
mEditGoodsQuantityDialog.setConfirmListener(new EditGoodsQuantityDialog.OnClickConfirmListener() {
@Override
public void onClickConfirm(String goodsQuantity) {
updateGoodsQuantity(viewModel, goodsQuantity, "1");
}
});
mEditGoodsQuantityDialog.setGoodsQuantity(viewModel.getGoodsQuantity());
mEditGoodsQuantityDialog.show();
}
}
11、点击增加商品数量;
/**
* 点击增加商品数量
*
* @param viewModel goods viewModel
*/
@Override
public void onClickAdd(final ShoppingCartGoodsItemVM viewModel) {
int goodsQuantityInt = viewModel.getGoodsQuantityInt();
updateGoodsQuantity(viewModel, String.valueOf(++goodsQuantityInt), "1");
}
12、点击减少商品数量;
/**
* 点击减少商品数量
*
* @param viewModel goods viewModel
*/
@Override
public void onClickMinus(final ShoppingCartGoodsItemVM viewModel) {
int goodsQuantityInt = viewModel.getGoodsQuantityInt();
updateGoodsQuantity(viewModel, String.valueOf(--goodsQuantityInt), "1");
}
13、点击选中商品Item;
/**
* 点击选中商品Item
*
* @param viewModel goods viewModel
*/
@Override
public void onClickCheckItem(ShoppingCartGoodsItemVM viewModel) {
//更新当前商品所在店铺CheckBox状态
viewModel.updateShopCheckState();
//更新包邮、满减、合计金额、总额、立省、全选CheckBox状态和底部结算/删除按钮状态
//购物车中所有商品数量(不包含不可点的)
int enableGoodsItemSize = 0;
//选中的商品数量
int checkedSize = 0;
//购物车中所有商品列表
for (BindingAdapterItemType bindingAdapterItemType : mViewObject.list) {
if (bindingAdapterItemType instanceof ShoppingCartGoodsItemVM) {
ShoppingCartGoodsItemVM shoppingCartGoodsItemVM = (ShoppingCartGoodsItemVM) bindingAdapterItemType;
if (shoppingCartGoodsItemVM.getCheckBoxEnable()) {
enableGoodsItemSize++;
if (shoppingCartGoodsItemVM.getCheckBoxIsChecked()) {
checkedSize++;
}
}
}
}
//更新全选CheckBox状态、结算/删除按钮状态
mShoppingCartVM.setBottomActionTextEnable(checkedSize > 0);
mShoppingCartVM.setAllSelectCbChecked(checkedSize == enableGoodsItemSize && checkedSize != 0);
if (mShoppingCartVM.getIsNormalModel()) {
mShoppingCartVM.setBottomActionText(checkedSize > 0 ? "结算(" + checkedSize + ")" : "结算");
}
if (mShoppingCartVM.getIsNormalModel()) {
//计算当前专区商品列表
mShoppingCartFunction.calcPostageAndFullReduce(viewModel, false);
}
}
14、点击店铺全选;
/**
* 点击店铺全选
* @param viewModel shop viewModel.
*/
public void onClickCheckShopItem(GoodsKindViewModel viewModel){
//更新当前店铺下商品选中状态
boolean shopSelectCbChecked = viewModel.getCheckBoxIsChecked();
List<ShoppingCartGoodsItemVM> shopGoodsItemVMList = viewModel.getShopGoodsItemVMList();
if(shopGoodsItemVMList != null) {
for (ShoppingCartGoodsItemVM shoppingCartGoodsItemVM : shopGoodsItemVMList) {
shoppingCartGoodsItemVM.setCheckBoxIsChecked(shopSelectCbChecked);
}
}
//更新包邮、满减、合计金额、总额、立省、全选CheckBox状态和底部结算/删除按钮状态
//购物车中所有商品数量(不包含不可点的)
int enableGoodsItemSize = 0;
//选中的商品数量
int checkedSize = 0;
//购物车中所有商品列表
for (BindingAdapterItemType bindingAdapterItemType : mViewObject.list) {
if (bindingAdapterItemType instanceof ShoppingCartGoodsItemVM) {
ShoppingCartGoodsItemVM shoppingCartGoodsItemVM = (ShoppingCartGoodsItemVM) bindingAdapterItemType;
if (shoppingCartGoodsItemVM.getCheckBoxEnable()) {
enableGoodsItemSize++;
if (shoppingCartGoodsItemVM.getCheckBoxIsChecked()) {
checkedSize++;
}
}
}
}
//更新全选CheckBox状态、结算/删除按钮状态
mShoppingCartVM.setBottomActionTextEnable(checkedSize > 0);
mShoppingCartVM.setAllSelectCbChecked(checkedSize == enableGoodsItemSize && checkedSize != 0);
if (mShoppingCartVM.getIsNormalModel()) {
//计算当前专区商品列表
foreachCalcPostageAndFullReduce();
mShoppingCartVM.setBottomActionText(checkedSize > 0 ? "结算(" + checkedSize + ")" : "结算");
}
}
15、点击底部整个购物车全选;
/**
* 点击底部整个购物车全选
*
* @param viewModel goods viewModel
*/
public void onClickCheckAll(ShoppingCartVM viewModel) {
boolean allSelectCbChecked = viewModel.getAllSelectCbChecked();
int checkedSize = 0;
for (BindingAdapterItemType bindingAdapterItemType : mViewObject.list) {
if (bindingAdapterItemType instanceof GoodsKindViewModel) {
((GoodsKindViewModel) bindingAdapterItemType).setCheckBoxIsChecked(allSelectCbChecked);
} else if (bindingAdapterItemType instanceof ShoppingCartGoodsItemVM) {
checkedSize++;
((ShoppingCartGoodsItemVM) bindingAdapterItemType).setCheckBoxIsChecked(allSelectCbChecked);
}
}
foreachCalcPostageAndFullReduce();
viewModel.setBottomActionTextEnable(allSelectCbChecked);
if (viewModel.getIsNormalModel()) {
viewModel.setBottomActionText(allSelectCbChecked ? "结算(" + checkedSize + ")" : "结算");
}
}
七、注意事项
1、秒杀商品不计入满减计算逻辑;
八、版本日志
时间 | 修改记录 | 作者 |
2019年12月05日 | 完成初步文档编写 | 吴忠园 |