App开发:购物车

购物车的实现有多种方式,一直觉得实现起来有难度,不过只实现一部分,有简单到复杂,可能一步步的就实现了购物车。

实现方式一:RecyclerView

参考:Android:玩转购物车界面和逻辑只需要一层Recyclerview,一个二层for循环和三个属性
实现原理:对item中的view设置回调。

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

效果图:

这里写图片描述 这里写图片描述

实现功能:

  • 点击“+”增加商品数量
  • 点击“-”减少商品数量
  • 点击–删除该商品
  • 选中商铺则选中该商铺下的所有商品
  • 若商铺下的商品都被选中,那么也选中商铺;有一个没选中,则不选中商铺
  • 点击–则购物车的商品全部选中;反之,都不选
  • 对–总价和商品件数时时更新

思路

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

布局:
RecyclerView中,item有2种,一种是head,一种是body,根据getItemViewType(int position来区分headbody
加+减+删除+选中:
对于各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

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值