Android使用单个RecyclerView而非嵌套列表实现购物车列表功能

使用单个RecyclerView而非嵌套列表实现购物车列表功能

效果图:

       实现一个购物车的列表效果,类似于天猫APP购物车的效果,UI布局使用 ConstraintLayout ,使用单个RecyclerView实现,对事件及数据的处理逻辑会复杂一些;

      目前主要是为了实现购物车各事件的交互功能,包括商品选择、全选、管理、删除等,暂未实现下拉刷新、上拉加载更多功能,列表的样式效果实现并不追求完全一致。 

主要代码实现:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:background="#f2f2f2"
    tools:context=".MainActivity">

    <View
        android:id="@+id/top_line"
        android:layout_width="0dp"
        android:layout_height="0.7dp"
        android:layout_marginTop="35dp"
        android:background="#f4f4f4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/tv_titile" />

    <View
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#fff"
        app:layout_constraintBottom_toTopOf="@+id/top_line"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_titile"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="购物车"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@id/top_line"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_manage"
        android:layout_width="60dp"
        android:layout_height="0dp"
        android:gravity="center"
        android:textSize="15sp"
        android:text="管理"
        app:layout_constraintBottom_toTopOf="@id/top_line"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteY="3dp" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottom_line"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/top_line" />

    <View
        android:id="@+id/bottom_line"
        android:layout_width="0dp"
        android:layout_height="0.7dp"
        android:layout_marginBottom="65dp"
        android:background="#f4f4f4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <View
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#fff"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/bottom_line" />

    <TextView
        android:id="@+id/tv_total_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="10dp"
        android:layout_marginRight="10dp"
        android:textSize="15sp"
        android:text="总价:¥00"
        app:layout_constraintBaseline_toBaselineOf="@id/tv_settlement"
        app:layout_constraintEnd_toStartOf="@id/tv_settlement" />

    <TextView
        android:id="@+id/tv_settlement"
        android:layout_width="wrap_content"
        android:gravity="center"
        android:layout_height="0dp"
        android:layout_marginEnd="15dp"
        android:layout_marginRight="15dp"
        android:textSize="18sp"
        android:text="结算"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/bottom_line" />

    <CheckBox
        android:id="@+id/cb_selectall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:text="全选"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/bottom_line" />

</androidx.constraintlayout.widget.ConstraintLayout>
package com.windfallsheng.shoppingcartexample;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @author lzsheng
 */
public class MainActivity extends AppCompatActivity {

    ShoppingCartAdapter shoppingCartAdapter;
    List<MultiData> multiDataList = new ArrayList<>();
    private TextView tvTotalPrice;
    private TextView tvSettlement;
    private TextView tvManage;
    private CheckBox cbSelectAll;
    private boolean mHasSelectAll;
    private boolean mDeletable;
    private int mSelectedGoodsCount;
    private int mSelectedMerchantCount;
    private int mTotalPrice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 初始化列表数据
        LinkedList<ShoppingCartBean> list = getdata();
        int size = list.size();
        for (int i = 0; i < size; i++) {
            ShoppingCartBean shoppingCart = list.get(i);
            ShoppingCartBean.MerchantInfoBean merchantInfo = shoppingCart.getMerchantInfo();
            String merchantId = merchantInfo.getMerchantId();
            multiDataList.add(new MultiData(CartConsts.CART_ITEM_MERCHANT, merchantInfo, merchantInfo.isHasSelected(), merchantId, ""));

            List<ShoppingCartBean.GoodsInfoBean> goodsInfoList = shoppingCart.getGoodsList();
            int goodsSize = goodsInfoList.size();
            if (goodsSize == 1) {
                ShoppingCartBean.GoodsInfoBean goodsInfo = goodsInfoList.get(0);
                multiDataList.add(new MultiData(CartConsts.CART_ITEM_GOODS_LAST, goodsInfo, false, goodsInfo.getId(), merchantId));
                goodsInfoList.remove(goodsInfo);
            } else {
                for (int j = 0; j < goodsSize; j++) {
                    ShoppingCartBean.GoodsInfoBean goodsInfo = goodsInfoList.get(j);
                    if (j == goodsInfoList.size() - 1) {
                        multiDataList.add(new MultiData(CartConsts.CART_ITEM_GOODS_LAST, goodsInfo, goodsInfo.isHasSelected(), goodsInfo.getId(), merchantId));
                    } else {
                        multiDataList.add(new MultiData(CartConsts.CART_ITEM_GOODS_NORMAL, goodsInfo, goodsInfo.isHasSelected(), goodsInfo.getId(), merchantId));
                    }
                    // 移除原有集合中的数据
                    goodsInfoList.remove(goodsInfo);
                    goodsSize--;
                    j--;
                }
            }
            // 移除原有集合中的数据
            list.remove(shoppingCart);
            size--;
            i--;
        }
        Log.d("ShoppingCartExample", "method:onCreate#multiDataList=" + multiDataList);

        // 初始化UI控件;
        tvTotalPrice = findViewById(R.id.tv_total_price);
        tvSettlement = findViewById(R.id.tv_settlement);
        tvManage = findViewById(R.id.tv_manage);
        cbSelectAll = findViewById(R.id.cb_selectall);
        RecyclerView recyclerView = findViewById(R.id.recyclerview);


        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        shoppingCartAdapter = new ShoppingCartAdapter(this, multiDataList);
        recyclerView.setAdapter(shoppingCartAdapter);

        // 初始化相关数据;
        initData();
        // 初始化UI状态;
        cbSelectAll.setChecked(mHasSelectAll);
        tvTotalPrice.setText("总价:¥" + mTotalPrice);
        tvSettlement.setText("结算(" + mSelectedGoodsCount + ")");

        shoppingCartAdapter.setRecyclerViewItemClick(new ShoppingCartAdapter.RecyclerViewItemClick() {
            @Override
            public void onRecyclerViewItemClick(View childView, int position) {
                MultiData multiDataClicked = multiDataList.get(position);
                if (childView instanceof CheckBox) {
                    handleChecked((CheckBox) childView, position, multiDataClicked);
                } else {
                    Toast.makeText(MainActivity.this, "跳转商品详情--position=" + position, Toast.LENGTH_SHORT).show();
                }
            }
        });

        cbSelectAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mHasSelectAll = !mHasSelectAll;
                handleSelectAll(mHasSelectAll);
                cbSelectAll.setChecked(mHasSelectAll);
                shoppingCartAdapter.notifyDataSetChanged();
                tvTotalPrice.setText("总价:¥" + mTotalPrice);
                tvSettlement.setText("结算(" + mSelectedGoodsCount + ")");
            }
        });

        tvSettlement.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mDeletable) {
                    if (mSelectedGoodsCount <= 0) {
                        Toast.makeText(MainActivity.this, "请选择删除商品!", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    // 处理数据业务;
                    handleDelete();
                    // 刷新UI
                    shoppingCartAdapter.notifyDataSetChanged();
                    mDeletable = false;
                    tvManage.setText("管理");
                    tvTotalPrice.setText("总价:¥" + mTotalPrice);
                    tvSettlement.setText("结算");
                    cbSelectAll.setChecked(mHasSelectAll);
                } else {
                    if (mSelectedGoodsCount > 0) {
                        Toast.makeText(MainActivity.this, "已选择" + mSelectedGoodsCount + "个商品", Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, "还没有选择商品哦!", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });
//        cbSelectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
//            @Override
//            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
//            }
//        });

        tvManage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDeletable = !mDeletable;
                if (mDeletable) {
                    tvSettlement.setText("删除(" + mSelectedGoodsCount + ")");
                    tvManage.setText("完成");
                } else {
                    tvSettlement.setText("结算(" + mSelectedGoodsCount + ")");
                    tvManage.setText("管理");
                }
            }
        });
    }

    private void handleDelete() {
        int size = multiDataList.size();
        for (int i = 0; i < size; i++) {
            MultiData multiData = multiDataList.get(i);
            boolean isChecked = multiData.isChecked();
            Log.d("ShoppingCartExample", "method:handlerDelete#isChecked=" + isChecked);
            String cId = multiData.getcId();
            String pId = multiData.getpId();
            Log.d("ShoppingCartExample", "method:handlerDelete#cId=" +
                    cId + ", multiData=" + pId);
            if (!isChecked) {
                continue;
            }

            // 删除列表中数据的处理方式之一:
            if (multiData.getDisplyType() == CartConsts.CART_ITEM_MERCHANT) {
                for (int j = i + 1; i < size; j++) {
                    MultiData multiDataGoods = multiDataList.get(j);
                    if (TextUtils.equals(multiDataGoods.getcId(), multiData.getpId())) {
                        // 如果是该商家下的商品
                        ShoppingCartBean.GoodsInfoBean goodsInfoBean = (ShoppingCartBean.GoodsInfoBean) multiDataGoods.getData();
                        mTotalPrice -= goodsInfoBean.getPrice();
                        mSelectedGoodsCount--;
                        // 移除
                        multiDataList.remove(multiDataGoods);
                        j--;
                        size--;
                        // 如果是该商家下的最后一个商品
                        if (multiDataGoods.getDisplyType() == CartConsts.CART_ITEM_GOODS_LAST) {
                            break;
                        }
                        continue;
                    }
                    break;
                }
                mSelectedMerchantCount--;
            } else {
                ShoppingCartBean.GoodsInfoBean goodsInfoBean = (ShoppingCartBean.GoodsInfoBean) multiData.getData();
                mTotalPrice -= goodsInfoBean.getPrice();
                mSelectedGoodsCount--;
                // 在该商家下将上一个商品修改为最后一个;
                if (multiData.getDisplyType() == CartConsts.CART_ITEM_GOODS_LAST) {
                    // 这个商品类型至少不会存在在第一个位置,实际至少存在在第二个位置;
                    if (multiDataList.size() > 1 && i > 1) {
                        MultiData multiDataLast = multiDataList.get(i - 1);
                        if (TextUtils.equals(multiDataLast.getpId(), multiData.getcId())) {
                            // 该商家下还有其它商品时
                            multiDataLast.setDisplyType(CartConsts.CART_ITEM_GOODS_LAST);
                        }
                    }
                }
            }
            // 移除
            multiDataList.remove(multiData);
            i--;
            size--;

            // 删除列表中数据的处理方式之一:
            /*if (multiData.getDisplyType() == CartConsts.CART_ITEM_MERCHANT) {
                mSelectedMerchantCount--;
            } else {
                mSelectedGoodsCount--;
                ShoppingCartBean.GoodsInfoBean goodsInfoBean = (ShoppingCartBean.GoodsInfoBean) multiData.getData();
                mTotalPrice -= goodsInfoBean.getPrice();
                // 该商家下还有其它商品时,在该商家下将上一个商品修改为最后一个;
                if (multiData.getDisplyType() == CartConsts.CART_ITEM_GOODS_LAST) {
                    // 这个商品类型至少不会存在在第一个位置,实际至少存在在第二个位置;
                    if (multiDataList.size() > 1 && i > 1) {
                        MultiData multiDataLast = multiDataList.get(i - 1);
                        if (TextUtils.equals(multiDataLast.getpId(), multiData.getcId())) {
                            // 该商家下还有其它商品时
                            multiDataLast.setDisplyType(CartConsts.CART_ITEM_GOODS_LAST);
                        }
                    }
                }
            }
            // 移除
            multiDataList.remove(multiData);
            i--;
            size--;*/


            // 最合理的方式是根据业务处理流程去修改相应的状态,不必强制赋值;
//            mHasSelectAll = false;
//            mTotalPrice = 0;
//            mSelectedMerchantCount = 0;
//            mSelectedGoodsCount = 0;
            Log.d("ShoppingCartExample", "method:handlerDelete#totalPrice=" + mTotalPrice);
        }

        hasSelectAll();
    }

    private void initData() {
        for (int i = 0; i < multiDataList.size(); i++) {
            MultiData multiData = multiDataList.get(i);
            boolean isChecked = multiData.isChecked();
            Log.d("ShoppingCartExample", "method:initData#isChecked=" + isChecked);
            String cId = multiData.getcId();
            Log.d("ShoppingCartExample", "method:initData#cId=" +
                    cId + ", multiData=" + multiData.getpId());
            if (!isChecked) {
                continue;
            }
            if (multiData.getDisplyType() == CartConsts.CART_ITEM_MERCHANT) {
                mSelectedMerchantCount++;
            } else {
                ShoppingCartBean.GoodsInfoBean goodsInfo = (ShoppingCartBean.GoodsInfoBean) multiDataList.get(i).getData();
                mTotalPrice += goodsInfo.getPrice();
                mSelectedGoodsCount++;
            }
            Log.d("ShoppingCartExample", "method:initData#totalPrice=" + mTotalPrice);
        }
        // 是否全选
        hasSelectAll();
        Log.d("ShoppingCartExample", "method:initData#hasSelectAll=" + mHasSelectAll);
    }

    private void hasSelectAll() {
        Log.d("ShoppingCartExample", "method:hasSelectAll#mSelectedGoodsCount=" + mSelectedGoodsCount
                + ", mSelectedMerchantCount=" + mSelectedMerchantCount);
        int totalSelectedCount = mSelectedGoodsCount + mSelectedMerchantCount;
        if (totalSelectedCount > 0 && totalSelectedCount == multiDataList.size()) {
            mHasSelectAll = true;
        } else {
            mHasSelectAll = false;
        }
    }

    private void handleSelectAll(boolean hasSelectAll) {
        for (int i = 0; i < multiDataList.size(); i++) {
            MultiData multiData = multiDataList.get(i);
            String cId = multiData.getcId();
            Log.d("ShoppingCartExample", "method:handleMerchantClicked#cId=" +
                    cId + ", multiData=" + multiData.getpId());
            if (multiData.isChecked() != hasSelectAll) {
                if (multiData.getDisplyType() == CartConsts.CART_ITEM_MERCHANT) {
                    if (hasSelectAll) {
                        mSelectedMerchantCount++;
                    } else {
                        mSelectedMerchantCount--;
                    }
                } else {
                    ShoppingCartBean.GoodsInfoBean goodsInfo = (ShoppingCartBean.GoodsInfoBean) multiDataList.get(i).getData();
                    // 计算总价、已选数量;
                    if (hasSelectAll) {
                        mTotalPrice += goodsInfo.getPrice();
                        mSelectedGoodsCount++;
                    } else {
                        mTotalPrice -= goodsInfo.getPrice();
                        mSelectedGoodsCount--;
                    }
                }

                // 修改各项状态
                multiData.setChecked(hasSelectAll);
            }
            Log.d("ShoppingCartExample", "method:initTotalPrice#totalPrice=" + mTotalPrice);
        }
    }

    private void handleChecked(CheckBox childView, final int position, MultiData multiDataClicked) {
        CheckBox cb = childView;
        boolean isChecked = cb.isChecked();
        // 处理当前点击项的状态;
        multiDataClicked.setChecked(isChecked);
        // 根据项的不同类型,处理相关项的状态;
        int displyType = multiDataClicked.getDisplyType();
        if (displyType == CartConsts.CART_ITEM_MERCHANT) {
            handleMerchantClicked(position, multiDataClicked, isChecked);
        } else {
            handleGoodsClicked(multiDataClicked, position, isChecked, displyType);
        }
        // 处理是否全选的状态;
        if (isChecked) {
            hasSelectAll();
            cbSelectAll.setChecked(mHasSelectAll);
        } else if (mHasSelectAll) {
            // 某项取消时,且是全选,则修改全选状态
            mHasSelectAll = isChecked;
            cbSelectAll.setChecked(mHasSelectAll);
        }
        Log.d("ShoppingCartExample", "method:handleChecked#isChecked=" + isChecked);
        Log.d("ShoppingCartExample", "method:handleChecked#totalPrice=" + mTotalPrice);
    }

    private void handleGoodsClicked(MultiData multiDataClicked, int positionClicked, boolean isChecked, int displayType) {
        // 计算价格,及总的商品选中数量
        ShoppingCartBean.GoodsInfoBean goodsInfo = (ShoppingCartBean.GoodsInfoBean) multiDataClicked.getData();
        if (isChecked) {
            mTotalPrice += goodsInfo.getPrice();
            mSelectedGoodsCount++;
        } else {
            mTotalPrice -= goodsInfo.getPrice();
            mSelectedGoodsCount--;
        }
        tvTotalPrice.setText("总价:¥" + mTotalPrice);
        tvSettlement.setText("结算(" + mSelectedGoodsCount + ")");

        // 如果商品项某个未选中,则商家项也是未选中
        if (!isChecked) {
            queryLastToMerchant(multiDataClicked, positionClicked, isChecked);
            // 刷新UI
            // TODO: 2020/9/7
            shoppingCartAdapter.notifyDataSetChanged();
            Log.d("ShoppingCartExample", "method:handleGoodsClicked#return#isChecked=" + isChecked);
            return;
        }
        // 如果商品项某个选中,则判断其它项状态;
        if (displayType == CartConsts.CART_ITEM_GOODS_NORMAL) {
            // 某个商家下的非最后一条商品
            // 判断当前商家下全部商品的选择状态,以处理商家项选择状态
            // TODO: 2020/9/7
            // 先向下查询其它商品项选中状态
            boolean isCheckedOfMerchant = true;
            for (int i = positionClicked + 1; i < multiDataList.size(); i++) {
                MultiData multiData = multiDataList.get(i);
                // 同一个商家下的商品
                if (TextUtils.equals(multiDataClicked.getcId(), multiData.getcId())
                        && !multiData.isChecked()) {
                    // 如果是商家项
                    isCheckedOfMerchant = multiData.isChecked();
                    break;
                }
                break;
            }
            // 再向上查找其它项选择状态
            if (isCheckedOfMerchant) {
                queryLastGoodsItem(multiDataClicked, positionClicked);
            } else {
                // 如果是未选中,则直接查询到商家项,设置为false;
                queryLastToMerchant(multiDataClicked, positionClicked, isCheckedOfMerchant);
            }
        } else if (displayType == CartConsts.CART_ITEM_GOODS_LAST) {
            // 某个商家下的最后一条商品
            // 判断当前商家下全部商品的选择状态,以处理商家项选择状态
            queryLastGoodsItem(multiDataClicked, positionClicked);
        }

        hasSelectAll();

        // 刷新UI
        // TODO: 2020/9/7
        shoppingCartAdapter.notifyDataSetChanged();
        Log.d("ShoppingCartExample", "method:handleGoodsClicked#totalPrice=" + mTotalPrice);
        tvTotalPrice.setText("总价:¥" + mTotalPrice);
        tvSettlement.setText("结算(" + mSelectedGoodsCount + ")");

    }

    private void queryLastToMerchant(MultiData multiDataClicked, int positionClicked, boolean isChecked) {
        // 直接查询到商家,设置为false;
        for (int i = positionClicked - 1; i >= 0; i--) {
            MultiData multiData = multiDataList.get(i);
            Log.d("ShoppingCartExample", "method:queryLastToMerchant#multiData=" + multiData.getpId());
            if (multiData.getDisplyType() == CartConsts.CART_ITEM_MERCHANT &&
                    TextUtils.equals(multiDataClicked.getcId(), multiData.getpId())) {
                // 如果是商家项
                if (multiData.isChecked() != isChecked) {
                    multiData.setChecked(isChecked);
                    if (isChecked) {
                        mSelectedMerchantCount++;
                    } else {
                        mSelectedMerchantCount--;
                    }
                }
                Log.d("ShoppingCartExample", "method:queryLastToMerchant#break");
                break;
            }
        }
    }

    private void queryLastGoodsItem(MultiData multiDataClicked, int positionClicked) {
        // 向上查找其它项选择状态
        boolean isCheckedOfMerchant = true;
        for (int i = positionClicked - 1; i >= 0; i--) {
            MultiData multiData = multiDataList.get(i);
            if (TextUtils.equals(multiData.getcId(), multiDataClicked.getcId())) {
                if (!isCheckedOfMerchant) {
                    continue;
                }
                // 如果是同一个商家下的商品
                if (multiData.getDisplyType() == CartConsts.CART_ITEM_GOODS_NORMAL && !multiData.isChecked()) {
                    isCheckedOfMerchant = multiData.isChecked();
                }
            } else if (multiData.getDisplyType() == CartConsts.CART_ITEM_MERCHANT &&
                    TextUtils.equals(multiDataClicked.getcId(), multiData.getpId())) {
                // 如果是商家项
                if (multiData.isChecked() != isCheckedOfMerchant) {
                    multiData.setChecked(isCheckedOfMerchant);
                    if (isCheckedOfMerchant) {
                        mSelectedMerchantCount++;
                    } else {
                        mSelectedMerchantCount--;
                    }
                }
                break;
            }
        }
    }

    private void handleMerchantClicked(int positionClicked, MultiData multiDataClicked, boolean isChecked) {
        // 如果点击的是商户的item;
        if (isChecked) {
            mSelectedMerchantCount++;
        } else {
            mSelectedMerchantCount--;
        }
        String pId = multiDataClicked.getpId();
        for (int i = positionClicked + 1; i < multiDataList.size(); i++) {
            MultiData multiData = multiDataList.get(i);
            String cId = multiData.getcId();
            Log.d("ShoppingCartExample", "method:handleMerchantClicked#cId=" +
                    cId + ", multiData=" + multiData.getpId());
            if (TextUtils.equals(cId, pId)) {
                ShoppingCartBean.GoodsInfoBean goodsInfo = (ShoppingCartBean.GoodsInfoBean) multiDataList.get(i).getData();
                if (isChecked) {
                    if (multiData.isChecked()) {
                        // 如果已经被选中,则不再处理状态,及加入总价
                    } else {
                        multiData.setChecked(isChecked);
                        mTotalPrice += goodsInfo.getPrice();
                        mSelectedGoodsCount++;
                    }
                } else {
                    if (multiData.isChecked()) {
                        multiData.setChecked(isChecked);
                        mTotalPrice -= goodsInfo.getPrice();
                        mSelectedGoodsCount--;
                    } else {
                        // 如果本来的状态就是未被选中,则不再处理状态,及加入总价
                    }
                    multiData.setChecked(isChecked);
                }
                Log.d("ShoppingCartExample", "method:handleMerchantClicked#continue");
                continue;
            }
            Log.d("ShoppingCartExample", "method:handleMerchantClicked#break");
            break;
        }
        hasSelectAll();
        // 刷新UI
        shoppingCartAdapter.notifyDataSetChanged();
        Log.d("ShoppingCartExample", "method:handleMerchantClicked#totalPrice=" + mTotalPrice);
        tvTotalPrice.setText("总价:¥" + mTotalPrice);
        tvSettlement.setText("结算(" + mSelectedGoodsCount + ")");
        cbSelectAll.setChecked(mHasSelectAll);

    }

    private LinkedList<ShoppingCartBean> getdata() {
        LinkedList<ShoppingCartBean> list = null;
        try {
            String jsonStr = getJsonStr(this, "cart_list.json");
            Gson gson = new Gson();
            list = gson.fromJson(jsonStr, new TypeToken<LinkedList<ShoppingCartBean>>() {
            }.getType());
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.d("ShoppingCartExample", "method:getdata#out#return value:list=" + list);
        return list;
    }

    public static String getJsonStr(Context context, String fileName) throws IOException {
        InputStream in = context.getResources().getAssets().open(fileName);
        int available = in.available();
        byte[] b = new byte[available];
        in.read(b);
        return new String(b, "UTF-8");
    }
}

        列表item的不同样式使用适配器的 ItemViewType 实现,在数据转换阶段将不同类型的数据转换,封装在一个列表中,没有使用列表的嵌套; 并且对购物车列表的商品选择事件、全选事件及管理删除事件做处理;

适配器:

package com.windfallsheng.shoppingcartexample;

import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

/**
 * @author lzsheng
 */
public class ShoppingCartAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Activity mActivity;
    private List<MultiData> shoppingCartList;
    private static RecyclerViewItemClick mRecyclerViewItemClick;

    public ShoppingCartAdapter(Activity mActivity, List<MultiData> shoppingCartList) {
        this.mActivity = mActivity;
        this.shoppingCartList = shoppingCartList;
    }

    public void setRecyclerViewItemClick(RecyclerViewItemClick recyclerViewItemClick) {
        mRecyclerViewItemClick = recyclerViewItemClick;
    }

    @Override
    public int getItemViewType(int position) {
        if (shoppingCartList != null && shoppingCartList.size() > 0) {
            return shoppingCartList.get(position).getDisplyType();
        }
        return -1;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        switch (viewType) {
            case CartConsts.CART_ITEM_MERCHANT:
                return new ViewHolderShop(mActivity.getLayoutInflater().inflate(R.layout.rv_cart_item_shop, parent, false));
            case CartConsts.CART_ITEM_GOODS_NORMAL:
                return new ViewHolderGoodsNormal(mActivity.getLayoutInflater().inflate(R.layout.rv_cart_item_goods_normal, parent, false));
            case CartConsts.CART_ITEM_GOODS_LAST:
                return new ViewHolderGoodsLast(mActivity.getLayoutInflater().inflate(R.layout.rv_cart_item_goods_last, parent, false));
        }
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
//        holder.textView.setText(shoppingCartList.get(position));
        int viewType = getItemViewType(position);
        MultiData multiData = shoppingCartList.get(position);
        switch (viewType) {
            case CartConsts.CART_ITEM_MERCHANT:
                ViewHolderShop holderShop = (ViewHolderShop) holder;
                holderShop.cbShop.setChecked(multiData.isChecked());
                break;
            case CartConsts.CART_ITEM_GOODS_NORMAL: {
                ViewHolderGoodsNormal holderGoodsNormal = (ViewHolderGoodsNormal) holder;
                holderGoodsNormal.cbShop.setChecked(multiData.isChecked());
                ShoppingCartBean.GoodsInfoBean goodsInfo = (ShoppingCartBean.GoodsInfoBean) multiData.getData();
                holderGoodsNormal.tvPrice.setText("¥" + goodsInfo.getPrice());
            }
            break;
            case CartConsts.CART_ITEM_GOODS_LAST: {
                ViewHolderGoodsLast viewHolderGoodsLast = (ViewHolderGoodsLast) holder;
                viewHolderGoodsLast.cbShop.setChecked(multiData.isChecked());
                ShoppingCartBean.GoodsInfoBean goodsInfo = (ShoppingCartBean.GoodsInfoBean) multiData.getData();
                viewHolderGoodsLast.tvPrice.setText("¥" + goodsInfo.getPrice());
            }
            break;
        }
    }

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

    static class ViewHolderShop extends RecyclerView.ViewHolder implements View.OnClickListener {
        CheckBox cbShop;

        public ViewHolderShop(@NonNull View itemView) {
            super(itemView);
//            textView = itemView.findViewById(R.id.tv_item_bottom_text);
            cbShop = itemView.findViewById(R.id.cb_shop);
            cbShop.setOnClickListener(this);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mRecyclerViewItemClick != null) {
                mRecyclerViewItemClick.onRecyclerViewItemClick(view, getAdapterPosition());
            }
        }
    }

    static class ViewHolderGoodsNormal extends RecyclerView.ViewHolder implements View.OnClickListener {
        CheckBox cbShop;
        TextView tvPrice;

        public ViewHolderGoodsNormal(@NonNull View itemView) {
            super(itemView);
//            textView = itemView.findViewById(R.id.tv_item_bottom_text);
            cbShop = itemView.findViewById(R.id.cb_goods);
            tvPrice = itemView.findViewById(R.id.tv_price);
            cbShop.setOnClickListener(this);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mRecyclerViewItemClick != null) {
                mRecyclerViewItemClick.onRecyclerViewItemClick(view, getAdapterPosition());
            }
        }
    }

    static class ViewHolderGoodsLast extends RecyclerView.ViewHolder implements View.OnClickListener {
        CheckBox cbShop;
        TextView tvPrice;

        public ViewHolderGoodsLast(@NonNull View itemView) {
            super(itemView);
//            textView = itemView.findViewById(R.id.tv_item_bottom_text);
            cbShop = itemView.findViewById(R.id.cb_goods);
            tvPrice = itemView.findViewById(R.id.tv_price);
            cbShop.setOnClickListener(this);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mRecyclerViewItemClick != null) {
                mRecyclerViewItemClick.onRecyclerViewItemClick(view, getAdapterPosition());
            }
        }
    }

    interface RecyclerViewItemClick {
        void onRecyclerViewItemClick(View childView, int position);
    }
}

数据实体类:

package com.windfallsheng.shoppingcartexample;

/**
 * @author lzsheng
 */
class MultiData<T> {

    private int displyType;

    private T data;

    private boolean isChecked;

    private String pId;

    private String cId;

    public int getDisplyType() {
        return displyType;
    }

    public void setDisplyType(int displyType) {
        this.displyType = displyType;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }

    public String getpId() {
        return pId;
    }

    public void setpId(String pId) {
        this.pId = pId;
    }

    public String getcId() {
        return cId;
    }

    public void setcId(String cId) {
        this.cId = cId;
    }

    public MultiData(int displyType, T data, boolean isChecked, String pId, String cId) {
        this.displyType = displyType;
        this.data = data;
        this.isChecked = isChecked;
        this.pId = pId;
        this.cId = cId;
    }

    @Override
    public String toString() {
        return "MultiData{" +
                "displyType=" + displyType +
                ", data=" + data +
                ", isChecked=" + isChecked +
                ", pId='" + pId + '\'' +
                ", cId='" + cId + '\'' +
                '}';
    }
}
package com.windfallsheng.shoppingcartexample;

import java.util.List;

/**
 * @author lzsheng
 */
class ShoppingCartBean {

    /**
     * merchantInfo : {"merchantId":"111","name":"这是我家的小小小店","icon":"/assets/images/cart_none_a.png","hasSelected":false,"quantityUpdatable":false,"isActivity":false}
     * goodsList : [{"id":1111,"merchantId":"222","title":"格力迷你静音台式电风扇","image":"/assets/images/cart_none_a.png","quantity":4,"price":130,"quantityUpdatable":false,"hasSelected":false}]
     */

    private MerchantInfoBean merchantInfo;
    private List<GoodsInfoBean> goodsList;

    public MerchantInfoBean getMerchantInfo() {
        return merchantInfo;
    }

    public void setMerchantInfo(MerchantInfoBean merchantInfo) {
        this.merchantInfo = merchantInfo;
    }

    public List<GoodsInfoBean> getGoodsList() {
        return goodsList;
    }

    public void setGoodsList(List<GoodsInfoBean> goodsList) {
        this.goodsList = goodsList;
    }

    @Override
    public String toString() {
        return "ShoppingCartBean{" +
                "merchantInfo=" + merchantInfo +
                ", goodsList=" + goodsList +
                '}';
    }

    public static class MerchantInfoBean {
        /**
         * merchantId : 111
         * name : 这是我家的小小小店
         * icon : /assets/images/cart_none_a.png
         * hasSelected : false
         * quantityUpdatable : false
         * isActivity : false
         */
        private String merchantId;
        private String name;
        private String icon;
        private boolean hasSelected;
        private boolean quantityUpdatable;
        private boolean isActivity;

        public String getMerchantId() {
            return merchantId;
        }

        public void setMerchantId(String merchantId) {
            this.merchantId = merchantId;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getIcon() {
            return icon;
        }

        public void setIcon(String icon) {
            this.icon = icon;
        }

        public boolean isHasSelected() {
            return hasSelected;
        }

        public void setHasSelected(boolean hasSelected) {
            this.hasSelected = hasSelected;
        }

        public boolean isQuantityUpdatable() {
            return quantityUpdatable;
        }

        public void setQuantityUpdatable(boolean quantityUpdatable) {
            this.quantityUpdatable = quantityUpdatable;
        }

        public boolean isIsActivity() {
            return isActivity;
        }

        public void setIsActivity(boolean isActivity) {
            this.isActivity = isActivity;
        }

        @Override
        public String toString() {
            return "MerchantInfoBean{" +
                    "merchantId='" + merchantId + '\'' +
                    ", name='" + name + '\'' +
                    ", icon='" + icon + '\'' +
                    ", hasSelected=" + hasSelected +
                    ", quantityUpdatable=" + quantityUpdatable +
                    ", isActivity=" + isActivity +
                    '}';
        }
    }

    public static class GoodsInfoBean {
        /**
         * id : 1111
         * merchantId : 222
         * title : 格力迷你静音台式电风扇
         * image : /assets/images/cart_none_a.png
         * quantity : 4
         * price : 130
         * quantityUpdatable : false
         * hasSelected : false
         */
        private String id;
        private String merchantId;
        private String title;
        private String image;
        private int quantity;
        private int price;
        private boolean quantityUpdatable;
        private boolean hasSelected;

        public String getId() {
            return id;
        }

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

        public String getMerchantId() {
            return merchantId;
        }

        public void setMerchantId(String merchantId) {
            this.merchantId = merchantId;
        }

        public String getTitle() {
            return title;
        }

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

        public String getImage() {
            return image;
        }

        public void setImage(String image) {
            this.image = image;
        }

        public int getQuantity() {
            return quantity;
        }

        public void setQuantity(int quantity) {
            this.quantity = quantity;
        }

        public int getPrice() {
            return price;
        }

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

        public boolean isQuantityUpdatable() {
            return quantityUpdatable;
        }

        public void setQuantityUpdatable(boolean quantityUpdatable) {
            this.quantityUpdatable = quantityUpdatable;
        }

        public boolean isHasSelected() {
            return hasSelected;
        }

        public void setHasSelected(boolean hasSelected) {
            this.hasSelected = hasSelected;
        }

        @Override
        public String toString() {
            return "GoodsInfoBean{" +
                    "id='" + id + '\'' +
                    ", merchantId='" + merchantId + '\'' +
                    ", title='" + title + '\'' +
                    ", image='" + image + '\'' +
                    ", quantity=" + quantity +
                    ", price=" + price +
                    ", quantityUpdatable=" + quantityUpdatable +
                    ", hasSelected=" + hasSelected +
                    '}';
        }
    }
}

模拟的数据列表json串cart_list.json:

[{
  "merchantInfo": {
    "merchantId": "111",
    "name": "这是我家的小小小店",
    "icon": "/assets/images/cart_none_a.png",
    "hasSelected": false,
    "quantityUpdatable": false,
    "isActivity": false
  },
  "goodsList": [{
    "id":"1111",
    "merchantId": "111",
    "title": "格力迷你静音台式电风扇",
    "image": "/assets/images/cart_none_a.png",
    "quantity": 4,
    "price": 130,
    "quantityUpdatable": false,
    "hasSelected": false
  }]
},
  {
    "merchantInfo": {
      "merchantId": "222",
      "name": "这是我家的小小小店",
      "icon": "/assets/images/cart_none_a.png",
      "hasSelected": false,
      "quantityUpdatable": false,
      "isActivity": false
    },
    "goodsList": [{
      "id": "2221",
      "merchantId": "222",
      "title": "格力迷你静音台式电风扇",
      "image": "/assets/images/cart_none_a.png",
      "quantity": 4,
      "price": 130,
      "quantityUpdatable": false,
      "hasSelected": true
    },
      {
        "id": "22222",
        "merchantId": "222",
        "title": "格力家用台式电风扇",
        "image": "/assets/images/cart_none_a.png",
        "quantity": 1,
        "price": 320,
        "quantityUpdatable": false,
        "hasSelected": false
      }
    ]
  },
  {
    "merchantInfo": {
      "merchantId": "333",
      "name": "这是我家的小小小店",
      "icon": "/assets/images/cart_none_a.png",
      "hasSelected": false,
      "isActivity": true
    },
    "goodsList": [{
      "id": "3331",
      "merchantId": "333",
      "title": "格力迷你静音台式电风扇",
      "image": "/assets/images/cart_none_a.png",
      "quantity": 4,
      "price": 110,
      "quantityUpdatable": false,
      "hasSelected": false
    },
      {
        "id": "3332",
        "merchantId": "333",
        "title": "格力家用台式电风扇",
        "image": "/assets/images/cart_none_a.png",
        "quantity": 1,
        "price": 310,
        "quantityUpdatable": false,
        "hasSelected": false
      },
      {
        "id": "3333",
        "merchantId": "333",
        "title": "格力迷你静音台式电风扇",
        "image": "/assets/images/cart_none_a.png",
        "quantity": 4,
        "price": 120,
        "quantityUpdatable": false,
        "hasSelected": false
      }
    ]
  },
  {
    "merchantInfo": {
      "merchantId": "444",
      "name": "这是我家的小小小店",
      "icon": "/assets/images/cart_none_a.png",
      "hasSelected": false,
      "isActivity": false
    },
    "goodsList": [{
      "id": "4441",
      "merchantId": "444",
      "title": "格力迷你静音台式电风扇",
      "image": "/assets/images/cart_none_a.png",
      "quantity": 4,
      "price": 130,
      "quantityUpdatable": false,
      "hasSelected": false
    },
      {
        "id": "4442",
        "merchantId": "444",
        "title": "格力家用台式电风扇",
        "image": "/assets/images/cart_none_a.png",
        "quantity": 1,
        "price": 290,
        "quantityUpdatable": false,
        "hasSelected": false
      },
      {
        "id": "4443",
        "merchantId": "444",
        "title": "格力迷你静音台式电风扇",
        "image": "/assets/images/cart_none_a.png",
        "quantity": 4,
        "price": 150,
        "quantityUpdatable": false,
        "hasSelected": false
      },
      {
        "id": "4444",
        "merchantId": "444",
        "title": "格力家用台式电风扇",
        "image": "/assets/images/cart_none_a.png",
        "quantity": 1,
        "price": 280,
        "quantityUpdatable": false,
        "hasSelected": false
      }
    ]
  }
]

单个RecyclerView实现的列表,各种事件的业务逻辑的处理相对复杂,可能会有业务上的BUG;

 

CSDN

勘误:源码中json串文件中,第一个商家下的商品关联的商家ID("merchantId": "222")书写有误,应为"merchantId": "111",但不影响现在的功能实现,因为在一开始将数据转换成一个列表集合的阶段并没有使用到;

由于作者水平有限,语言描述及代码实现中难免有纰漏,望各位看官多提宝贵意见!

Hello , World !

感谢所有!

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

windfallsheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值