使用单个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;
勘误:源码中json串文件中,第一个商家下的商品关联的商家ID("merchantId": "222")书写有误,应为"merchantId": "111",但不影响现在的功能实现,因为在一开始将数据转换成一个列表集合的阶段并没有使用到;
由于作者水平有限,语言描述及代码实现中难免有纰漏,望各位看官多提宝贵意见!
Hello , World !
感谢所有!