仿淘寶購物車

雙11剛過,感覺淘寶購物車,你挺強大呀。雖然在淘寶上買不起,但是我可以自己做一個購物車自己買過把癮。就想著自己也來仿著做一個吧。這個叫李博程序員十分不容易,白天上班,常常晚上寫項目,寫博客到半夜3點,希望大家多多支持一下吧。
github代碼直通車

啥也不說了,先上效果圖:


giphy.gif
購物車重要術語: 單品,商品,SKU,SPU

商品:淘寶叫item,京東叫product,商品特指與商家有關的商品,每個商品有一個商家編碼,每個商品下面有多個顏色,款式,大小,每種組合的笛卡爾積為一個SKU。

SKU:Stock Keeping Unit(庫存量單位),SKU即庫存進出計量的單位, 可以是以件、盒、托盤等為單位。在服裝、鞋類商品中使用最多最普遍。例如紡織品中一個SKU通常表示:規格、顏色、款式。一個商品可以有多個sku。

SPU:Standard Product Unit (標準化產品單元),SPU是商品信息聚合的最小單位,是一組可復用、易檢索的標準化信息的集合,該集合描述了一個產品的特性。通俗點講,屬性值、特性相同的商品就可以稱為一個SPU。例如iphone8的64G,黑色等售賣的屬性就是spu屬性。一個商品有一個spu。

購物車應該有的其他功能:
  • 支付之前可選:優惠券、打折券、滿減券等(用戶通過活動,購買返現,關注公眾號,搶紅包等獲得)。
  • 訂單狀態跟蹤:狀態可以包括未付款,已付款,備貨,配送中,確認收貨,取消訂單,退款中,退款成功,線下自取。
  • 防止刷單機制:獲取設備IMEI,0就是模擬器,后臺應判斷該設備不能創建訂單。
  • 訂單失效:30分鐘支付時間,未支付應該恢復SKU。

該購物車功能包括了選擇商品,增減商品數量,計算總價,全選,全不選功能。需要接入結算功能請看我的博客微信支付寶接入。

item實體類:
public class ShopcartEntity {
    /**
     * product_id : 53   商品id
     * quantity : 4      購物車選擇數量
     * product_name : 商品名稱
     * product_price :  商品價格
     * product_quantity : 20   庫存
     * picRes :  圖片資源res,這里用的本地圖片
     * product_status :  訂單狀態
     */
    private int id;
    private int product_id;
    private int quantity;
    private String product_name;
    private String product_price;
    private int product_quantity;
    private int picRes;
    private String product_status;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setProduct_id(int product_id) {
        this.product_id = product_id;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public void setProduct_name(String product_name) {
        this.product_name = product_name;
    }

    public void setProduct_price(String product_price) {
        this.product_price = product_price;
    }

    public void setProduct_quantity(int product_quantity) {
        this.product_quantity = product_quantity;
    }

    public int getPicRes() {
        return picRes;
    }

    public void setPicRes(int picRes) {
        this.picRes = picRes;
    }

    public void setProduct_status(String product_status) {
        this.product_status = product_status;
    }

    public int getProduct_id() {
        return product_id;
    }

    public int getQuantity() {
        return quantity;
    }

    public String getProduct_name() {
        return product_name;
    }

    public String getProduct_price() {
        return product_price;
    }

    public int getProduct_quantity() {
        return product_quantity;
    }

    public String getProduct_status() {
        return product_status;
    }

}

功能實現流程:

1.adapter.registerAdapterDataObserver(totalPriceObserver),給adapter添加數據變化監聽類,一旦有增減商品,在onChanged()回調中重新計算總價

2.用SparseArray優化集合存儲checkbox選擇了的商品,類似于hashmap,商品id作為鍵,列表當前position的checkbox選中狀態boolean作為值,這個數據需要計算總價。

3.calculateTotalPrice()方法:遍歷選中商品,用id匹配得到商品entity,該項價格=選中數量*該商品單價,再累加到總價

4.全選,將所有列表數據添加打sparseArray中。全部選,clear()清除全部數據。

功能實現類:
public class ShopCartActivity extends AppCompatActivity implements View.OnClickListener {

    @Bind(R.id.tv_nodatas)
    TextView tvNodatas;
    @Bind(R.id.tv_shopcart_totalmoney)
    TextView tvShopcartTotalmoney;
    @Bind(R.id.cb_shopcart_all)
    CheckBox cbShopcartAll;
    @Bind(R.id.tv_billing)
    TextView tvBilling;
    @Bind(R.id.rv)
    RecyclerView rv;
    private int[] pics = {R.mipmap.test1, R.mipmap.test2, R.mipmap.test3, R.mipmap.test4};
    private ArrayList<ShopcartEntity> datas = new ArrayList();
    private CommonAdapter<ShopcartEntity> adapter;
    /**
     * 用來記錄checkBox列表當前選中狀態,購物車id是鍵,是否選中狀態是值
     */
    private SparseArray<Boolean> mSelectState = new SparseArray();
    /**
     * 購物車商品總價格
     */
    private float totalMoney = 0;
    /**
     * 創建數量改變觀察者對象
     */
    private RecyclerView.AdapterDataObserver totalPriceObserver = new RecyclerView.AdapterDataObserver() {

        /**
         * 當Adapter的notifyDataSetChanged方法執行時被調用
         */
        public void onChanged() {
            calculateTotalPrice();
        }

        /**
         * 當Adapter 調用 notifyDataSetInvalidate方法執行時被調用
         */
        public void onInvalidated() {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shop_cart);
        ButterKnife.bind(this);

        initView();
    }

    private void initView() {

        rv.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        adapter = new CommonAdapter<ShopcartEntity>(getApplicationContext(), R.layout.item_shopcart, datas) {
            @Override
            protected void convert(ViewHolder baseViewHolder, final ShopcartEntity entity, final int position) {
                final CheckBox cbChoose = baseViewHolder.getView(R.id.cb_shopcart);
                ImageView ivCover = baseViewHolder.getView(R.id.iv_shopcart_cover);
                TextView tvName = baseViewHolder.getView(R.id.tv_shopcart_name);
                TextView tvPrice = baseViewHolder.getView(R.id.tv_shopcart_price);
                ImageButton ibDel = baseViewHolder.getView(R.id.ib_shopcart_del);
                final TextView tvReduce = baseViewHolder.getView(R.id.tv_detail_reduce);
                TextView tvPlus = baseViewHolder.getView(R.id.tv_detail_plus);
                final TextView tvNum = baseViewHolder.getView(R.id.tv_detail_productnum);

                ivCover.setImageResource(entity.getPicRes());
                tvName.setText(entity.getProduct_name());
                tvPrice.setText(entity.getProduct_price());
                tvNum.setText("" + entity.getQuantity());

                final int id = entity.getId();
                cbChoose.setChecked(mSelectState.get(id, false));
                cbChoose.setOnClickListener(new View.OnClickListener() {     //用onclick方法而不是onChecked方法,因為是自動調用onCheckedChange方法
                    @Override
                    public void onClick(View v) {
                        //通過保存的是否選中來判斷操作
                        boolean isSelected = !mSelectState.get(id,false);
                        cbChoose.setChecked(isSelected);
                        if(isSelected){
                            mSelectState.put(id, true);
                        }else{
                            mSelectState.delete(id);
                        }
                        cbShopcartAll.setChecked(mSelectState.size() == datas.size());   //判斷是否達到全選
                        notifyDataSetChanged();
                    }
                });

                tvReduce.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int quatity = (datas.get(position)).getQuantity();
                        if(quatity == 1) return;
                        (datas.get(position)).setQuantity(quatity - 1);
                        notifyDataSetChanged();
                    }
                });
                tvPlus.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int quatity = (datas.get(position)).getQuantity();
                        if(quatity >= entity.getProduct_quantity()){
                            Toast.makeText(getApplicationContext(),"超出庫存量", Toast.LENGTH_SHORT).show();
                            return;
                        }
                        (datas.get(position)).setQuantity(quatity + 1);
                        notifyDataSetChanged();
                    }
                });
            }
        };
        rv.setAdapter(adapter);
        adapter.registerAdapterDataObserver(totalPriceObserver);

        cbShopcartAll.setOnClickListener(this);
        tvBilling.setOnClickListener(this);

        initData();
    }

    /**
     * 模擬服務器數據
     */
    private void initData() {
        ArrayList list = new ArrayList();
        ShopcartEntity entity;
        for (int i = 0; i < pics.length; i++) {
            entity = new ShopcartEntity();
            entity.setId(i);
            entity.setProduct_id(i);
            entity.setProduct_name("商品" + i);
            entity.setProduct_price("199");
            entity.setProduct_status("selling");
            entity.setPicRes(pics[i]);
            entity.setQuantity(1);
            entity.setProduct_quantity(5);
            list.add(entity);
        }
        datas.addAll(list);
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.cb_shopcart_all:
                checkAll();
                break;
            case R.id.tv_billing:
                //判斷是否有一項商品的選擇
                if (mSelectState.size() == 0) {
                    Toast.makeText(getApplicationContext(), "未選擇商品", Toast.LENGTH_SHORT).show();
                } else {
                    //去結算
                }
                break;
        }
    }

    private void calculateTotalPrice() {
        totalMoney = 0;
        for (int i = 0; i < mSelectState.size(); i++) {
            for (ShopcartEntity entity : datas) {
                if (mSelectState.keyAt(i) == entity.getId()) {    //表明選中了當前這項
                    totalMoney += entity.getQuantity() * Float.parseFloat(entity.getProduct_price());
                }
            }
        }
        tvShopcartTotalmoney.setText("" + totalMoney);
    }

    private void checkAll() {
        mSelectState.clear();
        if (cbShopcartAll.isChecked()) {   //全選
            for (int i = 0; i < datas.size(); i++) {
                int id = datas.get(i).getId();
                mSelectState.put(id, true);
            }
            adapter.notifyDataSetChanged();
        } else {   //全不選
            adapter.notifyDataSetChanged();
        }
    }

}

購物車是個危險的東西,稍不注意就被剁手。喜歡我,就點我吧!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容