购物车(上)

购物车里面涉及到的逻辑以及相关技术点还是比较多的,那么我们也没必要着急,一步步来,今天我们就一起来聊一下购物车的相关操作,
在这里呐,为了方便大家了解,我将购物车的整体操作分成三步,每一步我都会写一篇文章,之所以写三篇,我是想把购物车这个功能拆开来理解,这样更加便于我们学习,我希望达到的目标是:即使你是刚刚入门的IT小白也能通过我的博客掌握购物车.

  • 第一篇:实现购物车的UI效果
  • 第二篇:实现一些购物车特有的方法,例如:判断这个商家与商品是否选中,计算商品的总数量,计算商品的总价,还有这个根据用户的选择,改变商家或商品的状态.https://blog.csdn.net/LZ0419/article/details/84323506
  • 第三篇:进一步的完善购物车的功能,根据全选按钮的点击,改变所有商品的复选框状态,更新总的数量和价格,对适配器设置监听,监听加减按钮,商家的复选框,子条目的复选框的改变.https://blog.csdn.net/LZ0419/article/details/84328334
    接下来我们就开始购物车的操作:
    购物车的具体操作流程
    如果你要完成购物车,那么你必须掌握以下的技术:expandableListView,接口回调,Java单例模式,双重for循环,okhttp及封装,组合式自定义控件
  • 提示:在敲购物车之前,先测试拷贝过来的OKhttp工具类与组合式自定义控件是否有问题,购物车接口数据是否有问题
  • 1.搭建环境(依赖和权限)
  • 2.okhttp的封装工具及组合式自定义控件(注意拷贝过来的时候一定要测试一下是否有问题)
  • 3.购物车里涉及的布局及控件的初始化
  • 4.使用网络的工具类Post请求数据,注意必须是map.put(“uid”,“71”);
  • 5.创建Bean,并进行Gson解析
  • 6.创建BaseExpandableListAdapter适配器,进行初始化对象,再设置适配器
  • 7.为显示二级列表再多写for循环
  • 8.写一个刷新底部全选框,textVIew和Button的UI,改变了CheckBox状态,总价,总数量的方法
  • 9.根据全选按钮的点击,改变所有商品的复选框状态,更新总的数量和价格
  • 10.对适配器设置监听,监听加减按钮,商家的复选框,子条目的复选框的改变
    **

第一篇:实现购物车的UI效果

**
首先我们先来配置一下购物车所需要的依赖和权限
所需依赖

 //购物车必须要有的依赖
    implementation 'com.google.code.gson:gson:2.4'
    implementation 'com.squareup.okhttp3:okhttp:3.9.0'

所需权限:

 <uses-permission android:name="android.permission.INTERNET"/>

下面我们先看一下MainActivity: 在这里我们第一步主要是要做的初始化控件和初始化数据

package com.cart.cartdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ExpandableListView;
import android.widget.TextView;

import com.google.gson.Gson;

import java.util.HashMap;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ExpandableListView el_cart;
    private CheckBox cb_cart_all_select;
    private TextView tv_cart_total_price;
    private Button btn_cart_pay;
    String url = "http://www.zhaoapi.cn/product/getCarts";
    private MyAdapter mMyAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        initView();
        //初始化数据
       initData();
    }

    private void initData() {
        HashMap<String,String > map = new HashMap<>();
        map.put("uid","71");
        OkHttpUtil.getInstance().doPost(url, map, new OkHttpUtil.OkCallback() {
            @Override//请求失败时执行该方法
            public void onFailure(Exception e) {
            }
            @Override//请求成功时执行该方法
            public void onResponse(String json) {
                Log.e("lz",json+"");
                CartInfo cartInfo = new Gson().fromJson(json, CartInfo.class);
                //如果字段是0的话,代表网络请求成功
                if ("0".equals(cartInfo.getCode())){
                    //得到含有具体数据的集合
                    List<CartInfo.DataBean> data = cartInfo.getData();
                    //把数据给Adapter,创建Adadpter
                    mMyAdapter = new MyAdapter(data,MainActivity.this);

                    //设置Adapter 对象
                    el_cart.setAdapter(mMyAdapter);

                    //设置展开二级列表
                    for (int x = 0; x < data.size(); x++){
                       el_cart.expandGroup(x);
                    }
                }
            }
        });
    }

    private void initView() {
        el_cart = (ExpandableListView) findViewById(R.id.el_cart);
        cb_cart_all_select = (CheckBox) findViewById(R.id.cb_cart_all_select);
        tv_cart_total_price = (TextView) findViewById(R.id.tv_cart_total_price);
        btn_cart_pay = (Button) findViewById(R.id.btn_cart_pay);

        btn_cart_pay.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {//设置底部复选框的点击事件
            case R.id.cb_cart_all_select :
                break;
        }
    }
}

**接下来我们需要把OkHttp进行二次封装:在这里我对Post请求和Get请求都进行了封装,大家可以根据需要选择其一即可;

package com.cart.cartdemo;

import android.os.Handler;
import android.os.Looper;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
 * date:2018/11/20
 * author:李壮(HUAWEI)
 * function:二次封装OkHttp
 */
public class OkHttpUtil {

    private Handler mHandler;
    private static OkHttpUtil mOkHttpUtil;
    private  OkHttpClient mOkHttpClient;

    private OkHttpUtil(){
        //创建一个主线程handle
         mHandler = new Handler(Looper.getMainLooper());
         //创建OkHttpClient对象
         mOkHttpClient = new OkHttpClient.Builder()
                .readTimeout(10,TimeUnit.SECONDS)
                .writeTimeout(10,TimeUnit.SECONDS)
                .connectTimeout(10,TimeUnit.SECONDS)
                .build();
    }

    //单例模式
    public static OkHttpUtil getInstance(){
        if (mOkHttpUtil == null){
          synchronized (OkHttpUtil.class){
              return mOkHttpUtil = new OkHttpUtil();
          }
        }
        return mOkHttpUtil;
    }

    public interface OkCallback{
        //失败
        void onFailure(Exception e);
        //成功
        void onResponse(String json);
    }

    //封装doPost请求方法
    public void doPost(String url, Map<String,String >map, final OkCallback okCallback){
       //创建FormBody对象,把数据添加到FormBody对象中
        FormBody.Builder builder = new FormBody.Builder();
        if (map != null){
            for (String key : map.keySet()) {
                 builder.add(key,map.get(key));
            }
        }

        FormBody formBody = builder.build();
        //创建request对象
        final Request request = new Request.Builder()
                .post(formBody)
                .url(url)
                .build();
        //创建call对象
        Call call = mOkHttpClient.newCall(request);
        //通过call方法调用enqueue方法完成网络请求
        call.enqueue(new Callback() {
            @Override//请求失败时执行该方法
            public void onFailure(Call call, final IOException e) {
                if (okCallback != null){
                    //切换至主线程
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            okCallback.onFailure(e);
                        }
                    });
                }
            }

            @Override//请求成功时执行该方法
            public void onResponse(Call call,final Response response) throws IOException {
                try {
                    if (response != null && response.isSuccessful()){
                        final String json = response.body().string();
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                if (okCallback != null){
                                    okCallback.onResponse(json);
                                  return;
                               }
                            }
                        });
                    }
                }catch (IOException e){
                   e.printStackTrace();
                }

                if (okCallback != null){
                    okCallback.onFailure(new Exception("网络异常"));
                }

            }
        });
    }

    //封装doGet请求方法
    public void doGet(String url, final OkCallback okCallback) {
         //创建request对象
         Request request = new Request.Builder()
                .get()
                .url(url)
                .build();
        //创建call对象
       final Call call = mOkHttpClient.newCall(request);
        //通过call方法调用enqueue方法完成网络请求
        call.enqueue(new Callback() {
            @Override//请求失败时执行该方法
            public void onFailure(Call call, final IOException e) {
                if (okCallback != null) {
                    //切换至主线程
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            okCallback.onFailure(e);
                        }
                    });
                }
            }

            @Override//请求成功时执行该方法
            public void onResponse(Call call,final Response response) throws IOException {
                try {
                    if (response != null && response.isSuccessful()) {
                        final String json = response.body().string();
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                okCallback.onResponse(json);
                            }
                        });
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

然后我们需要创建一个适配器Adapter,在Adapter,拿到bean里关于组的数据和拿到子条目的所有数据,

package com.cart.cartdemo;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;
import java.util.zip.Inflater;

/**
 * date:2018/11/20
 * author:李壮(HUAWEI)
 * function:
 */
class MyAdapter extends BaseExpandableListAdapter {

    private List<CartInfo.DataBean> mDataBeans;
    Context mContext;
    public MyAdapter(List<CartInfo.DataBean> data, MainActivity mainActivity) {
        this.mDataBeans = data;
        mContext = mainActivity;
    }

    //设置一共有几个组
    @Override
    public int getGroupCount() {
        return mDataBeans == null ? 0 : mDataBeans.size();
    }

    //设置每个组有多少个子条目
    @Override
    public int getChildrenCount(int groupPosition) {
        return mDataBeans.get(groupPosition).getList() == null ? 0 : mDataBeans.get(groupPosition).getList().size();
    }

    //分组的布局
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        //先拿到bean里关于组的数据
        CartInfo.DataBean dataBean = mDataBeans.get(groupPosition);
        ParentViewHolder parentViewHolder;
        if (convertView == null) {
            convertView = View.inflate(mContext, R.layout.item_cart_parent, null);
            parentViewHolder = new ParentViewHolder(convertView);
            convertView.setTag(parentViewHolder);
        } else {
            parentViewHolder = (ParentViewHolder) convertView.getTag();
        }
        parentViewHolder.seller_name_tv.setText(mDataBeans.get(groupPosition).getSellerName());
        return convertView;
    }

    public static class ParentViewHolder {
        public CheckBox seller_cb;
        public TextView seller_name_tv;

        public ParentViewHolder(View rootView) {
           this.seller_cb = (CheckBox) rootView.findViewById(R.id.seller_cb);
            this.seller_name_tv = (TextView) rootView.findViewById(R.id.seller_name_tv);
        }

    }

    //子条目的布局
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        //先拿到子条目的所有数据
        CartInfo.DataBean dataBean = mDataBeans.get(groupPosition);
        List<CartInfo.DataBean.ListBean> list = dataBean.getList();

        //拿到List集合里具体的商品信息
        CartInfo.DataBean.ListBean listBean = list.get(childPosition);

        ChildViewHolder childViewHolder;
        if (convertView == null) {
            convertView = View.inflate(mContext,R.layout.item_cart_child,null);
            childViewHolder = new ChildViewHolder(convertView);
            convertView.setTag(childViewHolder);
        } else {
            childViewHolder = (ChildViewHolder) convertView.getTag();
        }
        //设置商品名字
        childViewHolder.product_title_name_tv.setText(listBean.getTitle());
        //设置商品价格
        childViewHolder.product_price_tv.setText(listBean.getPrice()+"");
        //设置复选框是否选中
        childViewHolder.child_cb.setChecked(listBean.getSelected() == 1);
        //设置组合式自定义控件内部的数量
        childViewHolder.add_remove_view.setNumber(listBean.getNum());
        return convertView;
    }

    public static class ChildViewHolder {
        public CheckBox child_cb;
        public ImageView product_icon_iv;
        public TextView product_title_name_tv;
        public TextView product_price_tv;
        public MyAddSubView add_remove_view;

        public ChildViewHolder(View rootView) {
            this.child_cb = (CheckBox) rootView.findViewById(R.id.child_cb);
            this.product_icon_iv = (ImageView) rootView.findViewById(R.id.product_icon_iv);
            this.product_title_name_tv = (TextView) rootView.findViewById(R.id.product_title_name_tv);
            this.product_price_tv = (TextView) rootView.findViewById(R.id.product_price_tv);
            this.add_remove_view = (MyAddSubView) rootView.findViewById(R.id.add_remove_view);
        }

    }
//////////////以下是BaseExpandableListAdapter必须要实现,但是没有作用的代码/////////////////////
    @Override
    public Object getGroup(int groupPosition) {
        return null;
    }
    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return null;
    }
    @Override
    public long getGroupId(int groupPosition) {
        return 0;
    }
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return 0;
    }
    @Override
    public boolean hasStableIds() {
        return false;
    }
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }
}

紧接着在这里我对购物车数量进行增减的控件,采用的是自定义组合式控件,那么我们就需要来实现一下这个组合式控件的操作

package com.cart.cartdemo;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

/**
 * date:2018/11/20
 * author:李壮(HUAWEI)
 * function:
 */
public class MyAddSubView extends LinearLayout implements View.OnClickListener {
    private TextView sub_tv;
    private TextView product_number_tv;
    private TextView add_tv;
    private int number = 1;

    public MyAddSubView(Context context) {
        this(context, null);
    }
    public MyAddSubView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyAddSubView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View view = View.inflate(context, R.layout.add_remove_view_layout, this);
        add_tv = view.findViewById(R.id.add_tv);
        sub_tv = view.findViewById(R.id.sub_tv);
        product_number_tv = view.findViewById(R.id.product_number_tv);

        add_tv.setOnClickListener(this);
        sub_tv.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            //增加操作
            case R.id.add_tv:
                ++number;
                product_number_tv.setText(number + "");
                if (mOnNumberChangeListener != null) {
                    mOnNumberChangeListener.onNumberChange(number);
                }
                break;
                //减少操作
            case R.id.sub_tv:
                if (number > 1) {
                    --number;
                    product_number_tv.setText(number + "");
                    if (mOnNumberChangeListener != null) {
                        mOnNumberChangeListener.onNumberChange(number);
                    } else {
                        Toast.makeText(getContext(), "不能再少了", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
        }
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
        product_number_tv.setText(number + "");
    }

    //接口回调
    OnNumberChangeListener mOnNumberChangeListener;

    interface OnNumberChangeListener {
        void onNumberChange(int num);
    }

    public void setOnNumberChangeListener(OnNumberChangeListener onNumberChangeListener) {
        this.mOnNumberChangeListener = onNumberChangeListener;
    }
}

然后就是我们的bean类

package com.cart.cartdemo;

import java.util.List;

/**
 * date:2018/11/20
 * author:李壮(HUAWEI)
 * function:
 */
public class CartInfo {

    private String msg;
    private String code;
    private List<DataBean> data;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

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

    public List<DataBean> getData() {
        return data;
    }

    public void setData(List<DataBean> data) {
        this.data = data;
    }

    public static class DataBean {

        private String sellerName;
        private String sellerid;
        private List<ListBean> list;

        public String getSellerName() {
            return sellerName;
        }

        public void setSellerName(String sellerName) {
            this.sellerName = sellerName;
        }

        public String getSellerid() {
            return sellerid;
        }

        public void setSellerid(String sellerid) {
            this.sellerid = sellerid;
        }

        public List<ListBean> getList() {
            return list;
        }

        public void setList(List<ListBean> list) {
            this.list = list;
        }

        public static class ListBean {

            private float bargainPrice;
            private String createtime;
            private String detailUrl;
            private String images;
            private int num;
            private int pid;
            private float price;
            private int pscid;
            private int selected;
            private int sellerid;
            private String subhead;
            private String title;

            public float getBargainPrice() {
                return bargainPrice;
            }

            public void setBargainPrice(int bargainPrice) {
                this.bargainPrice = bargainPrice;
            }

            public String getCreatetime() {
                return createtime;
            }

            public void setCreatetime(String createtime) {
                this.createtime = createtime;
            }

            public String getDetailUrl() {
                return detailUrl;
            }

            public void setDetailUrl(String detailUrl) {
                this.detailUrl = detailUrl;
            }

            public String getImages() {
                return images;
            }

            public void setImages(String images) {
                this.images = images;
            }

            public int getNum() {
                return num;
            }

            public void setNum(int num) {
                this.num = num;
            }

            public int getPid() {
                return pid;
            }

            public void setPid(int pid) {
                this.pid = pid;
            }

            public float getPrice() {
                return price;
            }

            public void setPrice(int price) {
                this.price = price;
            }

            public int getPscid() {
                return pscid;
            }

            public void setPscid(int pscid) {
                this.pscid = pscid;
            }

            public int getSelected() {
                return selected;
            }

            public void setSelected(int selected) {
                this.selected = selected;
            }

            public int getSellerid() {
                return sellerid;
            }

            public void setSellerid(int sellerid) {
                this.sellerid = sellerid;
            }

            public String getSubhead() {
                return subhead;
            }

            public void setSubhead(String subhead) {
                this.subhead = subhead;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }
        }
    }
}

以上我们相关功能的代码就可以告一段落了,剩下的就是我们的布局文件,我们来看一下
activity_main: 因为购物车的数据展示需要用到二级列表,在这里我们可以采用ExpandableListView;

<?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">

    <ExpandableListView
        android:id="@+id/el_cart"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="60dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:background="#eeeeee"
        android:gravity="center_vertical">

        <CheckBox
            android:id="@+id/cb_cart_all_select"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="全选" />

        <TextView
            android:id="@+id/tv_cart_total_price"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:paddingLeft="20dp"
            android:text="合计:¥0.00" />

        <Button
            android:id="@+id/btn_cart_pay"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:text="去结算(0)" />
    </LinearLayout>

</RelativeLayout>

这个是自定义组合式控件的控件的不布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="2dp"
    android:layout_marginLeft="10dp"
    android:layout_width="60dp"
    android:layout_height="30dp"
    android:layout_gravity="center_vertical"
    android:background="#99000000"
    android:gravity="center_vertical">

    <TextView
        android:background="#ffffff"
        android:layout_weight="1"
        android:id="@+id/sub_tv"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="-"
        android:textSize="16sp" />

    <TextView
        android:text="1"
        android:layout_marginLeft="2dp"
        android:background="#ffffff"
        android:layout_weight="1"
        android:id="@+id/product_number_tv"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        />

    <TextView
        android:layout_marginLeft="2dp"
        android:background="#ffffff"
        android:layout_weight="1"
        android:id="@+id/add_tv"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="+"
        android:textSize="16sp" />

</LinearLayout>

然后就剩下我们购物车的二级列表布局,先看一下组的布局:

<?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="60dp"
    android:gravity="center_vertical"
    >

    <CheckBox
        android:id="@+id/seller_cb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/seller_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp" />
</LinearLayout>

然后就剩下一个子条目的布局:
``Java

<?xml version="1.0" encoding="utf-8"?>

<CheckBox
    android:id="@+id/child_cb"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<ImageView
    android:id="@+id/product_icon_iv"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:layout_marginLeft="20dp"
    android:scaleType="centerCrop"
    android:src="@color/colorPrimary" />

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:orientation="vertical">

    <TextView
        android:id="@+id/product_title_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ellipsize="end"

        android:text="商品标题" />

    <TextView
        android:id="@+id/product_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="¥0.0" />
</LinearLayout>

<com.cart.cartdemo.MyAddSubView
    android:id="@+id/add_remove_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginRight="10dp" />
``` 好了,目前位置我们已经完成了购物车UI效果的一个操作,具体效果如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20181121134019266.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xaMDQxOQ==,size_16,color_FFFFFF,t_70) 这个第一篇:实现购物车的UI效果,我们就完成了,希望能给大家带来帮助,稍后两篇我会进一步把购物车的相关功能实现

猜你喜欢

转载自blog.csdn.net/LZ0419/article/details/84317572