android仿淘宝购物车
先看gif效果图,以免看了半天不是想要的效果
看到这还没走的应该就是想要的效果吧,下面具体分析
等不及的可以拖到底部的下载地址
先看看布局文件,布局文件很简单,注意我这边用了design库的控件,需要引入design包
主布局:activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f5f5f5"
android:fitsSystemWindows="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="60dp"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="10dp">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="购物车"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_shoppingCart_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:padding="10dp"
android:text="编辑"
android:textColor="@android:color/white"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_tv_cb_shoppingCart"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:background="@android:color/white">
<CheckBox
android:id="@+id/cb_shoppingCart_all"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="13dp"
android:text="全选" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@id/cb_shoppingCart_all"
android:text="全选"
android:textSize="18sp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<TextView
android:id="@+id/tv_cb_shoppingCart_all_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="合计:"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_tv_cb_shoppingCart_all_total_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/tv_cb_shoppingCart_all_total"
android:text="¥0.00"
android:textColor="#ff5200"
android:textSize="18sp" />
</RelativeLayout>
<Button
android:id="@+id/bt_shoppingCart_checkout"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="13dp"
android:background="@color/colorPrimary"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="去结算(0)"
android:textColor="@android:color/white" />
</RelativeLayout>
</RelativeLayout>
recycleview item布局: item_product.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp">
<CheckBox
android:id="@+id/cb_item_product_cb"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="13dp" />
<ImageView
android:id="@+id/iv_item_product_img"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerVertical="true"
android:layout_marginLeft="56dp"
android:src="@mipmap/ic_launcher" />
<RelativeLayout
android:id="@+id/rl_item_product_normal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_item_product_img">
<TextView
android:id="@+id/tv_item_product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_item_product_priceStr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="15dp"
android:text="¥"
android:textColor="#ff5200"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_item_product_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="15dp"
android:layout_toRightOf="@+id/tv_item_product_priceStr"
android:textColor="#ff5200"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_item_product_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="13dp"
android:textColor="@android:color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/tv_item_product_num"
android:text="x"
android:textColor="@android:color/black"
android:textSize="16sp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_item_product_edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/iv_item_product_img"
android:visibility="gone">
<Button
android:id="@+id/bt_item_product_delete"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="@android:color/holo_red_dark"
android:text="删除"
android:textColor="@android:color/white" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/bt_item_product_delete">
<ImageView
android:id="@+id/iv_item_product_minus"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:src="@drawable/shopping_select_number_minus_btn_default" />
<ImageView
android:id="@+id/iv_item_product_add"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/shopping_select_number_add_btn_default" />
<TextView
android:id="@+id/tv_item_product_edit_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="35dp"
android:layout_marginRight="35dp"
android:textColor="@android:color/black"
android:gravity="center"
android:padding="10dp"
android:text="1"
android:textSize="16sp" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
下面简单分析一下流程
1、先实现recycleview加载数据
Product字段,isSelected是json中不包含的字段,用来判断是否选中
private Integer product_id;
private String product_name;
private Float price;
private Integer product_num;
private String img;
//判断是否选中
private boolean isSelected;
MainActivity部分实现代码
初始化数据,这边直接用json字符串来模拟获得到的数据
private List<Product> productList = null;
private CheckBox mCbSelectAll;
private TextView mTvEdit, mTvTotalPrice;
private Button mBtCheckout;
private RecyclerView mRvShoppingCart;
private ProductAdapter mProductAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
/**
* 初始化数据,模拟网络数据
*/
private void initData() {
Gson gson = new Gson();
productList = gson.fromJson(json, new TypeToken<List<Product>>() {}.getType());
}
private void initView() {
mTvEdit = (TextView) findViewById(R.id.tv_shoppingCart_edit);
mTvEdit.setOnClickListener(this);
mTvTotalPrice = (TextView) findViewById(R.id.tv_tv_cb_shoppingCart_all_total_price);
mCbSelectAll = (CheckBox) findViewById(R.id.cb_shoppingCart_all);
mCbSelectAll.setOnClickListener(this);
mBtCheckout = (Button) findViewById(R.id.bt_shoppingCart_checkout);
mRvShoppingCart = (RecyclerView) findViewById(R.id.rv_tv_cb_shoppingCart);
mRvShoppingCart.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
mProductAdapter =new ProductAdapter(getApplicationContext(),productList,this);
mRvShoppingCart.setAdapter(mProductAdapter);
}
ProductAdapter代码
我这边加载网络图片用的是Picasso,注意引包,也可以用其他框架
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.MyViewHolder> {
private Context mContext;
private List<Product> productList;
public ProductAdapter(Context context, List<Product> productList){
this.mContext = context;
this.productList = productList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(
mContext).inflate(R.layout.item_product, parent, false));
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
Picasso.with(mContext).load(productList.get(position).getImg()).error(R.mipmap.ic_launcher).into(holder.mIvPic);
holder.mTvName.setText(productList.get(position).getProduct_name());
holder.mTvNum.setText(productList.get(position).getProduct_num() + "");
holder.mTvPrice.setText(productList.get(position).getPrice() + "");
holder.mTvEditNum.setText(productList.get(position).getProduct_num() + "");
}
@Override
public int getItemCount() {
return productList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
CheckBox mCb;
ImageView mIvPic, mIvAdd, mIvMinus;
TextView mTvName,mTvPrice, mTvNum, mTvEditNum;
Button mBtDelete;
RelativeLayout mRlNormal, mRlEdit;
public MyViewHolder(View itemView) {
super(itemView);
mCb = (CheckBox) itemView.findViewById(R.id.cb_item_product_cb);
mIvPic = (ImageView) itemView.findViewById(R.id.iv_item_product_img);
mIvAdd = (ImageView) itemView.findViewById(R.id.iv_item_product_add);
mIvMinus = (ImageView) itemView.findViewById(R.id.iv_item_product_minus);
mTvName = (TextView) itemView.findViewById(R.id.tv_item_product_name);
mTvPrice = (TextView) itemView.findViewById(R.id.tv_item_product_price);
mTvNum = (TextView) itemView.findViewById(R.id.tv_item_product_num);
mTvEditNum = (TextView) itemView.findViewById(R.id.tv_item_product_edit_num);
mBtDelete = (Button) itemView.findViewById(R.id.bt_item_product_delete);
mRlNormal = (RelativeLayout) itemView.findViewById(R.id.rl_item_product_normal);
mRlEdit = (RelativeLayout) itemView.findViewById(R.id.rl_item_product_edit);
}
}
}
到这里recycleview已经加载好数据了
2、业务实现
先来整理一下需要实现哪些业务
1、编辑(控制item布局显示)
2、添加减少选购的数量
3、删除(去掉选中的产品)
4、商品选择(需要判断是否被全选以及修改总价和选中的商品数量)
5、全选
6、价格动态变化
编辑
其实就是切换2种布局,之前的item布局文件应该看到有个布局我设置为gone隐藏了,只要我们在点击编辑后切换布局的显示隐藏即可
首先在adapter中定义一个全局的boolean变量flag,默认为false
然后在adpater中写个Edit方法,当用户点击编辑按钮时候调用,并刷新adpater,下面是部分实现代码
ProductAdapter代码:
private boolean falg = false;
public void Edit(boolean flag) {
this.falg = flag;
this.notifyDataSetChanged();
}
adapter的onBindViewHolder中添加:
if (falg) {
holder.mRlNormal.setVisibility(View.GONE);
holder.mRlEdit.setVisibility(View.VISIBLE);
} else {
holder.mRlNormal.setVisibility(View.VISIBLE);
holder.mRlEdit.setVisibility(View.GONE);
}
MainActivity中编辑按钮点击事件
if (mProductAdapter != null) {
if (mTvEdit.getText().equals("编辑")) {
mTvEdit.setText("完成");
mProductAdapter.Edit(true);
} else {
mTvEdit.setText("编辑");
mProductAdapter.Edit(false);
}
}
添加减少选购的数量
添加减少按钮是item内部控件,所以直接在onBindViewHolder添加相应实现即可
//添加操作
holder.mIvAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
productList.get(position).setProduct_num(productList.get(position).getProduct_num() + 1);
notifyDataSetChanged();
}
});
//减少操作
holder.mIvMinus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (productList.get(position).getProduct_num() > 1) {
productList.get(position).setProduct_num(productList.get(position).getProduct_num() - 1);
notifyDataSetChanged();
}
}
});
删除
删除也是item内部控件,直接在onBindViewHolder实现相应业务即可
//删除操作
holder.mBtDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
productList.remove(position);
notifyDataSetChanged();
}
});
商品选择
同样还是item内部控件,直接在onBindViewHolder添加即可
//选中操作
holder.mCb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
if (cb.isChecked())
productList.get(position).setSelected(true);
else
productList.get(position).setSelected(false);
notifyDataSetChanged();
}
}
});
全选
关于商品的的选择,我在商品对象中添加了一个字段isSelected代表是否被选中,那么全选和取消全选即把对象列表中的isSelected字段改为true和false。
在adapter中添加方法selectALL
public void selectALL(boolean isChecked) {
for (int i = 0; i < productList.size(); i++) {
if (isChecked)
productList.get(i).setSelected(true);
else
productList.get(i).setSelected(false);
}
this.notifyDataSetChanged();
}
MainActivity全选控件点击事件
if (mProductAdapter != null) {
CheckBox cb = (CheckBox) v;
if (cb.isChecked())
mProductAdapter.selectALL(true);
else
mProductAdapter.selectALL(false);
}
价格动态变化
当我们选择商品,修改数量,删除商品都需要变化。变化的显示是在activity中的控件,所已当我们做以上操作后,需要通知activity改变。
我们在adpater中定义一个接口,用来通知activity
public interface IProductSelectCallback {
void update(List<Product> products);
}
然后我们给adapter的构造函数添加接口参数,并在相应操作地方添加接口回掉,最后看activity中回掉内的实现
@Override
public void update(List<Product> products) {
this.productList = products;
int count = 0;//选中的商品种类
totalPrice = 0.00;//总价值
for (int i = 0; i < products.size(); i++) {
if (products.get(i).isSelected()) {
totalPrice = totalPrice + products.get(i).getPrice() * products.get(i).getProduct_num();
count++;
}
}
if (count == products.size())
mCbSelectAll.setChecked(true);
else
mCbSelectAll.setChecked(false);
BigDecimal b = new BigDecimal(totalPrice);
mTvTotalPrice.setText(b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue() + "");
mBtCheckout.setText("去结算(" + totalProduct + ")");
}
最后附上demo下载地址