MVP实现二级购物车

来源:互联网 发布:云计算 大会 编辑:程序博客网 时间:2024/05/21 22:32

效果图 

使用接口:http://120.27.23.105/product/getCarts?uid=100


导入依赖

 compile 'com.squareup.okhttp3:okhttp:3.9.0'    compile 'com.google.code.gson:gson:2.8.2'    compile 'com.android.support:recyclerview-v7:25.3.1'    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
清单文件中加入权限
 <uses-permission android:name="android.permission.INTERNET"/>
配置application的name属性,ImageLoader初始化
<application    android:name=".appli.App"</application>
public class App extends Application{    @Override    public void onCreate() {        super.onCreate();        ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this).build();        ImageLoader.getInstance().init(configuration);    }}

布局中需要用到的的布局,在drawable下面新建qujiesuan.xml

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android">    <corners android:radius="200dp"/>    <solid android:color="#e53e42"/>    <size android:height="60dp" android:width="130dp"/></shape>
drawable下面还需要加入三张图片


接下来就是activity_main.xml里面的布局,上面是recyclerView下面是一系列的功能


activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    >    <android.support.v7.widget.RecyclerView        android:id="@+id/recycler_View"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1"        />    <LinearLayout        android:gravity="center_vertical"        android:padding="10dp"        android:orientation="horizontal"        android:layout_alignParentBottom="true"        android:layout_width="match_parent"        android:layout_height="wrap_content">        <CheckBox            android:background="@drawable/shopcart_unselected"            android:button="@null"            android:id="@+id/quanxuan"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <TextView            android:textStyle="bold"            android:layout_marginLeft="10dp"            android:textSize="23sp"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="全选"            />        <LinearLayout            android:padding="10dp"            android:layout_marginLeft="10dp"            android:orientation="vertical"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content">            <TextView                android:textColor="#e53e42"                android:id="@+id/total_price"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:textSize="20sp"                android:text="总价 : ¥0元"                />            <TextView                android:id="@+id/total_num"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:textSize="20sp"                android:text="共0件商品"                />        </LinearLayout>        <TextView            android:gravity="center"            android:textSize="25sp"            android:text="去结算"            android:textColor="#fff"            android:background="@drawable/qujiesuan"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />    </LinearLayout></LinearLayout>


自定义组合控件,,

custom_jiajian.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:orientation="horizontal"    android:gravity="center_vertical"    android:layout_height="wrap_content">    <Button        android:background="#fff"        android:textSize="20sp"        android:id="@+id/reverse"        android:text="一"        android:layout_width="50dp"        android:layout_height="wrap_content" />    <EditText        android:textStyle="bold"        android:textSize="23sp"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="1"        android:id="@+id/count"        />    <Button        android:id="@+id/add"        android:background="#fff"        android:textSize="25sp"        android:text="+"        android:layout_width="50dp"        android:layout_height="wrap_content" /></LinearLayout>

适配器的条目的布局,recy_cart_item.xml

com.example.day20_mvp_cart.customView.CustomJiaJian 别忘了改成自己的包名下的,否则会报错

recy_cart_item.xml,需要引入 自定义组合控件,在创建了CustomJiaJian类以后才可以引入

recy_cart_item.xml适配器的布局

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:orientation="vertical"    android:padding="15dp"    android:layout_height="match_parent">    <LinearLayout        android:orientation="horizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content">        <CheckBox            android:id="@+id/shop_checkbox"            android:layout_width="50dp"            android:layout_height="50dp" />        <TextView            android:layout_marginLeft="20dp"            android:text="良品铺子"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textSize="23sp"            android:textStyle="bold"            android:id="@+id/shop_name"            />    </LinearLayout>    <LinearLayout        android:gravity="center_vertical"        android:orientation="horizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content">        <CheckBox            android:id="@+id/item_checkbox"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <ImageView            android:id="@+id/item_face"            android:src="@mipmap/ic_launcher"            android:layout_width="120dp"            android:layout_height="120dp" />        <LinearLayout            android:layout_marginLeft="10dp"            android:orientation="vertical"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content">            <TextView                android:id="@+id/item_name"                android:textSize="20sp"                android:text="三只松鼠"                android:layout_width="wrap_content"                android:layout_weight="1"                android:layout_height="0dp"                />            <TextView                android:textColor="#f00"                android:id="@+id/item_price"                android:textSize="23sp"                android:text="299"                android:layout_width="wrap_content"                android:layout_height="0dp"                android:layout_weight="1"                />            <com.example.day171122_cart.customview.CustomJiaJian                android:id="@+id/custom_jiajian"                android:layout_width="wrap_content"                android:layout_height="0dp"                android:layout_weight="1"                />        </LinearLayout>        <ImageView            android:id="@+id/item_delete"            android:layout_marginRight="10dp"            android:src="@drawable/shopcart_delete"            android:layout_width="30dp"            android:layout_height="30dp" />    </LinearLayout></LinearLayout>

接着写代码里面的,首先将自定义组合控件的类填充在视图里

自定义组合控件的类,CustomJiaJian.java继承LinearLayout,inflate填充布局,+和-的点击事件回调给adapter


CustomJiaJian.java

public class CustomJiaJian extends LinearLayout{    private Button reverse;    private Button add;    private EditText countEdit;    private int mCount =1;    public CustomJiaJian(Context context) {        super(context);    }    public CustomJiaJian(Context context, AttributeSet attrs) {        super(context, attrs);        View view = View.inflate(context, R.layout.custom_jiajian,this);        reverse = (Button) view.findViewById(R.id.reverse);        add = (Button) view.findViewById(R.id.add);        countEdit = (EditText) view.findViewById(R.id.count);        reverse.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                String content = countEdit.getText().toString().trim();                int count = Integer.valueOf(content);                if(count>1){                    mCount = count-1;                    countEdit.setText(mCount+"");                    //回调给adapter里面                    if(customListener!=null){                        customListener.jiajian(mCount);                    }                }            }        });        add.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                String content = countEdit.getText().toString().trim();                int count = Integer.valueOf(content)+1;                mCount = count;                countEdit.setText(mCount+"");                //接口回调给adapter                if(customListener!=null){                    customListener.jiajian(mCount);                }            }        });    }    public CustomJiaJian(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    CustomListener customListener;    public void setCustomListener(CustomListener customListener){        this.customListener = customListener;    }    //加减的接口    public interface CustomListener{        public void jiajian(int count);        public void shuRuZhi(int count);    }    //这个方法是供recyadapter设置 数量时候调用的    public void setEditText(int num) {        if(countEdit !=null) {            countEdit.setText(num + "");        }    }}


下面就是okhttp二次封装的类,这里就不详细叙述了,详见okhttp封装类,拦截器地址

根据最上面给出的接口.访问到的数据,生成一个实体类CartBean


在CartBean类里面自己添加三个字段,isFirst,item_check,shop_check

 //自己添加的三个字段            private int isFirst = 1;//1为显示商铺, 2为隐藏商铺            private boolean item_check;//每个商品的选中状态            private boolean shop_check;//商店的选中状态            public int getIsFirst() {                return isFirst;            }            public void setIsFirst(int isFirst) {                this.isFirst = isFirst;            }            public boolean isItem_check() {                return item_check;            }            public void setItem_check(boolean item_check) {                this.item_check = item_check;            }            public boolean isShop_check() {                return shop_check;            }            public void setShop_check(boolean shop_check) {                this.shop_check = shop_check;            }

接下来就是使用MVP架构模式..首先创建presentermodel层,View这里默认就是MainActivity

写两个接口,一个是ModelCallBack是model层的回调接口,ViewCallBack是view层的回调接口

ModelCallBack.java

public interface ModelCallBack {    public void success(CartBean cartBean);    public void failure(Exception e);}

ViewCallBack.java
public interface ViewCallBack {    public void success(CartBean cartBean);    public void failure(Exception e);}

下面是重要的MainActivity.java, 调用presenter层的方法,实现view层的接口的方法,还调用适配器adapter里面的接口

public class MainActivity extends AppCompatActivity implements ViewCallBack{    private RecyclerView recyclerView;    private TextView total_price;    private TextView total_num;    private CheckBox quanxuan;    private MyPresenter myPresenter;    private RecyAdapter recyAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //http://120.27.23.105/product/getCarts?uid=100        recyclerView = (RecyclerView) findViewById(R.id.recycler_View);        total_price = (TextView) findViewById(R.id.total_price);        total_num = (TextView) findViewById(R.id.total_num);        quanxuan = (CheckBox) findViewById(R.id.quanxuan);        quanxuan.setTag(1);//1为不选中        LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this,LinearLayoutManager.VERTICAL,false);        //new出适配器        recyAdapter = new RecyAdapter(this);        myPresenter = new MyPresenter(this);        //调用presenter里面的请求数据的方法        myPresenter.getData();        recyclerView.setLayoutManager(manager);        recyclerView.setAdapter(recyAdapter);        //调用recyAdapter里面的接口,设置 全选按钮 总价 总数量        recyAdapter.setUpdateListener(new RecyAdapter.UpdateListener() {            @Override            public void setTotal(String total, String num, boolean allCheck) {                //设置ui的改变                total_num.setText("共"+num+"件商品");//总数量                total_price.setText("总价 :¥"+total+"元");//总价                if(allCheck){                    quanxuan.setTag(2);                    quanxuan.setBackgroundResource(R.drawable.shopcart_selected);                }else{                    quanxuan.setTag(1);                    quanxuan.setBackgroundResource(R.drawable.shopcart_unselected);                }                quanxuan.setChecked(allCheck);            }        });        //这里只做ui更改, 点击全选按钮,,调到adapter里面操作        quanxuan.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //调用adapter里面的方法 ,,把当前quanxuan状态传递过去                int tag = (int) quanxuan.getTag();                if(tag==1){                    quanxuan.setTag(2);                    quanxuan.setBackgroundResource(R.drawable.shopcart_selected);                }else{                    quanxuan.setTag(1);                    quanxuan.setBackgroundResource(R.drawable.shopcart_unselected);                }                recyAdapter.quanXuan(quanxuan.isChecked());            }        });    }    //实现接口,重写的方法    @Override    public void success(CartBean cartBean) {        //拿到返回来的数据 ,, 传给适配器数据        recyAdapter.add(cartBean);    }    @Override    public void failure(final Exception e) {        runOnUiThread(new Runnable() {            @Override            public void run() {                Toast.makeText(MainActivity.this,"error",Toast.LENGTH_SHORT).show();            }        });    }    @Override    protected void onDestroy() {        super.onDestroy();        //调用p层的解除绑定        myPresenter.detach();    }}

MyPresenter.java,presenter层里面 调用model层的方法,,防止内存泄露解绑

public class MyPresenter {    MyModel myModel = new MyModel();    ViewCallBack viewCallBack;    public MyPresenter(ViewCallBack viewCallBack) {        this.viewCallBack = viewCallBack;    }    //调用model 层的请求数据    public void getData(){        myModel.getData(new ModelCallBack() {            @Override            public void success(CartBean cartBean) {                if(viewCallBack!=null) {                    viewCallBack.success(cartBean);                }            }            @Override            public void failure(Exception e) {                if(viewCallBack!=null) {                    viewCallBack.failure(e);                }            }        });    }    /**     * 防止内存泄露     * */    public void detach(){        viewCallBack=null;    }}



MyModel.java,model层里面 调用okhttp的封装类 单例模式,请求网络数据.返回一个bean类,类型改成CartBean

上面已经放过了MainActivity接收到model传给presenter.presenter传给view 的CartBean以后,将数据添加给适配器,

public class MyModel {    public void getData(final ModelCallBack modelCallBack){        //访问接口        String path = "http://120.27.23.105/product/getCarts?uid=100";        OkhttpUtils.getInstance().asy(null, path, new AbstractUiCallBack<CartBean>() {            @Override            public void success(CartBean cartBean) {                modelCallBack.success(cartBean);            }            @Override            public void fail(Exception e) {                modelCallBack.failure(e);            }        });    }}


下面是适配器RecyAdapter.java 这里面的操作量就很大,包括了对每个条目的一系列操作,删除,选中,和对自定义视图加减号改变数量等,和是否显示和隐藏一级商家的信息,求总价,总数量等,部分操作也用到了接口回调出去

public class RecyAdapter extends RecyclerView.Adapter<RecyAdapter.MyViewHolder>{    Context context;    //创建大的集合    private List<CartBean.DataBean.ListBean> list;    //存放商家的id和商家的名称的map集合    private Map<String,String> map = new HashMap<>();    public RecyAdapter(Context context) {        this.context = context;    }    /**     * 添加数据并更新显示     * */    public void add(CartBean cartBean){        //传进来的是bean对象        if(list == null){            list = new ArrayList<>();        }        //第一层遍历商家和商品        for (CartBean.DataBean shop : cartBean.getData()){            //把商品的id和商品的名称添加到map集合里 ,,为了之后方便调用            map.put(shop.getSellerid(),shop.getSellerName());            //第二层遍历里面的商品            for (int i=0;i<shop.getList().size();i++){                //添加到list集合里                list.add(shop.getList().get(i));            }        }        //调用方法 设置显示或隐藏 商铺名        setFirst(list);        notifyDataSetChanged();    }    /**     * 设置数据源,控制是否显示商家     * */    private void setFirst(List<CartBean.DataBean.ListBean> list) {        if(list.size()>0){            //如果是第一条数据就设置isFirst为1            list.get(0).setIsFirst(1);            //从第二条开始遍历            for (int i=1;i<list.size();i++){                //如果和前一个商品是同一家商店的                if (list.get(i).getSellerid() == list.get(i-1).getSellerid()){                    //设置成2不显示商铺                    list.get(i).setIsFirst(2);                }else{//设置成1显示商铺                    list.get(i).setIsFirst(1);                    //如果当前条目选中,把当前的商铺也选中                    if (list.get(i).isItem_check()==true){                        list.get(i).setShop_check(list.get(i).isItem_check());                    }                }            }        }    }    @Override    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = View.inflate(context, R.layout.recy_cart_item,null);        MyViewHolder myViewHolder = new MyViewHolder(view);        return myViewHolder;    }    @Override    public void onBindViewHolder(final MyViewHolder holder, final int position) {        /**         * 设置商铺的 shop_checkbox和商铺的名字 显示或隐藏         * */        if(list.get(position).getIsFirst()==1){            //显示商家            holder.shop_checkbox.setVisibility(View.VISIBLE);            holder.shop_name.setVisibility(View.VISIBLE);            //设置shop_checkbox的选中状态            holder.shop_checkbox.setChecked(list.get(position).isShop_check());            holder.shop_name.setText(map.get(String.valueOf(list.get(position).getSellerid())));        }else{//2            //隐藏商家            holder.shop_name.setVisibility(View.GONE);            holder.shop_checkbox.setVisibility(View.GONE);        }        //拆分images字段        String[] split = list.get(position).getImages().split("\\|");        //设置商品的图片        ImageLoader.getInstance().displayImage(split[0],holder.item_face);        //控制商品的item_checkbox,,根据字段改变        holder.item_checkbox.setChecked(list.get(position).isItem_check());        holder.item_name.setText(list.get(position).getTitle());        holder.item_price.setText(list.get(position).getPrice()+"");        //调用customjiajian里面的方法设置 加减号中间的数字        holder.customJiaJian.setEditText(list.get(position).getNum());        //商铺的shop_checkbox点击事件 ,控制商品的item_checkbox        holder.shop_checkbox.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //先改变数据源中的shop_check                list.get(position).setShop_check(holder.shop_checkbox.isChecked());                for (int i=0;i<list.size();i++){                    //如果是同一家商铺的 都给成相同状态                    if(list.get(position).getSellerid()==list.get(i).getSellerid()){                        //当前条目的选中状态 设置成 当前商铺的选中状态                        list.get(i).setItem_check(holder.shop_checkbox.isChecked());                    }                }                //刷新适配器                notifyDataSetChanged();                //调用求和的方法                sum(list);            }        });        //商品的item_checkbox点击事件,控制商铺的shop_checkbox        holder.item_checkbox.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //先改变数据源中的item_checkbox                list.get(position).setItem_check(holder.item_checkbox.isChecked());                //反向控制商铺的shop_checkbox                for (int i=0;i<list.size();i++){                    for (int j=0;j<list.size();j++){                        //如果两个商品是同一家店铺的 并且 这两个商品的item_checkbox选中状态不一样                        if(list.get(i).getSellerid()==list.get(j).getSellerid() && !list.get(j).isItem_check()){                            //就把商铺的shop_checkbox改成false                            list.get(i).setShop_check(false);                            break;                        }else{                            //同一家商铺的商品 选中状态都一样,就把商铺shop_checkbox状态改成true                            list.get(i).setShop_check(true);                        }                    }                }                //更新适配器                notifyDataSetChanged();                //调用求和的方法                sum(list);            }        });        //删除条目的点击事件        holder.item_delete.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                list.remove(position);//移除集合中的当前数据                //删除完当前的条目 重新判断商铺的显示隐藏                setFirst(list);                //调用重新求和                sum(list);                notifyDataSetChanged();            }        });        //加减号的监听,        holder.customJiaJian.setCustomListener(new CustomJiaJian.CustomListener() {            @Override            public void jiajian(int count) {                //改变数据源中的数量                list.get(position).setNum(count);                notifyDataSetChanged();                sum(list);            }            @Override            //输入值 求总价            public void shuRuZhi(int count) {                list.get(position).setNum(count);                notifyDataSetChanged();                sum(list);            }        });    }    /**     * 计算总价的方法     * */    private void sum(List<CartBean.DataBean.ListBean> list){        int totalNum = 0;//初始的总价为0        float totalMoney = 0.0f;        boolean allCheck = true;        for (int i=0;i<list.size();i++){            //把 已经选中的 条目 计算价格            if (list.get(i).isItem_check()){                totalNum += list.get(i).getNum();                totalMoney += list.get(i).getNum() * list.get(i).getPrice();            }else{                //如果有个未选中,就标记为false                allCheck = false;            }        }        //接口回调出去 把总价 总数量 和allcheck 传给view层        updateListener.setTotal(totalMoney+"",totalNum+"",allCheck);    }    //view层调用这个方法, 点击quanxuan按钮的操作    public void quanXuan(boolean checked) {        for (int i=0;i<list.size();i++){            list.get(i).setShop_check(checked);            list.get(i).setItem_check(checked);        }        notifyDataSetChanged();        sum(list);    }    @Override    public int getItemCount() {        return list==null?0:list.size();    }    public static class MyViewHolder extends RecyclerView.ViewHolder {        private final CheckBox shop_checkbox;        private final TextView shop_name;        private final CheckBox item_checkbox;        private final TextView item_name;        private final TextView item_price;        private final CustomJiaJian customJiaJian;        private final ImageView item_delete;        private final ImageView item_face;        public MyViewHolder(View itemView) {            super(itemView);            shop_checkbox = (CheckBox) itemView.findViewById(R.id.shop_checkbox);            shop_name = (TextView) itemView.findViewById(R.id.shop_name);            item_checkbox = (CheckBox) itemView.findViewById(R.id.item_checkbox);            item_name = (TextView) itemView.findViewById(R.id.item_name);            item_price = (TextView) itemView.findViewById(R.id.item_price);            customJiaJian = (CustomJiaJian) itemView.findViewById(R.id.custom_jiajian);            item_delete = (ImageView) itemView.findViewById(R.id.item_delete);            item_face = (ImageView) itemView.findViewById(R.id.item_face);        }    }    UpdateListener updateListener;    public void setUpdateListener(UpdateListener updateListener){        this.updateListener = updateListener;    }    //接口    public interface UpdateListener{        public void setTotal(String total,String num,boolean allCheck);    }}




原创粉丝点击