您的位置:首页 > 移动开发

App开发:购物车

2016-11-22 17:58 148 查看
购物车的实现有多种方式,一直觉得实现起来有难度,不过只实现一部分,有简单到复杂,可能一步步的就实现了购物车。

实现方式一:RecyclerView

参考:Android:玩转购物车界面和逻辑只需要一层Recyclerview,一个二层for循环和三个属性

实现原理:对item中的view设置回调。

这是自己练习写的,只实现了购物车这一个界面,是假数据,可能有bug,不建议作为项目使用。大家可以看上面的参考,实现方式不同。

效果图:





实现功能:

点击“+”增加商品数量

点击“-”减少商品数量

点击–删除该商品

选中商铺则选中该商铺下的所有商品

若商铺下的商品都被选中,那么也选中商铺;有一个没选中,则不选中商铺

点击–则购物车的商品全部选中;反之,都不选

对–总价和商品件数时时更新

思路

为了避免Button等控件造成抢占点击事件的情况发生,item都由TextView+ImageView这些不含点击事件的控件组成。

布局:

RecyclerView
中,
item
有2种,一种是
head
,一种是
body
,根据
getItemViewType(int position
来区分
head
body


加+减+删除+选中:

对于各
TextView
在其点击事件的监听中,添加了回调,在
MainActivity
中使用回调处理业务逻辑.

bug:

RecyclerView
滑动不流畅,感觉有卡顿。

代码

2种ViewType

先定义2中viewType:int

private static int ViewTypeHead = 0;
private static int ViewTypeBody = 1;


再重写方法
getItemViewType(int position)


@Override
public int getItemViewType(int position) {
if (list.get(position) instanceof ShopBean) {
return ViewTypeHead;
} else {
return ViewTypeBody;
}
}


接着创建
ViewTyped
对应的
ViewHolder


public class ItemHeadViewHolder extends RecyclerView.ViewHolder {
...
public ItemHeadViewHolder(View itemView) {
super(itemView);
...
}
}


public class ItemBodyViewHolder extends RecyclerView.ViewHolder {
...
public ItemBodyViewHolder(View itemView) {
super(itemView);
...
}
}


然后在方法
onCreateViewHolder(...)
中返回不同的
ViewHolder


@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ViewTypeHead) {
View itemView = View.inflate(parent.getContext(), R.layout.item_header, null);
return new ItemHeadViewHolder(itemView);
} else {
View itemView = View.inflate(parent.getContext(), R.layout.item_body, null);
return new ItemBodyViewHolder(itemView);
}
}


最后在方法
onBindViewHolder(...)
中绑定数据

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
if (ViewTypeHead == getItemViewType(position)) {
...
} else if (ViewTypeBody == getItemViewType(position)) {
...
}
}


这样就实现了复杂的
RecyclerView
的显示,下面设置回调

+ 增加商品数量

Step1:回调:

//增加数量
public interface OnAddListener {
void addCount(int position);
}
public OnAddListener mOnAddListener;
public void setmOnAddListener(OnAddListener mOnAddListener) {
this.mOnAddListener = mOnAddListener;
}


Step2:在
onBindViewHolder(...)
声明回调

holder.tv_product_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnAddListener.addCount(position);
}
});


Step3:在
MainActivity
中使用

取出
position
对应的商品对象,将其数量+1,然后更新
ui,  notifyDataSetChanged()


adapter.setmOnAddListener(new MyAdapter.OnAddListener() {
@Override
public void addCount(int position) {
CartlistBean info = (CartlistBean) list.get(position);
info.setCount(info.getCount() + 1);
adapter.notifyDataSetChanged();
}
});


- 减少商品数量

Step1:回调:

public interface OnCutListener {
void cutCount(int position);
}

public OnCutListener mOnCutListener;

public void setmOnCutListener(OnCutListener mOnCutListener) {
this.mOnCutListener = mOnCutListener;
}


Step2:在
onBindViewHolder(...)
声明回调

holder.tv_product_subtract.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnCutListener.cutCount(position);
}
});


Step3:在
MainActivity
中使用回调并处理逻辑

取出
position
对应的商品对象,将其数量-1,若数量=1,则弹出Toast,然后更新
ui,  notifyDataSetChanged()


adapter.setmOnCutListener(new MyAdapter.OnCutListener() {
@Override
public void cutCount(int position) {
CartlistBean info = (CartlistBean) list.get(position);
if (info.getCount() > 1) {
info.setCount(info.getCount() - 1);
adapter.notifyDataSetChanged();
} else {
Toast.makeText(context, "商品数量必须大于等于1", Toast.LENGTH_SHORT).show();
}
}
});


删除商品

Step1:回调:

public interface OnDeleteListener {
void DeleteProduct(int position);
}

public OnDeleteListener mOnDeleteListener;

public void setmOnDeleteListener(OnDeleteListener mOnDeleteListener) {
this.mOnDeleteListener = mOnDeleteListener;
}


Step2:在
onBindViewHolder(...)
声明回调

holder.tv_product_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnDeleteListener.DeleteProduct(position);
}
});


Step3:在
MainActivity
中使用

从集合中删除
position
对应的对象,如果
position-1
是商铺,而且
position-1
是集合的最后一个或者
position
也是店铺,那么也把
position-1
删除掉,然后更新
ui,  notifyDataSetChanged()


adapter.setmOnDeleteListener(new MyAdapter.OnDeleteListener() {
@Override
public void DeleteProduct(int position) {
list.remove(position);
if (list.get(position - 1) instanceof ShopBean) {
if (position-1 == (list.size() - 1)||list.get(position) instanceof ShopBean) {
list.remove(position - 1);
}
}
adapter.notifyDataSetChanged();
}
});


选中店铺

Step1:回调:

public interface OnShopSelectListener {
void selectShop(int position);
}

public OnShopSelectListener mOnShopSelectListener;

public void setOnShopSelectListener(OnShopSelectListener mOnShopSelectListener) {
this.mOnShopSelectListener = mOnShopSelectListener;
}


Step2:在
onBindViewHolder(...)
声明回调

holder.tv_shop_select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnShopSelectListener.selectShop(position);
}
});


Step3:在
MainActivity
中使用

选中position对应的商铺,从
position+1
开始循环,如是商铺则结束循环;若不是商铺,就是商品,则选中该商品,最后更新
UI, adapter.notifyDataSetChanged()


adapter.setOnShopSelectListener(new MyAdapter.OnShopSelectListener() {
@Override
public void selectShop(int position) {
//选中商铺
ShopBean info = (ShopBean) list.get(position);
info.setShopSelect(!info.getShopSelect());
//选中商品
for (int i = position + 1; i < list.size(); i++) {
if (list.get(i) instanceof ShopBean) {
//是商铺,则结束循环
break;
} else {
//非商铺,选中[position + 1,i)之间的商品
CartlistBean info2 = (CartlistBean) list.get(i);
info2.setSelect(info.getShopSelect());
}
}
adapter.notifyDataSetChanged();
}
});


选中商品

Step1:回调:

public interface OnProductSelectListener {
void selectProduct(int position);
}

public OnProductSelectListener mOnProductSelectListener;

public void setOnProductSelectListener(OnProductSelectListener mOnPorductSelectListener) {
this.mOnProductSelectListener = mOnPorductSelectListener;
}


Step2:在
onBindViewHolder(...)
声明回调

holder.tv_product_select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnProductSelectListener.selectProduct(position);
}
});


Step3:在
MainActivity
中使用

首先选中该
position
对应的商品,

如果该商铺下的商品全选中了,那么选中该商铺,那么怎么判断该商铺下的商品全部被选中了呢?:

首先取出该
position
所在的商铺的
position
,设置
m
,在得到下一间商铺的
position
,设置
n
,选中
(m,n)
之间的商品。

关键就是怎么获取
m,n
的值?

m:
递减
for
循环,如果
list.get(i)
的类型是
ShopBean
,那么
m = i;


n:
递增
for
循环,如果
list.get(i)
的类型是
ShopBean
,那么
n = i;
,

若该商铺是最后一间商铺,那么
n
设为集合的长度
n = list.size();


adapter.setOnProductSelectListener(new MyAdapter.OnProductSelectListener() {
@Override
public void selectProduct(int position) {
CartlistBean info = (CartlistBean) list.get(position);
info.setSelect(!info.getIsSelect());
int m = 0;//商铺的position--该商品所在的商铺
int n = 0;//下一间商铺的position
//取值m
for (int i = position; i > 0; i--) {
if (list.get(i) instanceof ShopBean) {
m = i;
break;
}
}
//取值n
for (int i = position; i < list.size(); i++) {
if (list.get(i) instanceof ShopBean) {
n = i;
break;
} else {
n = list.size();
}
}
//将该商铺下的商品是否选中的而状态放入集合selectList
ArrayList<Boolean> selectList = new ArrayList<>();
for (int i = m + 1; i < n; i++) {
CartlistBean info2 = (CartlistBean) list.get(i);
selectList.add(info2.getIsSelect());
}
//如果全是true,那么商铺页选true
ShopBean shopInfo = (ShopBean) list.get(m);
if (selectList.contains(false)) {
shopInfo.setShopSelect(false);
} else {
shopInfo.setShopSelect(true);
}
adapter.notifyDataSetChanged();
}
});


全选

点击全选,那么选中所有商品,并显示总价和数量;

取消全选,那么取消所有商品,并显示总价和数量为0.

- 首先定义3个变量
num 、totalPrice 、isCheckAll
,是:总数量 +总价格 + 是否全部选中,默认值分别是
0,0.0F,false


- 点击“全选”按钮后,
isCheckAll = !isCheckAll;
,根据
isCheckAll
,判断是否选中店铺和商品

若是true,遍历集合,选中店铺和商品,同时计算商品数量和总价格。

若是false,遍历集合,不选中店铺和商品,同时将
num 、 totalPrice
的值设为0.

tv_check_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
num = 0;
totalPrice = 0.0f;
isCheckAll = !isCheckAll;
if (isCheckAll) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) instanceof ShopBean) {
ShopBean info = (ShopBean) list.get(i);
info.setShopSelect(isCheckAll);
} else {
CartlistBean info = (CartlistBean) list.get(i);
info.setSelect(isCheckAll);
num = num + 1;
totalPrice = totalPrice + Float.parseFloat(info.getPrice()) * Float.parseFloat(info.getPrice());
}
}
} else {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) instanceof ShopBean) {
ShopBean info = (ShopBean) list.get(i);
info.setShopSelect(isCheckAll);
} else {
CartlistBean info = (CartlistBean) list.get(i);
info.setSelect(isCheckAll);
}
}
num = 0;
totalPrice = 0.0f;
}
tv_check_all.setSelected(isCheckAll);
adapter.notifyDataSetChanged();
tv_total_count.setText("共" + num + "件商品");
tv_total_price.setText("总价:" + totalPrice);
}
});


监控全局

当某一个商品被选中的时候,底部的总价格和总数量要及时更新。

当商品全部选中的时候,底部的“全选”也要选中

Step1:回调:

//监控购物车列表选中情况
public interface OnRefreshListener {
void refresh(boolean havaSelect);//是否有选中item
//boolean havaSelect参数无效,去掉也可以
}

public OnRefreshListener mOnRefreshListener;

public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
this.mOnRefreshListener = mOnRefreshListener;
}


Step2:在
onBindViewHolder(...)
声明回调

if (mOnRefreshListener != null) {
mOnRefreshListener.refresh(true);
}


Step3:在
MainActivity
中使用

怎么计算总价和和总数量?

遍历集合,集合中的元素是商品,那么数量*价格,就是总价格;
totalCount = totalCount + info.getCount();
就是总数量。

怎么实现 当商品全部选中的时候,底部的“全选”也要选中?

遍历集合,如果有一个商品没被选中,就不是”全选,”那么结束循环,不改变
全选
的背景图片

反之,则改变
全选
的背景图片。

adapter.setOnRefreshListener(new MyAdapter.OnRefreshListener() {
@Override
public void refresh(boolean havaSelect) {
int totalCount = 0;
float totalPrice = 0.0f;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) instanceof CartlistBean) {
CartlistBean info = (CartlistBean) list.get(i);
if (info.getIsSelect()) {
totalCount = totalCount + info.getCount();
totalPrice = totalPrice + info.getCount() * Float.parseFloat(info.getPrice());
}
}
}
tv_total_count.setText("共" + totalCount + "件商品");
tv_total_price.setText("总价:" + totalPrice);

//全部选中,那么底部勾选“全选”
boolean isCheckAll = false;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) instanceof CartlistBean) {
CartlistBean info = (CartlistBean) list.get(i);
if (!info.getIsSelect()) {
isCheckAll = false;
break;
} else {
isCheckAll = true;
}
}
}
tv_check_all.setSelected(isCheckAll);
}
});


Demo

https://git.oschina.net/shoppingmallProject/ShopCartDemo01

实现方式二:RecyclerView

item布局包含商铺和商品,

但只有下面2中情况才显示商铺名称,其余隐藏。

第一个

(i+1)与i的店铺名称不同

下面是联系人列表



Contact contact = contacts.get(position);
MyHolder holder = (MyHolder) viewHolder;
//显示index
if (position == 0 || !contact.getIndex().equals(contacts.get(position - 1).getIndex())) {
holder.tv_index.setVisibility(View.VISIBLE);
holder.tv_index.setText(contact.getIndex());
} else {
holder.tv_index.setVisibility(View.GONE);
}


实现方式三:ExpandableListView

https://github.com/louisgeek/LouisShopCart
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: