Android 购物车的最佳实现(附源码)(通过ExpandableListView实现分店铺)

        购物车是IT电商项目必不可少的功能之一,尽管该功能不算很复杂,但是却很重要。实现的方式也多种多样,就我而言就曾经通过三种方式开发过购物车:第一种方式是通过LinearLayout嵌套LinearLayout实现,这种方式会消耗更多的内存,数据过多时会造成页面的卡顿,还可能产生OOM异常,不可取;第二种方式是通过ListView嵌套LinearLayout实现,这种方式与第一种大同小异,提升效率有限;第三种方式(也是目前我认为的最佳方式)是通过ExpandableListView实现购物车分店铺功能,由于 ExpandableListView是系统原生控件,因此由系统底层维护并提供了更多的方法供我们使用,操作简单、页面流畅而且代码量较前两种也小了很多。
        虽然第一、二中方式现在看起来很不友好,但当时也是我逐步摸索的实践过程,对第三种方式的完成提供了必不可少的贡献。当然,购物车的实现方式多种多样,应该还有更好的实现方式,这篇博客就当作抛砖引玉了,希望大家前来学习交流、批评指正、共同成长。
        注:文章末尾附项目源码下载链接。

        页面布局文件

        Android开发,页面的美观、流畅与用户体验及其重要,因此开发购物车功能的第一步就是布局文件。购物车的主要功能包括:单选、全选、合计、删除、商品数量的加减,为了方便测试又新增了刷新数据功能,先看下页面效果(动图)。实际开发中,美观的页面需要UI的设计与我们的开发,这一点很重要!

         以下为购物车的页面布局文件代码:该文件中使用的是ExpandableListView原生控件,来实现购物车的分店铺。实际开发中,如果遇到ScrollView嵌套ExpandableListView控件,可以使用自定义控件ExpandableListViewForScrollView 解决ScrollView嵌套ExpandableListView,ExpandableListView显示不全的问题。
        注:文章末尾附ExpandableListViewFroScrollView源码。
<?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="match_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ededed"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:background="#ffffff"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_titlebar_left"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@null"
                android:gravity="center"
                android:paddingLeft="13dp"
                android:paddingRight="13dp"
                android:singleLine="true"
                android:text="刷新数据"
                android:textColor="#4c4c4c"
                android:textSize="15dp"
                android:visibility="visible" />

            <TextView
                android:id="@+id/tv_titlebar_center"
                android:layout_width="200dp"
                android:layout_height="match_parent"
                android:layout_centerHorizontal="true"
                android:ellipsize="end"
                android:gravity="center"
                android:maxLength="18"
                android:singleLine="true"
                android:text="购物车"
                android:textColor="#1a1a1a"
                android:textSize="17dp"
                android:visibility="visible" />

            <TextView
                android:id="@+id/tv_titlebar_right"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_alignParentRight="true"
                android:background="@null"
                android:gravity="center"
                android:paddingLeft="13dp"
                android:paddingRight="13dp"
                android:singleLine="true"
                android:text="编辑"
                android:textColor="#4c4c4c"
                android:textSize="15dp"
                android:visibility="gone" />

            <View
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:layout_alignParentBottom="true"
                android:background="#cccccc" />
        </RelativeLayout>

        <ExpandableListView
            android:id="@+id/elv_shopping_car"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#ededed"
            android:divider="@null"
            android:groupIndicator="@null"
            android:scrollbars="none"
            android:visibility="gone" />

        <RelativeLayout
            android:id="@+id/rl"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="#ffffff"
            android:visibility="gone">

            <LinearLayout
                android:id="@+id/ll_select_all"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:orientation="horizontal"
                android:paddingRight="10dp">

                <ImageView
                    android:id="@+id/iv_select_all"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="10dp"
                    android:background="@mipmap/unselect" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginLeft="6dp"
                    android:text="全选"
                    android:textColor="#333333"
                    android:textSize="14dp" />
            </LinearLayout>

            <Button
                android:id="@+id/btn_order"
                android:layout_width="125dp"
                android:layout_height="match_parent"
                android:layout_alignParentRight="true"
                android:background="#ee1d23"
                android:text="去结算"
                android:textColor="#ffffff"
                android:textSize="16dp"
                android:visibility="visible" />

            <Button
                android:id="@+id/btn_delete"
                android:layout_width="125dp"
                android:layout_height="match_parent"
                android:layout_alignParentRight="true"
                android:background="#ee1d23"
                android:text="删除"
                android:textColor="#ffffff"
                android:textSize="16dp"
                android:visibility="gone" />

            <RelativeLayout
                android:id="@+id/rl_total_price"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_toLeftOf="@id/btn_order"
                android:layout_toRightOf="@id/ll_select_all">

                <TextView
                    android:id="@+id/tv_total_price"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_centerVertical="true"
                    android:layout_marginLeft="2dp"
                    android:layout_marginRight="10dp"
                    android:maxLength="12"
                    android:singleLine="true"
                    android:text="¥0.00"
                    android:textColor="#333333"
                    android:textSize="15dp" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_marginLeft="20dp"
                    android:layout_toLeftOf="@id/tv_total_price"
                    android:text="合计:"
                    android:textColor="#333333"
                    android:textSize="15dp" />
            </RelativeLayout>

            <View
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:background="#cccccc" />
        </RelativeLayout>
    </LinearLayout>

    <RelativeLayout
        android:id="@+id/rl_no_contant"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:visibility="gone">

        <ImageView
            android:id="@+id/iv_no_contant"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerHorizontal="true"
            android:scaleType="centerCrop"
            android:src="@mipmap/home_tab_no_data" />

        <TextView
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:layout_below="@+id/iv_no_contant"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:text="购物车竟然是空的,快去购买书籍充实自己吧!"
            android:textColor="#808080"
            android:textSize="16dp" />
    </RelativeLayout>
</RelativeLayout>

        初始化数据(含Bean类)

         在实际开发中,通过请求后台接口获取购物车数据并解析( 为了方便测试,在本地模拟了购物车数据),定义了Bean类ShoppingCarDataBean,然后使用Gson按照Bean类的格式解析数据,最后填充到ExpandableListView中使用(详见下方初始化ExpandableListView的数据)。
        以下为初始化数据的代码:在此解析数据,并填充到 ExpandableListView中使用。
/**
 * 初始化数据
 */
private void initData() {
    //使用Gson解析购物车数据,
    //ShoppingCarDataBean为bean类,Gson按照bean类的格式解析数据
    /**
     * 实际开发中,通过请求后台接口获取购物车数据并解析
     */
    Gson gson = new Gson();
    ShoppingCarDataBean shoppingCarDataBean = gson.fromJson(shoppingCarData, ShoppingCarDataBean.class);
    datas = shoppingCarDataBean.getDatas();

    initExpandableListViewData(datas);
}
         以下为ShoppingCarDataBean的代码:在bean类的DatasBean中新增了isSelect_shop属性,用来判断店铺是否被选中;在GoodsBean中新增了isSelect属性,用来判断商品是否被选中。正是通过这两个属性,完成了单选、全选、删除、合计以及后续的去结算操作,不仅大大的减少了代码量,还精简了业务逻辑、保证了数据的精确。所以,越是复杂的功能,Bean类的作用也就越重要,大家不要忽略它的作用。
package com.wangyang.shoppingcarbestimplementation.bean;

import java.util.List;

/**
 * 购物车数据的bean类
 */

public class ShoppingCarDataBean {

    private int code;
    private List<DatasBean> datas;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public List<DatasBean> getDatas() {
        return datas;
    }

    public void setDatas(List<DatasBean> datas) {
        this.datas = datas;
    }

    public static class DatasBean {

        private String store_id;
        private String store_name;
        private boolean isSelect_shop;      //店铺是否在购物车中被选中
        private List<GoodsBean> goods;

        public boolean getIsSelect_shop() {
            return isSelect_shop;
        }

        public void setIsSelect_shop(boolean select_shop) {
            isSelect_shop = select_shop;
        }

        public String getStore_id() {
            return store_id;
        }

        public void setStore_id(String store_id) {
            this.store_id = store_id;
        }

        public String getStore_name() {
            return store_name;
        }

        public void setStore_name(String store_name) {
            this.store_name = store_name;
        }

        public List<GoodsBean> getGoods() {
            return goods;
        }

        public void setGoods(List<GoodsBean> goods) {
            this.goods = goods;
        }

        public static class GoodsBean {

            private String goods_id;
            private String goods_image;
            private String goods_name;
            private String goods_num;
            private String goods_price;
            private boolean isSelect;        //商品是否在购物车中被选中

            public boolean getIsSelect() {
                return isSelect;
            }

            public void setIsSelect(boolean isSelect) {
                this.isSelect = isSelect;
            }

            public String getGoods_id() {
                return goods_id;
            }

            public void setGoods_id(String goods_id) {
                this.goods_id = goods_id;
            }

            public String getGoods_image() {
                return goods_image;
            }

            public void setGoods_image(String goods_image) {
                this.goods_image = goods_image;
            }

            public String getGoods_name() {
                return goods_name;
            }

            public void setGoods_name(String goods_name) {
                this.goods_name = goods_name;
            }

            public String getGoods_num() {
                return goods_num;
            }

            public void setGoods_num(String goods_num) {
                this.goods_num = goods_num;
            }

            public String getGoods_price() {
                return goods_price;
            }

            public void setGoods_price(String goods_price) {
                this.goods_price = goods_price;
            }
        }
    }
}

        初始化ExpandableListView

        页面布局开发完成后,接下来就需要通过代码来实现业务逻辑了,首先就需要初始化 ExpandableListView控件,为其创建数据适配器并进行初始化操作。因为使用的是ExpandableListView,所以adapter继承BaseExpandableListAdapter,并实现其未实现的方法。
        以下为初始化ExpandableListView的代码:在此对删除与修改商品数量的回调进行了处理。
/**
 * 初始化ExpandableListView
 * 创建数据适配器adapter,并进行初始化操作
 */
private void initExpandableListView() {
    shoppingCarAdapter = new ShoppingCarAdapter(context, llSelectAll, ivSelectAll, btnOrder, btnDelete, rlTotalPrice, tvTotalPrice);
    elvShoppingCar.setAdapter(shoppingCarAdapter);

    //删除的回调
    shoppingCarAdapter.setOnDeleteListener(new ShoppingCarAdapter.OnDeleteListener() {
        @Override
        public void onDelete() {
            initDelete();
            /**
             * 实际开发中,在此请求删除接口,删除成功后,
             * 通过initExpandableListViewData()方法刷新购物车数据。
             * 注:通过bean类中的DatasBean的isSelect_shop属性,判断店铺是否被选中;
             *                  GoodsBean的isSelect属性,判断商品是否被选中,
             *                  (true为选中,false为未选中)
             */
        }
    });

    //修改商品数量的回调
    shoppingCarAdapter.setOnChangeCountListener(new ShoppingCarAdapter.OnChangeCountListener() {
        @Override
        public void onChangeCount(String goods_id) {
            /**
             * 实际开发中,在此请求修改商品数量的接口,商品数量修改成功后,
             * 通过initExpandableListViewData()方法刷新购物车数据。
             */
        }
    });
}
         以下为购物车的数据适配器ShoppingCarAdapter的代码:在此对单选、全选、合计、商品数量加减(本地)以及数据的刷新进行了处理,并定义了删除、商品数量加减回调接口。代码中已经给出了较为详细的注释,在这里就不再分段解释了,如果有不明白的地方,评论联系我即可。
package com.wangyang.shoppingcarbestimplementation.adapter;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.wangyang.shoppingcarbestimplementation.R;
import com.wangyang.shoppingcarbestimplementation.bean.ShoppingCarDataBean;
import com.wangyang.shoppingcarbestimplementation.util.ToastUtil;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import butterknife.ButterKnife;
import butterknife.InjectView;

/**
 * 购物车的adapter
 * 因为使用的是ExpandableListView,所以继承BaseExpandableListAdapter
 */
public class ShoppingCarAdapter extends BaseExpandableListAdapter {

    private final Context context;
    private final LinearLayout llSelectAll;
    private final ImageView ivSelectAll;
    private final Button btnOrder;
    private final Button btnDelete;
    private final RelativeLayout rlTotalPrice;
    private final TextView tvTotalPrice;
    private List<ShoppingCarDataBean.DatasBean> data;
    private boolean isSelectAll = false;
    private double total_price;

    public ShoppingCarAdapter(Context context, LinearLayout llSelectAll,
                              ImageView ivSelectAll, Button btnOrder, Button btnDelete,
                              RelativeLayout rlTotalPrice, TextView tvTotalPrice) {
        this.context = context;
        this.llSelectAll = llSelectAll;
        this.ivSelectAll = ivSelectAll;
        this.btnOrder = btnOrder;
        this.btnDelete = btnDelete;
        this.rlTotalPrice = rlTotalPrice;
        this.tvTotalPrice = tvTotalPrice;
    }

    /**
     * 自定义设置数据方法;
     * 通过notifyDataSetChanged()刷新数据,可保持当前位置
     *
     * @param data 需要刷新的数据
     */
    public void setData(List<ShoppingCarDataBean.DatasBean> data) {
        this.data = data;
        notifyDataSetChanged();
    }

    @Override
    public int getGroupCount() {
        if (data != null && data.size() > 0) {
            return data.size();
        } else {
            return 0;
        }
    }

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

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

    @Override
    public View getGroupView(final int groupPosition, final boolean isExpanded, View convertView, ViewGroup parent) {
        GroupViewHolder groupViewHolder;
        if (convertView == null) {
            convertView = View.inflate(context, R.layout.item_shopping_car_group, null);

            groupViewHolder = new GroupViewHolder(convertView);
            convertView.setTag(groupViewHolder);
        } else {
            groupViewHolder = (GroupViewHolder) convertView.getTag();
        }
        final ShoppingCarDataBean.DatasBean datasBean = data.get(groupPosition);
        //店铺ID
        String store_id = datasBean.getStore_id();
        //店铺名称
        String store_name = datasBean.getStore_name();

        if (store_name != null) {
            groupViewHolder.tvStoreName.setText(store_name);
        } else {
            groupViewHolder.tvStoreName.setText("");
        }

        //店铺内的商品都选中的时候,店铺的也要选中
        for (int i = 0; i < datasBean.getGoods().size(); i++) {
            ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = datasBean.getGoods().get(i);
            boolean isSelect = goodsBean.getIsSelect();
            if (isSelect) {
                datasBean.setIsSelect_shop(true);
            } else {
                datasBean.setIsSelect_shop(false);
                break;
            }
        }

        //因为set之后要重新get,所以这一块代码要放到一起执行
        //店铺是否在购物车中被选中
        final boolean isSelect_shop = datasBean.getIsSelect_shop();
        if (isSelect_shop) {
            groupViewHolder.ivSelect.setImageResource(R.mipmap.select);
        } else {
            groupViewHolder.ivSelect.setImageResource(R.mipmap.unselect);
        }

        //店铺选择框的点击事件
        groupViewHolder.ll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                datasBean.setIsSelect_shop(!isSelect_shop);

                List<ShoppingCarDataBean.DatasBean.GoodsBean> goods = datasBean.getGoods();
                for (int i = 0; i < goods.size(); i++) {
                    ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = goods.get(i);
                    goodsBean.setIsSelect(!isSelect_shop);
                }
                notifyDataSetChanged();
            }
        });

        //当所有的选择框都是选中的时候,全选也要选中
        w:
        for (int i = 0; i < data.size(); i++) {
            List<ShoppingCarDataBean.DatasBean.GoodsBean> goods = data.get(i).getGoods();
            for (int y = 0; y < goods.size(); y++) {
                ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = goods.get(y);
                boolean isSelect = goodsBean.getIsSelect();
                if (isSelect) {
                    isSelectAll = true;
                } else {
                    isSelectAll = false;
                    break w;//根据标记,跳出嵌套循环
                }
            }
        }
        if (isSelectAll) {
            ivSelectAll.setBackgroundResource(R.mipmap.select);
        } else {
            ivSelectAll.setBackgroundResource(R.mipmap.unselect);
        }

        //全选的点击事件
        llSelectAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isSelectAll = !isSelectAll;

                if (isSelectAll) {
                    for (int i = 0; i < data.size(); i++) {
                        List<ShoppingCarDataBean.DatasBean.GoodsBean> goods = data.get(i).getGoods();
                        for (int y = 0; y < goods.size(); y++) {
                            ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = goods.get(y);
                            goodsBean.setIsSelect(true);
                        }
                    }
                } else {
                    for (int i = 0; i < data.size(); i++) {
                        List<ShoppingCarDataBean.DatasBean.GoodsBean> goods = data.get(i).getGoods();
                        for (int y = 0; y < goods.size(); y++) {
                            ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = goods.get(y);
                            goodsBean.setIsSelect(false);
                        }
                    }
                }
                notifyDataSetChanged();
            }
        });

        //合计的计算
        total_price = 0.0;
        tvTotalPrice.setText("¥0.00");
        for (int i = 0; i < data.size(); i++) {
            List<ShoppingCarDataBean.DatasBean.GoodsBean> goods = data.get(i).getGoods();
            for (int y = 0; y < goods.size(); y++) {
                ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = goods.get(y);
                boolean isSelect = goodsBean.getIsSelect();
                if (isSelect) {
                    String num = goodsBean.getGoods_num();
                    String price = goodsBean.getGoods_price();

                    double v = Double.parseDouble(num);
                    double v1 = Double.parseDouble(price);

                    total_price = total_price + v * v1;

                    //让Double类型完整显示,不用科学计数法显示大写字母E
                    DecimalFormat decimalFormat = new DecimalFormat("0.00");
                    tvTotalPrice.setText("¥" + decimalFormat.format(total_price));
                }
            }
        }

        //去结算的点击事件
        btnOrder.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //创建临时的List,用于存储被选中的商品
                List<ShoppingCarDataBean.DatasBean.GoodsBean> temp = new ArrayList<>();
                for (int i = 0; i < data.size(); i++) {
                    List<ShoppingCarDataBean.DatasBean.GoodsBean> goods = data.get(i).getGoods();
                    for (int y = 0; y < goods.size(); y++) {
                        ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = goods.get(y);
                        boolean isSelect = goodsBean.getIsSelect();
                        if (isSelect) {
                            temp.add(goodsBean);
                        }
                    }
                }

                if (temp != null && temp.size() > 0) {//如果有被选中的
                    /**
                     * 实际开发中,如果有被选中的商品,
                     * 则跳转到确认订单页面,完成后续订单流程。
                     */
                    ToastUtil.makeText(context, "跳转到确认订单页面,完成后续订单流程");
                } else {
                    ToastUtil.makeText(context, "请选择要购买的商品");
                }
            }
        });

        //删除的点击事件
        btnDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 * 实际开发中,通过回调请求后台接口实现删除操作
                 */
                if (mDeleteListener != null) {
                    mDeleteListener.onDelete();
                }
            }
        });

        return convertView;
    }

    static class GroupViewHolder {
        @InjectView(R.id.iv_select)
        ImageView ivSelect;
        @InjectView(R.id.tv_store_name)
        TextView tvStoreName;
        @InjectView(R.id.ll)
        LinearLayout ll;

        GroupViewHolder(View view) {
            ButterKnife.inject(this, view);
        }
    }

    //------------------------------------------------------------------------------------------------
    @Override
    public int getChildrenCount(int groupPosition) {
        if (data.get(groupPosition).getGoods() != null && data.get(groupPosition).getGoods().size() > 0) {
            return data.get(groupPosition).getGoods().size();
        } else {
            return 0;
        }
    }

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

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

    @Override
    public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ChildViewHolder childViewHolder;
        if (convertView == null) {
            convertView = View.inflate(context, R.layout.item_shopping_car_child, null);

            childViewHolder = new ChildViewHolder(convertView);
            convertView.setTag(childViewHolder);
        } else {
            childViewHolder = (ChildViewHolder) convertView.getTag();
        }
        final ShoppingCarDataBean.DatasBean datasBean = data.get(groupPosition);
        //店铺ID
        String store_id = datasBean.getStore_id();
        //店铺名称
        String store_name = datasBean.getStore_name();
        //店铺是否在购物车中被选中
        final boolean isSelect_shop = datasBean.getIsSelect_shop();
        final ShoppingCarDataBean.DatasBean.GoodsBean goodsBean = datasBean.getGoods().get(childPosition);
        //商品图片
        String goods_image = goodsBean.getGoods_image();
        //商品ID
        final String goods_id = goodsBean.getGoods_id();
        //商品名称
        String goods_name = goodsBean.getGoods_name();
        //商品价格
        String goods_price = goodsBean.getGoods_price();
        //商品数量
        String goods_num = goodsBean.getGoods_num();
        //商品是否被选中
        final boolean isSelect = goodsBean.getIsSelect();

        Glide.with(context)
                .load(goods_image)
                .into(childViewHolder.ivPhoto);
        if (goods_name != null) {
            childViewHolder.tvName.setText(goods_name);
        } else {
            childViewHolder.tvName.setText("");
        }
        if (goods_price != null) {
            childViewHolder.tvPriceValue.setText(goods_price);
        } else {
            childViewHolder.tvPriceValue.setText("");
        }
        if (goods_num != null) {
            childViewHolder.tvEditBuyNumber.setText(goods_num);
        } else {
            childViewHolder.tvEditBuyNumber.setText("");
        }

        //商品是否被选中
        if (isSelect) {
            childViewHolder.ivSelect.setImageResource(R.mipmap.select);
        } else {
            childViewHolder.ivSelect.setImageResource(R.mipmap.unselect);
        }

        //商品选择框的点击事件
        childViewHolder.ivSelect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                goodsBean.setIsSelect(!isSelect);
                if (!isSelect == false) {
                    datasBean.setIsSelect_shop(false);
                }
                notifyDataSetChanged();
            }
        });

        //加号的点击事件
        childViewHolder.ivEditAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //模拟加号操作
                String num = goodsBean.getGoods_num();
                Integer integer = Integer.valueOf(num);
                integer++;
                goodsBean.setGoods_num(integer + "");
                notifyDataSetChanged();

                /**
                 * 实际开发中,通过回调请求后台接口实现数量的加减
                 */
                if (mChangeCountListener != null) {
                    mChangeCountListener.onChangeCount(goods_id);
                }
            }
        });
        //减号的点击事件
        childViewHolder.ivEditSubtract.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //模拟减号操作
                String num = goodsBean.getGoods_num();
                Integer integer = Integer.valueOf(num);
                if (integer > 1) {
                    integer--;
                    goodsBean.setGoods_num(integer + "");

                    /**
                     * 实际开发中,通过回调请求后台接口实现数量的加减
                     */
                    if (mChangeCountListener != null) {
                        mChangeCountListener.onChangeCount(goods_id);
                    }
                } else {
                    ToastUtil.makeText(context, "商品不能再减少了");
                }
                notifyDataSetChanged();
            }
        });

        if (childPosition == data.get(groupPosition).getGoods().size() - 1) {
            childViewHolder.view.setVisibility(View.GONE);
            childViewHolder.viewLast.setVisibility(View.VISIBLE);
        } else {
            childViewHolder.view.setVisibility(View.VISIBLE);
            childViewHolder.viewLast.setVisibility(View.GONE);
        }

        return convertView;
    }

    static class ChildViewHolder {
        @InjectView(R.id.iv_select)
        ImageView ivSelect;
        @InjectView(R.id.iv_photo)
        ImageView ivPhoto;
        @InjectView(R.id.tv_name)
        TextView tvName;
        @InjectView(R.id.tv_price_key)
        TextView tvPriceKey;
        @InjectView(R.id.tv_price_value)
        TextView tvPriceValue;
        @InjectView(R.id.iv_edit_subtract)
        ImageView ivEditSubtract;
        @InjectView(R.id.tv_edit_buy_number)
        TextView tvEditBuyNumber;
        @InjectView(R.id.iv_edit_add)
        ImageView ivEditAdd;
        @InjectView(R.id.view)
        View view;
        @InjectView(R.id.view_last)
        View viewLast;

        ChildViewHolder(View view) {
            ButterKnife.inject(this, view);
        }
    }

    //-----------------------------------------------------------------------------------------------

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

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

    //删除的回调
    public interface OnDeleteListener {
        void onDelete();
    }

    public void setOnDeleteListener(OnDeleteListener listener) {
        mDeleteListener = listener;
    }

    private OnDeleteListener mDeleteListener;

    //修改商品数量的回调
    public interface OnChangeCountListener {
        void onChangeCount(String goods_id);
    }

    public void setOnChangeCountListener(OnChangeCountListener listener) {
        mChangeCountListener = listener;
    }

    private OnChangeCountListener mChangeCountListener;
    

        初始化ExpandableListView的数据

        数据和ExpandableListView的初始化操作完成后,接下来就是将数据填充到ExpandableListView中使用。同时在数据刷新的时候,通过此方法不仅可以刷新页面数据,还能保证页面保持在当前位置,提升用户体验。
        以下为初始化ExpandableListView数据的代码:在此初始化了ExpandableListView的数据,并保证数据刷新时,页面保持当前位置。同时也使ExpandableListView的所有组展开,并点击无效果。
/**
 * 初始化ExpandableListView的数据
 * 并在数据刷新时,页面保持当前位置
 *
 * @param datas 购物车的数据
 */
private void initExpandableListViewData(List<ShoppingCarDataBean.DatasBean> datas) {
    if (datas != null && datas.size() > 0) {
        //刷新数据时,保持当前位置
        shoppingCarAdapter.setData(datas);

        //使所有组展开
        for (int i = 0; i < shoppingCarAdapter.getGroupCount(); i++) {
            elvShoppingCar.expandGroup(i);
        }

        //使组点击无效果
        elvShoppingCar.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

            @Override
            public boolean onGroupClick(ExpandableListView parent, View v,
                                        int groupPosition, long id) {
                return true;
            }
        });

        tvTitlebarRight.setVisibility(View.VISIBLE);
        tvTitlebarRight.setText("编辑");
        rlNoContant.setVisibility(View.GONE);
        elvShoppingCar.setVisibility(View.VISIBLE);
        rl.setVisibility(View.VISIBLE);
        rlTotalPrice.setVisibility(View.VISIBLE);
        btnOrder.setVisibility(View.VISIBLE);
        btnDelete.setVisibility(View.GONE);
    } else {
        tvTitlebarRight.setVisibility(View.GONE);
        rlNoContant.setVisibility(View.VISIBLE);
        elvShoppingCar.setVisibility(View.GONE);
        rl.setVisibility(View.GONE);
    }
}       

        小结

        以上就是购物车的主要功能,另外本地删除功能、圆角删除dialog以及商品数量的加减在源码中也有详细的注释,在这里就不一一赘述了,实际开发中还是需要通过接口完成删除与商品数量的加减等功能,保持前后台数据的及时性与一致性。由于图片加载使用了Glide框架,所以需要在AndroidManifest清单文件中添加INTERNET访问网络的权限。
        Android Studio已经发布到3.0以上了,但是本项目时间较老了,使用的gradle还是2.2,版本不同的需要修改下。也可以将java文件、资源文件copy到新创建的项目中使用,都很方便。

        附:

        GitHub项目地址
        圆角的dialog

猜你喜欢

转载自blog.csdn.net/qq941263013/article/details/80901277