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的博客不是好博客,链接附上!

资源下载

 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值