购物车,订单,支付功能开发(包含支付宝支付和微信支付)(一)
1、购物车UI编写
1.1 购物车根布局
1.2 购物车根页面效果图
1.3 购物车中每条数据效果图
2、购物车数据结构分析、解析与转化
2.1 购物车每个item的数据解析类
2.2 item的数据与视图绑定
2.3 购物车根布局
2.4 效果图
3、购物车事件逻辑梳理与实现-1
3.1 处理购物车中点击事件
3.3 根布局处理全选点击事件
3.4 效果图
4、删除、清空键的事件处理
4.1 数据转换类中,更新删除后的item位置
4.2 效果图
5、购物车价格计算逻辑梳理,排坑与实现
5.1 当购物车为空时,占位view
5.2 左右加减,使得总数值改变
位于latte-ec模块main->cart包下的ShopCartDelegate。
主要作用:购物车页面的根fragment。
public class ShopCartDelegate extends BottomItemDelegate {
@Override
public Object setLayout() {
return R.layout.delegate_shop_cart;
}
@Override
public void onBindView(@Nullable Bundle savedInstanceState, View rootView) {
}
}
位于latte-ec模块main->cart包下的ShopCartDataConverter。
主要作用:购物车页面中每个item的解析类。
public class ShopCartDataConverter extends DataConverter {
@Override
public ArrayList convert() {
ArrayList dataList = new ArrayList<>();
JSONArray dataArray = JSON.parseObject(getJsonData()).getJSONArray("data");
final int size = dataArray.size();
for (int i=0;i
位于latte-ec模块main->cart包下的ShopCartAdapter。
主要作用:购物车页面中每个item的转换类,购物车的数据适配器,将json数据与视图绑定。
public class ShopCartAdapter extends MultipleRecyclerAdapter {
private static final RequestOptions OPTIONS = new RequestOptions()//图片加载库
.diskCacheStrategy(DiskCacheStrategy.ALL)
.centerCrop()
.dontAnimate();
protected ShopCartAdapter(List data) {
super(data);
//添加购物车item布局
addItemType(ShopCartItemType.SHOP_CART_ITEM, R.layout.item_shop_cart);
}
@Override
protected void convert(MultipleViewHolder holder, MultipleItemEntity entity) {
super.convert(holder, entity);
switch (holder.getItemViewType()) {
case ShopCartItemType.SHOP_CART_ITEM:
//先取出所有值
final int id = entity.getFiled(MultipleFields.ID);
final String thumb = entity.getFiled(MultipleFields.IMAGE_URL);
final String title = entity.getFiled(ShopCartItemFields.TITLE);
final String desc = entity.getFiled(ShopCartItemFields.DESC);
final int count = entity.getFiled(ShopCartItemFields.COUNT);
final double price = entity.getFiled(ShopCartItemFields.PRICE);
//取出所有控件
final AppCompatImageView imageThumb = holder.getView(R.id.image_item_shop_cart);
final AppCompatTextView tvTitle = holder.getView(R.id.tv_item_shop_cart_title);
final AppCompatTextView tvDesc = holder.getView(R.id.tv_item_shop_cart_desc);
final AppCompatTextView tvPrice = holder.getView(R.id.tv_item_shop_cart_price);
final IconTextView iconMinus = holder.getView(R.id.icon_item_minus);
final IconTextView iconPlus = holder.getView(R.id.icon_item_plus);
final AppCompatTextView tvCount = holder.getView(R.id.tv_item_shop_cart_count);
//赋值(json数据与视图绑定)
tvTitle.setText(title);
tvDesc.setText(desc);
tvPrice.setText(String.valueOf(price));
tvCount.setText(String.valueOf(count));
Glide.with(mContext)
.load(thumb)
.into(imageThumb);
break;
default:
break;
}
}
}
位于latte-ec模块main->cart包下的ShopCartDelegate。
主要作用:购物车页面的根fragment。
public class ShopCartDelegate extends BottomItemDelegate implements ISuccess {
@BindView(R2.id.rv_shop_cart)//rebuild project
RecyclerView mRecyclerView = null;
ShopCartAdapter mAdapter = null;
......
@Override
public void onSuccess(String response) {
final ArrayList data =
new ShopCartDataConverter()
.setJsonData(response)
.convert();
mAdapter = new ShopCartAdapter(data);
final LinearLayoutManager manager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(manager);//控制其显示的方式,请通过布局管理器LayoutManager
mRecyclerView.setAdapter(mAdapter);//将视图与适配器绑定,转换数据
}
}
逻辑:设置每个item的初始标记位为false,给每个item的选择框添加点击事件,点击事件前需要获取最新状态的item状态,再根据item状态进行更改;全选:在ShopCartDelegate中通过全选View内置的tag,判断是否全选,然后变换状态,将状态通过Adapter的setIsSelectedAll(boolean isSelectedAll)方法传入item的标志位中,再每个item的点击事件前判断标志位。
位于latte-ec模块main->cart包下的ShopCartAdapter。
主要作用:购物车页面中每个item的转换类,购物车的数据适配器,将json数据与视图绑定。处理item按钮的点击事件和根据根布局中全选状态改变item的状态。
public class ShopCartAdapter extends MultipleRecyclerAdapter {
......
@Override
protected void convert(MultipleViewHolder holder, final MultipleItemEntity entity) {
.......
** final IconTextView iconIsSelected = holder.getView(R.id.icon_item_shop_cart);
//赋值
tvTitle.setText(title);
tvDesc.setText(desc);
tvPrice.setText(String.valueOf(price));
tvCount.setText(String.valueOf(count));
Glide.with(mContext)
.load(thumb)
.into(imageThumb);
** //在左侧勾勾渲染之前改变全选与否状态
entity.setField(ShopCartItemFields.IS_SELECTED, mIsSelectedAll);
final boolean isSelected = entity.getFiled(ShopCartItemFields.IS_SELECTED);;
** //根据数据状态显示左侧勾勾
if (isSelected){//可根据2、中,先根据是否全选初始化item的状态,因此需要这步初始化状态
iconIsSelected.setTextColor
(ContextCompat.getColor(Latte.getApplicationContext(),R.color.app_main));
}else {
iconIsSelected.setTextColor(Color.GRAY);
}
** //添加左侧勾勾点击事件
iconIsSelected.setOnClickListener(new View.OnClickListener() {//不要在类上implement(在类上不会对应到每个上面),这里是根据每个item来new不同的实例
@Override
public void onClick(View v) {
final boolean currentSelected = entity.getFiled(ShopCartItemFields.IS_SELECTED);//每次需要动态取状态
if (currentSelected){
iconIsSelected.setTextColor(Color.GRAY);
entity.setField(ShopCartItemFields.IS_SELECTED,false);
}else{
iconIsSelected.setTextColor
(ContextCompat.getColor(Latte.getApplicationContext(),R.color.app_main));
entity.setField(ShopCartItemFields.IS_SELECTED,true);
}
}
});
break;
default:
break;
}
}
public void setIsSelectedAll(boolean isSelectedAll) {
this.mIsSelectedAll = isSelectedAll;
}
}
位于latte-ec模块main->cart包下的ShopCartDelegate。
主要作用:购物车页面的根fragment,通过标志位处理全选点击,更改所有item的按钮状态。
public class ShopCartDelegate extends BottomItemDelegate implements ISuccess {
......
@BindView(R2.id.icon_shop_cart_select_all)//用view自带的Tag(唯一数量)记录是否选中状态
IconTextView mIconSelectAll = null;
@OnClick(R2.id.icon_shop_cart_select_all)//为全选按钮添加点击事件
void onClickSelectAll(){
final int tag = (int) mIconSelectAll.getTag();
if(tag==0){//没选中
mIconSelectAll.setTextColor
(ContextCompat.getColor(Latte.getApplicationContext(),R.color.app_main));//变为橙色
mIconSelectAll.setTag(1);//已经选择了
mAdapter.setIsSelectedAll(true);//在Adapter中记录被选择的状态,
//更新RecyclerView的显示状态
mAdapter.notifyItemRangeChanged(0,mAdapter.getItemCount());
}else {
mIconSelectAll.setTextColor(Color.GRAY);
mIconSelectAll.setTag(0);
mAdapter.setIsSelectedAll(false);
mAdapter.notifyItemRangeChanged(0,mAdapter.getItemCount());
}
}
.......
@Override
public void onBindView(@Nullable Bundle savedInstanceState, View rootView) {
mIconSelectAll.setTag(0);//默认初始化
}
......
}
逻辑:根据item状态,将选中的加入到待删除的集合中,遍历待删除的集合,获取当前item的position,如果position>0,就更改item位置标志,从adapter中删除;调用adapter中的updateItemRangeFieldPosition(int positionStart)方法,从position头开始变更每个留下的position标志。
位于latte-ec模块main->cart包下的ShopCartDelegate。
主要作用:购物车页面的根fragment,通过选中个数进行删除,删除后更改每个item的位置。
public class ShopCartDelegate extends BottomItemDelegate implements ISuccess {
private ShopCartAdapter mAdapter = null;
//购物车数量标记
private int mCurrentCount = 0;//当前点中要删除的item的数量
private int mTotalCount = 0;//总共item的数量
@BindView(R2.id.rv_shop_cart)
RecyclerView mRecyclerView = null;
@BindView(R2.id.icon_shop_cart_select_all)
IconTextView mIconSelectAll = null;
......
@OnClick(R2.id.tv_top_shop_cart_remove_selected)
void onClickRemoveSelectedItem() {
final List data = mAdapter.getData();
//要删除的数据
List deleteEntities = new ArrayList<>();
for (MultipleItemEntity entity : data) {
final boolean isSelected = entity.getFiled(ShopCartItemFields.IS_SELECTED);
if (isSelected) {//被选中的就是要删除的
deleteEntities.add(entity);
}
}
int size = deleteEntities.size();
int entityPosition = 0;
for(int i=size-1;i>=0;i--){//取出要删除的entity
entityPosition = deleteEntities.get(i).getFiled(ShopCartItemFields.POSITION);
if(entityPosition<=mAdapter.getItemCount()){//防止item被删除,防止位置越界,防止超出数组上线
mAdapter.remove(entityPosition);//删除数据
}
}
//更新数据
mAdapter.updateItemRangeFieldPosition(entityPosition);//4、传入小更新的item的POSITION
}
@OnClick(R2.id.tv_top_shop_cart_clear)
void onClickClear() {//清空所有数据
mAdapter.getData().clear();
mAdapter.notifyDataSetChanged();
}
......
}
位于latte-ec模块main->cart包下的ShopCartAdapter。
主要作用:购物车页面中每个item的转换类,购物车的数据适配器,将json数据与视图绑定。通过传入的最小position,更新删除后的item位置。
public class ShopCartAdapter extends MultipleRecyclerAdapter {
private boolean mIsSelectedAll = false;
private int mCheckCount = 0;
......
//删除position位置的item后要更新该位置后面所有item的 POSITION 和 IS_SELECTED , checkCount 要记得--
public final void updateItemRangeFieldPosition(int positionStart){
int size = this.getData().size();
for(int i=positionStart;i
逻辑:在购物车根布局页面购物车item部分采用stubView进行占位,布局文件中关联占位布局;当点击全选后/获取数据成功后,需要检查item数量,为0:显示占位布局,部位0:显示RecycleView布局。
latte-ec模块res->layout包,更改delegate_shop_cart.xml文件,(购物车fragment的布局文件)
latte-ec模块res->layout包,stub_shop_cart_no_item.xml文件(购物车中每item的占位的布局文件)
位于latte-ec模块main->cart包下的ShopCartDelegate。
主要作用:购物车页面的根fragment,通过选中个数进行删除,删除后更改每个item的位置。
public class ShopCartDelegate extends BottomItemDelegate implements ISuccess {
......
@BindView(R2.id.stub_no_item)
ViewStubCompat mStubNoItem = null;//原
......
@OnClick(R2.id.tv_top_shop_cart_remove_selected)
void onClickRemoveSelectedItem() {
......
checkItemCount();//检查item数量
}
@OnClick(R2.id.tv_top_shop_cart_clear)
void onClickClear() {
......
checkItemCount();
}
void checkItemCount(){//检测recycleView中的item的个数
final int count = mAdapter.getItemCount();
if(count==0){//没有item
final View stubView = mStubNoItem.inflate();//stubView减少View渲染空间,对原来的占位View根据内部layout进行填充
final AppCompatTextView tvToBuy = stubView.findViewById(R.id.tv_stub_to_buy);//取出文字部分
tvToBuy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(),"你该购物啦!",Toast.LENGTH_SHORT).show();
}
});
mRecyclerView.setVisibility(View.GONE);
}else {
mRecyclerView.setVisibility(View.VISIBLE);
}
}
......
@Override
public void onSuccess(String response) {
......
checkItemCount();
}
}
逻辑:当点击‘-’,‘-’个数>1时,item的数量变化,计算变更后的总价,通过接口回调;购物车根布局实现接口中方法,因为item的总价变化,通过接口中的方法进入到跟布局中,获取总价,进行显示。
位于latte-ec模块main->cart包下的ShopCartAdapter。
主要作用:购物车页面中每个item的转换类,购物车的数据适配器,将json数据与视图绑定。通过选中的item个数和单价,计算总价
public class ShopCartAdapter extends MultipleRecyclerAdapter {
......
private ICartItemListener mCartItemListener = null;//监听接口
private double mTotalPrice = 0.0;//总价钱
private int mCheckCount = 0;
......
protected ShopCartAdapter(List data) {
super(data);
//初始化总价
for (MultipleItemEntity entity : data) {
final double price = entity.getFiled(ShopCartItemFields.PRICE);//获取每个item的单个价钱
final int count = entity.getFiled(ShopCartItemFields.COUNT);//获取每个item的数量
final double total = price * count;//每个item的总价
mTotalPrice = mTotalPrice + total;//总total
}
//添加购物车item布局
addItemType(ShopCartItemType.SHOP_CART_ITEM, R.layout.item_shop_cart);
}
......
public void setIsSelectedAll(boolean isSelectedAll) {
this.mIsSelectedAll = isSelectedAll;
}
public void setCartItemListener(ICartItemListener listener) {//设置监听接口
this.mCartItemListener = listener;
}
public double getTotalPrice(){//获取总价
return mTotalPrice;
}
@Override
protected void convert(MultipleViewHolder holder, final MultipleItemEntity entity) {
super.convert(holder, entity);
switch (holder.getItemViewType()) {
......
//添加加减事件
iconMinus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final int currentCount = entity.getFiled(ShopCartItemFields.COUNT);//从现有数据取出数量值
if (Integer.parseInt(tvCount.getText().toString()) > 1) {//数量大于1,可以减
RestClient.builder()
.url("shop_cart_count.php")
.loader(mContext)
.params("count", currentCount)
.success(new ISuccess() {
@Override
public void onSuccess(String response) {//点击一次,数据提交到服务器,数量保持同步
int countNum = Integer.parseInt(tvCount.getText().toString());
countNum--;
tvCount.setText(String.valueOf(countNum));//更改数量值
if (mCartItemListener != null) {//如果设置了监听
mTotalPrice = mTotalPrice - price;//每减一个,总价减每个的price
final double itemTotal = countNum * price;//每个item的总价
mCartItemListener.onItemClick(itemTotal);//具体还没操作
}
}
})
.build()
.post();
}
if (Integer.parseInt(tvCount.getText().toString()) == 1) {
Toast.makeText(mContext, "该宝贝不能减少了呦!", Toast.LENGTH_SHORT).show();
}
}
});
iconPlus.setOnClickListener(new View.OnClickListener() {
final int currentCount = entity.getFiled(ShopCartItemFields.COUNT);
@Override
public void onClick(View v) {
RestClient.builder()
.url("shop_cart_count.php")
.loader(mContext)
.params("count", currentCount)
.success(new ISuccess() {
@Override
public void onSuccess(String response) {
int countNum = Integer.parseInt(tvCount.getText().toString());
countNum++;
tvCount.setText(String.valueOf(countNum));
if (mCartItemListener != null) {
mTotalPrice = mTotalPrice + price;
final double itemTotal = countNum * price;
mCartItemListener.onItemClick(itemTotal);
}
}
})
.build()
.post();
}
});
break;
default:
break;
}
}
}
位于latte-ec模块main->cart包下的ICartItemListener接口。
主要作用:传item的总价,通过接口返回值。
public interface ICartItemListener {
void onItemClick(double itemTotalPrice);
}
位于latte-ec模块main->cart包下的ShopCartDelegate。
主要作用:购物车页面的根fragment,回调监听商品总价。
public class ShopCartDelegate extends BottomItemDelegate implements ISuccess,ICartItemListener {
private ShopCartAdapter mAdapter = null;
//购物车数量标记
private int mCurrentCount = 0;
private int mTotalCount = 0;
......
@BindView(R2.id.tv_shop_cart_total_price)//购物车总价钱的按钮
AppCompatTextView mTotalPrice = null;
.......
@Override
public void onSuccess(String response) {
final ArrayList data =
new ShopCartDataConverter()
.setJsonData(response)
.convert();
mAdapter = new ShopCartAdapter(data);
** mAdapter.setCartItemListener(this);//数据请求结束,添加监听
final LinearLayoutManager manager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(mAdapter);
checkItemCount();
}
@Override
public void onItemClick(double itemTotalPrice) {
final double price = mAdapter.getTotalPrice();//购物车总价
mTotalPrice.setText(String.valueOf(price));//购物车总价显示
}