Android開發丶商城購物車模塊(含demo超詳細)

這篇博文詳細描述了從零開始實現購物車,使用的控件很簡單,就一個ExpandableListView,避免了嵌套,操作更簡單方便。

先看看效果吧。

怎麼樣,很棒吧,如圖所示,主要有店鋪名稱,商品價格,數量,圖片,底部有總價格和結算(刪除)按鈕。

1.那我們先把常用的控件添加依賴,這裏主要就是用一個第三方的刷新控件smartRefreshLayout。

刷新控件:com.scwang.smartrefresh:SmartRefreshLayout:1.0.5.1

2.現在就可以畫主界面了。主界面東西不多,主要就是一個刷新控件smartRefreshLayout,一個列表控件ExpandableListView,還有若干TextView,畫完大概就是這樣的(中間空白是因爲是列表還沒有數據)。

activity_main.xml代碼如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:text="京西不自營"
            android:textSize="16sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:background="@drawable/bg_edit"
            android:text="編輯"
            android:textColor="#f0584f"
            android:textSize="16sp" />

    </RelativeLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#000" />

    <com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:id="@+id/main_smartRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <ExpandableListView
            android:id="@+id/main_expandableListView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </com.scwang.smartrefresh.layout.SmartRefreshLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#000" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:paddingLeft="20dp"
            android:text="總價格:0"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@color/colorPrimaryDark"
            android:gravity="center"
            android:text="結算"
            android:textColor="#fff"
            android:textSize="16sp" />

    </LinearLayout>
</LinearLayout>

值得一提的是,我們這裏給 "編輯" 按鈕添加了一個紅線背景,這裏我們手動畫一個xml背景文件即可。

bg_edit.xml代碼如下

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 邊角弧度 -->
    <corners
        android:bottomLeftRadius="5dp"
        android:bottomRightRadius="5dp"
        android:topLeftRadius="5dp"
        android:topRightRadius="5dp" />

    <!-- 包裹線條 -->
    <stroke
        android:width="1dp"
        android:color="#f0584f" />

    <!-- 內間距 -->
    <padding
        android:bottom="5dp"
        android:left="10dp"
        android:right="10dp"
        android:top="5dp" />

</shape>

3.接下來我們就可以畫列表item了。

我們發現主要是以選中狀態、商品名稱、規格描述、價格、右邊的數量選擇器來顯示,這個不難,畫完大概就是這樣的

這裏稍微有點難度的是選擇器的繪製,主要是三個Textview,三者不同之處在於背景顏色和邊框顏色,我們先畫兩個背景xml

A、加減號所需的灰色背景xml:

bg_calculator_gray.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#f2f2f2" />
    <stroke
        android:width="1px"
        android:color="#CCCCCC" />

</shape>

B、中間數字所需的白色背景xml:

bg_calculator_white.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    
    <solid android:color="#ffffff"/>
    <stroke android:color="#CCCCCC"
        android:width="1px"/>

</shape>

現在沒什麼難度了,都懟上去吧,下面是子item佈局的詳細代碼(一個相對佈局就能搞定的就不要重複嵌套其它佈局了):

item_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:paddingRight="10dp">

    <RelativeLayout
        android:id="@+id/item_check"
        android:layout_width="40dp"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/item_checkStatus"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_centerInParent="true"
            android:src="@drawable/radio_choose" />
    </RelativeLayout>

    <ImageView
        android:id="@+id/item_pic"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/item_check"
        android:src="@color/colorPrimaryDark" />

    <TextView
        android:id="@+id/item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/item_pic"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/item_pic"
        android:text="這是一隻標題"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/item_spec"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/item_name"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@id/item_pic"
        android:text="這是一隻規格" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/item_pic"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/item_pic"
        android:text="¥0.00"
        android:textColor="#f0584f" />

    <TextView
        android:id="@+id/item_reduce"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_alignBottom="@id/item_pic"
        android:layout_toLeftOf="@id/item_num"
        android:background="@drawable/bg_calculator_gray"
        android:gravity="center"
        android:text="—" />

    <TextView
        android:id="@+id/item_num"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_alignBottom="@id/item_pic"
        android:layout_toLeftOf="@id/item_add"
        android:background="@drawable/bg_calculator_white"
        android:gravity="center"
        android:text="0" />

    <TextView
        android:id="@+id/item_add"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_alignBottom="@id/item_pic"
        android:layout_alignParentRight="true"
        android:background="@drawable/bg_calculator_gray"
        android:gravity="center"
        android:text="+" />

</RelativeLayout>

4.我們現在來繪製父item的佈局。

就一個選擇狀態圖片,店鋪名稱,很簡單,畫完大概就是這樣的

代碼如下:

item_main_head.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <RelativeLayout
        android:id="@+id/item_head_check"
        android:layout_width="40dp"
        android:layout_height="40dp">

        <ImageView
            android:id="@+id/item_head_checkStatus"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_centerInParent="true"
            android:src="@drawable/radio_choose" />
    </RelativeLayout>

    <TextView
        android:id="@+id/item_head_shopName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:text="天貓一號賣家"
        android:textSize="16sp" />

</LinearLayout>

5.現在我們搞一下店鋪和商品的列表bean。

店鋪主要是一個選中狀態isSelect和店鋪名shopNum。

商品主要是一個選中狀態isSelect、一個圖片goodsPic、一個標題goodsName、一個規格goodsSpec、一個原價格goodsOriginalPrice、一個選中器商品數量goodsShopNum。

把參數懟進去搞起來!!!代碼如下

MainBean.java

package com.fantasychong.carttest0128;

import java.io.Serializable;
import java.util.List;

/**
 * @author fantasychong
 * @date 2019/1/29
 */
public class MainBean implements Serializable {
    private boolean isSelect; //店鋪選中狀態
    private String shopName; //店鋪名稱
    private List<MainItemBean> cartItemBeanList; //商品list

    @Override
    public String toString() {
        return "MainBean{" +
                "isSelect=" + isSelect +
                ", shopName='" + shopName + '\'' +
                ", cartItemBeanList=" + cartItemBeanList +
                '}';
    }

    public MainBean(boolean isSelect, String shopName, List<MainItemBean> cartItemBeanList) {
        this.isSelect = isSelect;
        this.shopName = shopName;
        this.cartItemBeanList = cartItemBeanList;
    }

    public boolean isSelect() {
        return isSelect;
    }

    public void setSelect(boolean select) {
        isSelect = select;
    }

    public String getShopName() {
        return shopName;
    }

    public void setShopName(String shopName) {
        this.shopName = shopName;
    }

    public List<MainItemBean> getCartItemBeanList() {
        return cartItemBeanList;
    }

    public void setCartItemBeanList(List<MainItemBean> cartItemBeanList) {
        this.cartItemBeanList = cartItemBeanList;
    }

    public static class MainItemBean implements Serializable {
        private boolean isSelect; //商品選中狀態
        private int goodsPic; //商品圖片
        private String goodsName; //商品名稱
        private String goodsSpec; //商品規格
        private String goodsOriginalPrice; //商品價格
        private String goodsNum;  //商品數量

        public MainItemBean(boolean isSelect, int goodsPic, String goodsName, String goodsSpec, String goodsOriginalPrice, String goodsNum) {
            this.isSelect = isSelect;
            this.goodsPic = goodsPic;
            this.goodsName = goodsName;
            this.goodsSpec = goodsSpec;
            this.goodsOriginalPrice = goodsOriginalPrice;
            this.goodsNum = goodsNum;
        }

        @Override
        public String toString() {
            return "CartItemBean{" +
                    "isSelect=" + isSelect +
                    ", goodsPic=" + goodsPic +
                    ", goodsName='" + goodsName + '\'' +
                    ", goodsSpec='" + goodsSpec + '\'' +
                    ", goodsOriginalPrice='" + goodsOriginalPrice + '\'' +
                    ", goodsNum='" + goodsNum + '\'' +
                    '}';
        }

        public boolean isSelect() {
            return isSelect;
        }

        public void setSelect(boolean select) {
            isSelect = select;
        }

        public int getGoodsPic() {
            return goodsPic;
        }

        public void setGoodsPic(int goodsPic) {
            this.goodsPic = goodsPic;
        }

        public String getGoodsName() {
            return goodsName;
        }

        public void setGoodsName(String goodsName) {
            this.goodsName = goodsName;
        }

        public String getGoodsSpec() {
            return goodsSpec;
        }

        public void setGoodsSpec(String goodsSpec) {
            this.goodsSpec = goodsSpec;
        }

        public String getGoodsOriginalPrice() {
            return goodsOriginalPrice;
        }

        public void setGoodsOriginalPrice(String goodsOriginalPrice) {
            this.goodsOriginalPrice = goodsOriginalPrice;
        }

        public String getGoodsNum() {
            return goodsNum;
        }

        public void setGoodsNum(String goodsNum) {
            this.goodsNum = goodsNum;
        }
    }
}

6.接下來,我們就來搞最重要也是最核心的列表adapter了(前方高能預警)。

新建一個MainAdapter,繼承核心基類BaseExpandableListAdapter,生成10個方法,我們先來分析一下。

這裏我們定義兩個從MainActivity通過構造函數傳進來的父list和子list,對應的分別是mainBeanList和mainItemBeanList()。

1)getGroupCount()和getChildCount()

      顧名思義,返回父item和子item的個數,比如在我們購物車裏,那就是返回相應的店鋪個數和對應商鋪下商品的個數。

@Override
public int getGroupCount() {
    return mainBeanList.size();
}

@Override
public int getChildrenCount(int groupPosition) {
    return mainItemBeanList.size();
}

2)getGroup()和getChild()

      意爲獲取某個位置下的某個對象,這個是固定模板的。

@Override
public Object getGroup(int groupPosition) {
    return mainBeanList.get(groupPosition);
}

@Override
public Object getChild(int groupPosition, int childPosition) {
    return mainItemBeanList.get(groupPosition).get(childPosition);
}

3)getGroupId()和getChildId()

      意爲獲取某個位置下的對象id,這個是固定模板的。

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

4)hasStableIds()

     字面意思是是否持有穩定的id,不過我還沒太搞懂這個是幹嘛的。。。返回true就行。

@Override
public boolean hasStableIds() {
    return true;
}

5)isChildSelectable()

     返回true就是表示可以操作子item。

@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}

6)重點在於getGroupView()和getChildView(),父子item的界面繪製、邏輯調用都在此完成

getGroupView():

首先是常規的尋找控件,設置複用。

@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
    GroupHolder groupHolder= null;
    if (convertView== null){
        groupHolder= new GroupHolder();
        convertView= LayoutInflater.from(context).inflate(R.layout.item_main_head, parent, false);
        convertView.setTag(groupHolder);
    }else {
        groupHolder= (GroupHolder) convertView.getTag();
    }

    return convertView;
}

class GroupHolder{
    RelativeLayout check; //選擇點擊框
    ImageView checkStatus; //選擇狀態圖片
    LinearLayout layout; //父item佈局
}

getChildView():

@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
    ChildHolder childHolder= null;
    if (convertView== null){
        childHolder= new ChildHolder();
        convertView= LayoutInflater.from(context).inflate(R.layout.item_main, parent, false);
        childHolder.check= convertView.findViewById(R.id.item_check);
        childHolder.checkStatus= convertView.findViewById(R.id.item_checkStatus);
        childHolder.goodsPic= convertView.findViewById(R.id.item_pic);
        childHolder.goodsName= convertView.findViewById(R.id.item_name);
        childHolder.goodsSpec= convertView.findViewById(R.id.item_spec);
        childHolder.goodsOriginalPrice= convertView.findViewById(R.id.item_price);
        childHolder.reduce= convertView.findViewById(R.id.item_reduce);
        childHolder.num= convertView.findViewById(R.id.item_num);
        childHolder.add= convertView.findViewById(R.id.item_add);
        convertView.setTag(childHolder);
    }else {
        childHolder= (ChildHolder) convertView.getTag();
    }
    childHolder.checkStatus.setImageResource(mainItemBeanList.get(groupPosition).get(childPosition).isSelect()? R.drawable.radio_choose: R.drawable.radio_normal_black);
    childHolder.goodsPic.setImageResource(mainItemBeanList.get(groupPosition).get(childPosition).getGoodsPic());
    childHolder.goodsName.setText(mainItemBeanList.get(groupPosition).get(childPosition).getGoodsName());
    childHolder.goodsSpec.setText(mainItemBeanList.get(groupPosition).get(childPosition).getGoodsSpec());
    childHolder.goodsOriginalPrice.setText("¥"+ mainItemBeanList.get(groupPosition).get(childPosition).getGoodsOriginalPrice());
    childHolder.num.setText(mainItemBeanList.get(groupPosition).get(childPosition).getGoodsNum());

    return convertView;
}

class ChildHolder{
    RelativeLayout check; //選擇點擊框
    ImageView checkStatus; //選擇狀態圖片
    ImageView goodsPic; //商品圖片
    TextView goodsName; //商品名稱
    TextView goodsSpec; //商品規格
    TextView goodsOriginalPrice; //商品原價
    TextView reduce; //加減器減號
    TextView num; //加減器數量
    TextView add; //加減器加號
}

7.新建一個MainActivity。

1)我們先配置控件。

/**
 * 配置控件
 */
private void initViews() {
    expandableListView = findViewById(R.id.main_expandableListView);
    smartRefreshLayout = findViewById(R.id.main_smartRefreshLayout);
}

2)配置適配器

/**
 * 配置適配器
 */
private void initAdapter() {
    adapter = new MainAdapter(MainActivity.this, mainBeanList, itemList);
    expandableListView.setAdapter(adapter);
}

3)最後配置數據

/**
 * 配置數據
 */
private void initData() {

    //商品測試數據
    MainBean.MainItemBean mainItemBean = new MainBean.MainItemBean(false, R.mipmap.ic_launcher, "iPhoneXs Max", "銀色 8G+256G", "9989", "1");
    MainBean.MainItemBean mainItemBean1 = new MainBean.MainItemBean(false, R.mipmap.ic_launcher, "華爲P20 Pro", "深空灰色 6G+128G", "5299", "1");
    MainBean.MainItemBean mainItemBean2 = new MainBean.MainItemBean(false, R.mipmap.ic_launcher, "小米9", "中國紅 8G+128G", "4999", "2");
    MainBean.MainItemBean mainItemBean3 = new MainBean.MainItemBean(false, R.mipmap.ic_launcher, "三星Galaxy S10+", "黑色 4G+64G", "9998", "1");
    MainBean.MainItemBean mainItemBean4 = new MainBean.MainItemBean(false, R.mipmap.ic_launcher, "魅族16th+", "黑色 4G+64G", "2698", "1");
    MainBean.MainItemBean mainItemBean5 = new MainBean.MainItemBean(false, R.mipmap.ic_launcher, "OPPO FINDX", "黑色 4G+64G", "2698", "1");

    List<MainBean.MainItemBean> mainItemBeanList = new ArrayList<>();
    mainItemBeanList.add(mainItemBean);
    mainItemBeanList.add(mainItemBean1);

    List<MainBean.MainItemBean> mainItemBeanList1 = new ArrayList<>();
    mainItemBeanList1.add(mainItemBean2);
    mainItemBeanList1.add(mainItemBean3);

    List<MainBean.MainItemBean> mainItemBeanList2 = new ArrayList<>();
    mainItemBeanList2.add(mainItemBean4);
    mainItemBeanList2.add(mainItemBean5);

    //店鋪測試數據
    MainBean mainBean = new MainBean(false, "天貓金牌賣家一號", mainItemBeanList);
    MainBean mainBean1 = new MainBean(false, "地汪銀牌賣家二號", mainItemBeanList);
    MainBean mainBean2 = new MainBean(false, "京西銅牌賣家三號", mainItemBeanList);

    //添加店鋪列表
    adapter.getMainList().clear();
    adapter.getMainList().add(mainBean);
    adapter.getMainList().add(mainBean1);
    adapter.getMainList().add(mainBean2);

    //添加商品佈局
    adapter.getMainItemList().clear();
    adapter.getMainItemList().add(mainItemBeanList);
    adapter.getMainItemList().add(mainItemBeanList1);
    adapter.getMainItemList().add(mainItemBeanList2);
    adapter.notifyDataSetChanged();

    //設置所有的子item都展開
    for (int i = 0; i < adapter.getMainList().size(); i++) {
        expandableListView.expandGroup(i);
    }

}

8.現在我們就可以把項目跑起來了,看看效果。

納尼?商品呢?

差點蒙圈了,這是ExpandableListView,默認是收縮的,需要點開才能看到子佈局

那麼問題來了。。。。

我們不可能讓用戶每次都逐條去點,這樣會造成糟糕的用戶體驗,而且父item佈局默認還有一個箭頭影響觀感,所以我們要做一些簡單的處理。

1)首先讓所有的子item都默認打開。

打開MainActivity, 在配置數據的方法initData()裏。

//設置所有的子item都展開
for (int i= 0 ;i< mainBeanList.size(); i++){
    expandableListView.expandGroup(i);
}

2)進入配置控件initViews()的方法裏, 再取消父item的默認箭頭

//去除父item的箭頭
expandableListView.setGroupIndicator(null);

3)最後,我們取消父item的點擊事件,避免誤觸後又摺疊起來。

打開adapter,在getGroupView()裏。

//取消父item的點擊事件
groupHolder.layout.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //什麼處理都不做
    }
});

現在再跑起來~~~

牛逼啊馬飛,大致雛形已經出來了,下來我們就慢慢搞內部邏輯了。

9.接下來,就先搞這個父item的選中吧

根據邏輯,當我們選中父item時,會改變當前店鋪的選中狀態,當爲選中狀態時,同樣會選中當前店鋪下的所有商品,反之則會取消選中當前店鋪下的所有商品,同時也會改變總價格的計算狀態。。。。哇暈菜了,我們慢慢一步步來~

1)先搞定點擊改變父item也就是店鋪的選中狀態。

進入adapter的getGroupView()方法,設置點擊監聽。

//店鋪的選中監聽
groupHolder.check.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mainBeanList.get(groupPosition).setSelect(!mainBeanList.get(groupPosition).isSelect());
        notifyDataSetChanged();
    }
});

這段代碼的意思就是,改變當前的select值爲相反值,然後刷新adapter。

看看效果:

點擊前:

點擊後:

再點擊:


完美!!!

2)下一步,我們來關聯點擊店鋪後順帶選擇對應下的商品列表。

for (int i = 0; i < mainItemBeanList.get(groupPosition).size(); i++) {
    mainItemBeanList.get(groupPosition).get(i).setSelect(mainBean.isSelect());
}

現在跑起來,點選某個店鋪。

成功!!!

10.搞完店鋪的點選,接下來我們就搞商品的點選了

先設置選中監聽事件。

childHolder.check.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MainBean.MainItemBean mainItemBean= mainItemBeanList.get(groupPosition).get(childPosition);
        mainItemBean.setSelect(!mainItemBean.isSelect());
        notifyDataSetChanged();
    }
});

同樣,當我們點選了某個店鋪下全部的商品,則把店鋪也自動點選,反之店鋪則不點選。

//當點選某個店鋪下全部商品時,則選中該店鋪
boolean noSelect= false;
for (int i= 0; i< mainItemBeanList.get(groupPosition).size(); i++){
    if (!mainItemBeanList.get(groupPosition).get(i).isSelect()){
        noSelect= true;
    }
}
mainBeanList.get(groupPosition).setSelect(!noSelect);

運行看下效果:

11.接下來,我們做下加減器。

這裏的原理就是,點擊商品相應的加減號,進行數量的增減。這裏我們只需要把當前的商品數量引入計算即可。

//加減器加號
final ChildHolder finalChildHolder = childHolder;
childHolder.add.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MainBean.MainItemBean mainItemBean = mainItemBeanList.get(groupPosition).get(childPosition);
        if (Integer.valueOf(mainItemBean.getGoodsNum()) < 5) {
            mainItemBean.setGoodsNum(Integer.valueOf(finalChildHolder.num.getText().toString()) + 1 + "");
            notifyDataSetChanged();
        }
    }
});

//加減器減號
childHolder.reduce.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MainBean.MainItemBean mainItemBean = mainItemBeanList.get(groupPosition).get(childPosition);
        if (Integer.valueOf(mainItemBean.getGoodsNum()) > 0) {
            mainItemBean.setGoodsNum(Integer.valueOf(finalChildHolder.num.getText().toString()) - 1 + "");
            notifyDataSetChanged();
        }
    }
});

這時我們要跟數量做一個限制,避免出現負數的現象,也可以做個最大數量限制,比如5。當商品數量達到極限值時,相應的按鈕變爲灰色並且不可點擊。

//加減器的極限值顯示狀態(達到最小值)
if ("0".equals(childHolder.num.getText().toString())) {
    childHolder.reduce.setTextColor(context.getResources().getColor(R.color.color_a1a1a1));
} else {
    childHolder.reduce.setTextColor(context.getResources().getColor(R.color.color_000000));
}

//加減器的極限值顯示狀態(達到最大值)
if ("5".equals(childHolder.num.getText().toString())) {
    childHolder.add.setTextColor(context.getResources().getColor(R.color.color_a1a1a1));
} else {
    childHolder.add.setTextColor(context.getResources().getColor(R.color.color_000000));
}

跑起來看看~

12.現在我們可以做一下價格計算了

首先是點選商品時的價格計算,因爲價格這裏涉及到精密計算,傳統的int float等計算起來不是那麼精密,所以我們這裏要引入BigDecimal,這是專門用來進行精密計算的類。

寫一個計算已勾選商品的價格計算的方法。

/**
 * 計算商品價格
 */
public String getAllPrice(List<MainBean> list){
    BigDecimal decimal= new BigDecimal("0");
    if (list!= null){
        for (int i= 0; i< list.size(); i++){
            for (int j= 0; j< mainItemBeanList.get(i).size(); j++){
                if (mainItemBeanList.get(i).get(j).isSelect()){
                    BigDecimal decimalGoodsNum= new BigDecimal(mainItemBeanList.get(i).get(j).getGoodsNum());
                    BigDecimal decimalGoodsPrice= new BigDecimal(mainItemBeanList.get(i).get(j).getGoodsOriginalPrice());
                    //將單價和數量相乘,累加
                    decimal= decimal.add(decimalGoodsNum.multiply(decimalGoodsPrice));
                }
            }
        }
    }
    return decimal.toString();
}

現在我們先通過點選多個商品來進行價格計算。

Log.d("fantasychong_aaa", getAllPrice(mainBeanList));

9989+5299=15288

9989+5299+4999=20287

沒錯吧,這時我們取消一個選中

此時價格已更改爲15288,沒錯吧。

取消全部的選中,價格歸爲0。

完美啦,現在我們再把價格計算的方法添加到店鋪點選上,並把最終計算的價格呈現在底部價格欄。

這裏我們使用EventBus來實時刷新價格。

1)首先在gradle裏添加依賴。

//EventBus
implementation 'org.simple:androideventbus:1.0.5.1'

2)配置環境

在MainAdapter裏註冊

public MainAdapter(List<MainBean> mainBeanList, List<List<MainBean.MainItemBean>> mainItemBeanList, Context context) {
    this.mainBeanList = mainBeanList;
    this.mainItemBeanList = mainItemBeanList;
    this.context = context;
    //註冊EventBus
    EventBus.getDefault().register(context);
}

在adapter裏取消註冊

這裏我們添加一個onDestroy()方法,最後在宿主Activity的onDestroy()方法裏執行即可。

public void onDestory(){
    //EventBus取消註冊
    EventBus.getDefault().unregister(context);
}

同樣在宿主MainActivity裏完成註冊與取消註冊。

/**
 * 配置控件
 */
private void initViews() {
    .....
    //EventBus註冊
    EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
    super.onDestroy();
    .....
    //EventBus取消註冊
    EventBus.getDefault().unregister(this);
}

3)配置完環境,我們打開adapter,在價格計算的入口發送實時價格。

分析下入口,共有4處分別爲

1.點選店鋪

2.點選商品

3.加減器加號

4.加減器減號

分別在入口通過EventBus發送價格。

//更改商品價格
EventBus.getDefault().post(getAllPrice(mainBeanList), TAG_REFRESH_PRICE);

4)打開宿主MainActivity,編寫接收方法並顯示價格。

/**
 * 更改價格
 * @param price
 */
@Subscriber(tag = TAG_REFRESH_PRICE, mode = ThreadMode.MAIN)
public void onRefreshPrice(String price){
    priceTv.setText("總價格:"+ price);
}

現在跑起來,我們看下效果:

成功!

13.同時我們再顯示一下所選商品的數量

寫一個計算數量的方法getAllNum()。

/**
 * 計算商品數量
 */
public String getAllNum(List<MainBean> list){
    selectList= new ArrayList<>();
    int allNum= 0;
    if (list!= null){
        for (int i= 0; i< list.size(); i++){
            for (int j= 0; j< mainItemBeanList.get(i).size(); j++){
                if (mainItemBeanList.get(i).get(j).isSelect()){
                    int num= 1* Integer.valueOf(mainItemBeanList.get(i).get(j).getGoodsNum());
                    allNum= allNum+ num;
                }
            }
        }
    }
    return String.valueOf(allNum);
}

在店鋪點選,商品點選,加減器加減處添加方法。

//更改所選商品數量
EventBus.getDefault().post(getAllNum(mainBeanList), TAG_REFRESH_NUM);

在宿主MainActivity裏添加顯示方法。

/**
 * 更改數量
 */
@Subscriber(tag = TAG_REFRESH_NUM, mode = ThreadMode.MAIN)
public void onRefreshNum(String num){
    submitTv.setText("結算("+ num+ ")");
}

跑起來,看看效果:

成功!!!

14.現在我們做一下商品的刪除處理。

要想刪除,必須點擊右上角的編輯按鈕,下方結算按鈕會隨之顯示 “刪除”,點擊即可刪除。

@Override
public void onClick(View v) {
    switch (v.getId()){
        case R.id.main_edit:
            if ("結算".equals(submitTv.getText().toString())){
                submitTv.setText("刪除");
                edit.setText("完成");
            }else{
                submitTv.setText("結算");
                edit.setText("編輯");
            }
            break;
        default:
            break;
    }
}

當顯示”刪除“時,編寫對應的方法。

/**
 * 刪除所選商品
 */
public void removeGoods() {
    for (int i = 0; i < mainBeanList.size(); i++) {
        if (mainBeanList.get(i).isSelect()) {
            mainBeanList.remove(i);
            mainItemBeanList.remove(i);
        } else {
            for (int j = 0; j < mainItemBeanList.get(i).size(); j++) {
                if (mainItemBeanList.get(i).get(j).isSelect()) {
                    mainItemBeanList.get(i).remove(j);
                }
            }
        }
    }
    notifyDataSetChanged();
}

這段代碼意思是,當選中店鋪(選中了該店鋪下的所有商品)時,清除掉對應的店鋪和商品list,別忘了最後notifyDataSetChanged()

運行看下效果:

選中一個商品、刪除。

選中一個店鋪,刪除。

同時清除底部的價格和數量

//更改商品價格
EventBus.getDefault().post(getAllPrice(mainBeanList), TAG_REFRESH_PRICE);
//更改所選商品數量
EventBus.getDefault().post(getAllNum(mainBeanList), TAG_REFRESH_NUM);

完美!

15.現在我們做一下結算功能

結算的思路就是把已選中的商品放置在一個集合中,之後根據需求對該集合進行相應的處理。

在adapter裏寫一個獲取選中商品list的方法。

/**
 * 獲取所選商品
 */
public List<MainBean.MainItemBean> getSelectList(){
    selectList= new ArrayList<>();
    for (int i= 0; i< mainBeanList.size(); i++){
        for (int j= 0; j< mainItemBeanList.get(i).size(); j++){
            if (mainItemBeanList.get(i).get(j).isSelect()){
                selectList.add(mainItemBeanList.get(i).get(j));
            }
        }
    }
    return selectList;
}

在結算入口打印日誌

if ("結算".equals(submitTv.getText().toString())){
    Log.d("fantasychong_selctList", adapter.getSelectList().toString());
}

運行,看效果:

這樣就對應上了。

16.現在我們已經把基本功能全都完成了,最後做下下拉刷新處理。

首先實現下拉刷新監聽請求數據。

//下拉刷新監聽
smartRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
    @Override
    public void onRefresh(RefreshLayout refreshLayout) {
        initData();
    }
});

這時,當我們選擇了某些商品後,下拉刷新結果發現

納尼?選中的商品怎麼又不選中了?

所以,我們這裏要做下選中狀態的保存。

這裏我們需要給每個商品做一個唯一標識符id,打開MainItemBean,添加id參數。

private String id; //商品id
public String getId() {
    return id;
}

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

同時構造函數也添加上該參數

這時在請求新數據initData()方法中,在添加數據之前,先把舊數據的商品選中狀態保存到新數據中。

//保存選中狀態
List<SelectBean> selectBeanList = new ArrayList<>();
if (adapter.getMainItemList().size() != 0 && adapter.getMainItemList() != null) {
    for (int i = 0; i < adapter.getMainList().size(); i++) {
        for (int j = 0; j < adapter.getMainItemList().get(i).size(); j++) {
            if (adapter.getMainItemList().get(i).get(j).isSelect()) {
                selectBean = new SelectBean(adapter.getMainItemList().get(i).get(j).getId(), adapter.getMainItemList().get(i).get(j).isSelect());
                selectBeanList.add(selectBean);
            }
        }
    }

    for (int i = 0; i < mainItemBeanList.size(); i++) {
        for (int j = 0; j < selectBeanList.size(); j++) {
            if (selectBeanList.get(j).getId().equals(mainItemBeanList.get(i).getId())) {
                mainItemBeanList.get(i).setSelect(selectBeanList.get(j).isSelect());
            }
        }
    }
}

這時把保存的商品選中狀態賦給新數據。

//將保存的選中狀態賦給新請求到的數據
if (adapter.getMainItemList().size() != 0 && adapter.getMainItemList() != null) {
    for (int i = 0; i < adapter.getMainList().size(); i++) {
        for (int j = 0; j < adapter.getMainItemList().get(i).size(); j++) {
            if (adapter.getMainItemList().get(i).get(j).isSelect()) {
                selectBean = new SelectBean(adapter.getMainItemList().get(i).get(j).getId(), adapter.getMainItemList().get(i).get(j).isSelect());
                selectBeanList.add(selectBean);
            }
        }
    }
    for (int i = 0; i < mainItemBeanList.size(); i++) {
        for (int j = 0; j < selectBeanList.size(); j++) {
            if (selectBeanList.get(j).getId().equals(mainItemBeanList.get(i).getId())) {
                mainItemBeanList.get(i).setSelect(selectBeanList.get(j).isSelect());
            }
        }
    }
    for (int i = 0; i < mainItemBeanList.size(); i++) {
        for (int j = 0; j < selectBeanList.size(); j++) {
            if (selectBeanList.get(j).getId().equals(mainItemBeanList1.get(i).getId())) {
                mainItemBeanList1.get(i).setSelect(selectBeanList.get(j).isSelect());
            }
        }
    }
    for (int i = 0; i < mainItemBeanList.size(); i++) {
        for (int j = 0; j < selectBeanList.size(); j++) {
            if (selectBeanList.get(j).getId().equals(mainItemBeanList2.get(i).getId())) {
                mainItemBeanList2.get(i).setSelect(selectBeanList.get(j).isSelect());
            }
        }
    }
}

最後根據商品的選中狀態來決定對應店鋪的選中狀態。

//根據子item的選中狀態來決定店鋪的點選狀態
boolean noSelect = false;
for (int j = 0; j < adapter.getMainItemList().get(0).size(); j++) {
    if (!adapter.getMainItemList().get(0).get(j).isSelect()) {
        noSelect = true;
    }
    adapter.getMainList().get(0).setSelect(!noSelect);
}

boolean noSelect1 = false;
for (int j = 0; j < adapter.getMainItemList().get(1).size(); j++) {
    if (!adapter.getMainItemList().get(1).get(j).isSelect()) {
        noSelect1 = true;
    }
    adapter.getMainList().get(1).setSelect(!noSelect1);
}

boolean noSelect2 = false;
for (int j = 0; j < adapter.getMainItemList().get(2).size(); j++) {
    if (!adapter.getMainItemList().get(2).get(j).isSelect()) {
        noSelect2 = true;
    }
    adapter.getMainList().get(2).setSelect(!noSelect2);
}

最後別忘了通知adapter刷新。

adapter.notifyDataSetChanged();

 

至此全部完成,沒有demo的博客不是好博客,鏈接附上!

資源下載

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章