需要源码请点赞关注收藏后评论区留言~~~

一、需求描述

电商App的购物车可谓是司空见惯了,可以知道购物车除了底部有一个结算行,其余部分主要是已加入购物车的商品列表,然后每个商品左边是商品小图,右边是商品名称以及价格,第一次进入购物车页面应该是空的,随着加入东西的增加而增加。并且在其他界面也能看到购物车,比如有新商品加入时数字会加一

二、界面设计

主要用到了以下控件

线性布局

网格布局

相对布局

数据库SQLite

全局内存

存储卡文件

共享参数SharedPreferences

效果如下

三、关键部分

1:关于页面跳转

因为购物车页面允许直接跳到商场页面,并且商场页面也允许跳到购物车页面,所以如果用户在这两个页面之间来回跳转,然后再按返回键,结果发现返回的时候也是在这两个页面之间跳转,出现问题的原因在于:每次启动活动页面都往活动栈中加入一个新活动,那么返回出栈之时,也只好一个个活动依次退出了

2:关于商品图片的缓存

通常商品图片由后端服务器提供,App打开页面时再从服务器下载所需的商品图,可是购物车模块的多个页面都会展示商品图片,如果每次都到服务器请求图片,显然非常消耗时间和流浪,因此App都会缓存常用的图片,一旦从服务器成功下载图片,便在手机储存卡上保存图片文件。然后下次界面需要加载商品图片时,就先从存储卡寻找该图片,如果找到就读取,没找到再去服务器下载

四、部分源码

ShoppingDetailActivity

package com.example.chapter06;import android.annotation.SuppressLint;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter06.bean.GoodsInfo;import com.example.chapter06.database.CartDBHelper;import com.example.chapter06.database.GoodsDBHelper;import com.example.chapter06.util.ToastUtil;@SuppressLint("SetTextI18n")public class ShoppingDetailActivity extends AppCompatActivity implements View.OnClickListener {private TextView tv_title;private TextView tv_count;private TextView tv_goods_price;private TextView tv_goods_desc;private ImageView iv_goods_pic;private long mGoodsId; // 当前商品的商品编号private GoodsDBHelper mGoodsHelper; // 声明一个商品数据库的帮助器对象private CartDBHelper mCartHelper; // 声明一个购物车数据库的帮助器对象@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shopping_detail);tv_title = findViewById(R.id.tv_title);tv_count = findViewById(R.id.tv_count);tv_goods_price = findViewById(R.id.tv_goods_price);tv_goods_desc = findViewById(R.id.tv_goods_desc);iv_goods_pic = findViewById(R.id.iv_goods_pic);findViewById(R.id.iv_back).setOnClickListener(this);findViewById(R.id.iv_cart).setOnClickListener(this);findViewById(R.id.btn_add_cart).setOnClickListener(this);tv_count.setText("" + MainApplication.goodsCount);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.iv_back) { // 点击了返回图标finish(); // 关闭当前页面} else if (v.getId() == R.id.iv_cart) { // 点击了购物车图标Intent intent = new Intent(this, ShoppingCartActivity.class);startActivity(intent); // 跳转到购物车页面} else if (v.getId() == R.id.btn_add_cart) { // 点击了“添加”按钮addToCart(mGoodsId); // 把该商品添加到购物车}}// 把指定编号的商品添加到购物车private void addToCart(long goods_id) {MainApplication.goodsCount++;tv_count.setText("" + MainApplication.goodsCount);mCartHelper.save(goods_id); // 把该商品填入购物车数据库ToastUtil.show(this, "成功添加至购物车");}@Overrideprotected void onResume() {super.onResume();// 获取商品数据库的帮助器对象mGoodsHelper = GoodsDBHelper.getInstance(this, 1);mGoodsHelper.openReadLink(); // 打开商品数据库的读连接// 获取购物车数据库的帮助器对象mCartHelper = CartDBHelper.getInstance(this, 1);mCartHelper.openWriteLink(); // 打开购物车数据库的写连接showDetail(); // 展示商品详情}@Overrideprotected void onPause() {super.onPause();mGoodsHelper.closeLink(); // 关闭商品数据库的数据库连接mCartHelper.closeLink(); // 关闭购物车数据库的数据库连接}private void showDetail() {// 获取上一个页面传来的商品编号mGoodsId = getIntent().getLongExtra("goods_id", 0L);if (mGoodsId > 0) {// 根据商品编号查询商品数据库中的商品记录GoodsInfo info = mGoodsHelper.queryById(mGoodsId);tv_title.setText(info.name); // 设置商品名称tv_goods_desc.setText(info.desc); // 设置商品描述tv_goods_price.setText("" + (int)info.price); // 设置商品价格iv_goods_pic.setImageURI(Uri.parse(info.pic_path)); // 设置商品图片}}}

ShoppingChannelActivity类

package com.example.chapter06;import android.annotation.SuppressLint;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.widget.Button;import android.widget.GridLayout;import android.widget.ImageView;import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter06.bean.GoodsInfo;import com.example.chapter06.database.CartDBHelper;import com.example.chapter06.database.GoodsDBHelper;import com.example.chapter06.util.ToastUtil;import java.util.ArrayList;import java.util.List;@SuppressLint("SetTextI18n")public class ShoppingChannelActivity extends AppCompatActivity implements View.OnClickListener {private TextView tv_count;private GridLayout gl_channel; // 声明一个商品频道的网格布局对象private GoodsDBHelper mGoodsHelper; // 声明一个商品数据库的帮助器对象private CartDBHelper mCartHelper; // 声明一个购物车数据库的帮助器对象@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shopping_channel);TextView tv_title = findViewById(R.id.tv_title);tv_title.setText("手机商场");tv_count = findViewById(R.id.tv_count);gl_channel = findViewById(R.id.gl_channel);findViewById(R.id.iv_back).setOnClickListener(this);findViewById(R.id.iv_cart).setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.iv_back) { // 点击了返回图标finish(); // 关闭当前页面} else if (v.getId() == R.id.iv_cart) { // 点击了购物车图标// 从商场页面跳到购物车页面Intent intent = new Intent(this, ShoppingCartActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志startActivity(intent); // 跳转到购物车页面}}// 把指定编号的商品添加到购物车private void addToCart(long goods_id, String goods_name) {MainApplication.goodsCount++;tv_count.setText("" + MainApplication.goodsCount);mCartHelper.save(goods_id); // 把该商品填入购物车数据库ToastUtil.show(this, "已添加一部" + goods_name + "到购物车");}@Overrideprotected void onResume() {super.onResume();tv_count.setText("" + MainApplication.goodsCount);// 获取商品数据库的帮助器对象mGoodsHelper = GoodsDBHelper.getInstance(this, 1);mGoodsHelper.openReadLink(); // 打开商品数据库的读连接// 获取购物车数据库的帮助器对象mCartHelper = CartDBHelper.getInstance(this, 1);mCartHelper.openWriteLink(); // 打开购物车数据库的写连接showGoods(); // 展示商品列表}@Overrideprotected void onPause() {super.onPause();mGoodsHelper.closeLink(); // 关闭商品数据库的数据库连接mCartHelper.closeLink(); // 关闭购物车数据库的数据库连接}private void showGoods() {gl_channel.removeAllViews(); // 移除下面的所有子视图// 查询商品数据库中的所有商品记录List goodsArray = mGoodsHelper.query("1=1");for (final GoodsInfo info : goodsArray) {// 获取布局文件item_goods.xml的根视图View view = LayoutInflater.from(this).inflate(R.layout.item_goods, null);ImageView iv_thumb = view.findViewById(R.id.iv_thumb);TextView tv_name = view.findViewById(R.id.tv_name);TextView tv_price = view.findViewById(R.id.tv_price);Button btn_add = view.findViewById(R.id.btn_add);tv_name.setText(info.name); // 设置商品名称iv_thumb.setImageURI(Uri.parse(info.pic_path)); // 设置商品图片iv_thumb.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(ShoppingChannelActivity.this, ShoppingDetailActivity.class);intent.putExtra("goods_id", info.rowid);startActivity(intent); // 跳到商品详情页面}});tv_price.setText("" + (int)info.price); // 设置商品价格btn_add.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {addToCart(info.rowid, info.name); // 添加到购物车}});gl_channel.addView(view); // 把商品视图添加到网格布局}}}

ShoppingCartActivity类

package com.example.chapter06;import android.annotation.SuppressLint;import android.app.AlertDialog;import android.content.DialogInterface;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter06.bean.CartInfo;import com.example.chapter06.bean.GoodsInfo;import com.example.chapter06.database.CartDBHelper;import com.example.chapter06.database.GoodsDBHelper;import com.example.chapter06.util.FileUtil;import com.example.chapter06.util.SharedUtil;import com.example.chapter06.util.ToastUtil;import java.util.ArrayList;import java.util.HashMap;import java.util.List;@SuppressLint("SetTextI18n")public class ShoppingCartActivity extends AppCompatActivity implements View.OnClickListener {private final static String TAG = "ShoppingCartActivity";private TextView tv_count;private TextView tv_total_price;private LinearLayout ll_content;private LinearLayout ll_cart; // 声明一个购物车列表的线性布局对象private LinearLayout ll_empty;private GoodsDBHelper mGoodsHelper; // 声明一个商品数据库的帮助器对象private CartDBHelper mCartHelper; // 声明一个购物车数据库的帮助器对象@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shopping_cart);TextView tv_title = findViewById(R.id.tv_title);tv_title.setText("购物车");tv_count = findViewById(R.id.tv_count);tv_total_price = findViewById(R.id.tv_total_price);ll_content = findViewById(R.id.ll_content);ll_cart = findViewById(R.id.ll_cart);ll_empty = findViewById(R.id.ll_empty);findViewById(R.id.iv_back).setOnClickListener(this);findViewById(R.id.btn_shopping_channel).setOnClickListener(this);findViewById(R.id.btn_clear).setOnClickListener(this);findViewById(R.id.btn_settle).setOnClickListener(this);}// 显示购物车图标中的商品数量private void showCount() {tv_count.setText("" + MainApplication.goodsCount);if (MainApplication.goodsCount == 0) {ll_content.setVisibility(View.GONE);ll_cart.removeAllViews(); // 移除下面的所有子视图mGoodsMap.clear();ll_empty.setVisibility(View.VISIBLE);} else {ll_content.setVisibility(View.VISIBLE);ll_empty.setVisibility(View.GONE);}}@Overridepublic void onClick(View v) {if (v.getId() == R.id.iv_back) { // 点击了返回图标finish(); // 关闭当前页面} else if (v.getId() == R.id.btn_shopping_channel) { // 点击了“商场”按钮// 从购物车页面跳到商场页面Intent intent = new Intent(this, ShoppingChannelActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志startActivity(intent); // 跳转到手机商场页面} else if (v.getId() == R.id.btn_clear) { // 点击了“清空”按钮mCartHelper.deleteAll(); // 清空购物车数据库MainApplication.goodsCount = 0;showCount(); // 显示最新的商品数量ToastUtil.show(this, "购物车已清空");} else if (v.getId() == R.id.btn_settle) { // 点击了“结算”按钮AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("结算商品");builder.setMessage("客官抱歉,支付功能尚未开通,请下次再来");builder.setPositiveButton("我知道了", null);builder.create().show(); // 显示提醒对话框}}@Overrideprotected void onResume() {super.onResume();showCount(); // 显示购物车的商品数量// 获取商品数据库的帮助器对象mGoodsHelper = GoodsDBHelper.getInstance(this, 1);mGoodsHelper.openWriteLink(); // 打开商品数据库的写连接// 获取购物车数据库的帮助器对象mCartHelper = CartDBHelper.getInstance(this, 1);mCartHelper.openWriteLink(); // 打开购物车数据库的写连接downloadGoods(); // 模拟从网络下载商品图片showCart(); // 展示购物车中的商品列表}@Overrideprotected void onPause() {super.onPause();mGoodsHelper.closeLink(); // 关闭商品数据库的数据库连接mCartHelper.closeLink(); // 关闭购物车数据库的数据库连接}// 声明一个购物车中的商品信息列表private List mCartArray = new ArrayList();// 声明一个根据商品编号查找商品信息的映射private HashMap mGoodsMap = new HashMap();private void deleteGoods(CartInfo info) {MainApplication.goodsCount -= info.count;// 从购物车的数据库中删除商品mCartHelper.delete("goods_id=" + info.goods_id);// 从购物车的列表中删除商品for (int i = 0; i < mCartArray.size(); i++) {if (info.goods_id == mCartArray.get(i).goods_id) {mCartArray.remove(i);break;}}showCount(); // 显示最新的商品数量ToastUtil.show(this, "已从购物车删除" + mGoodsMap.get(info.goods_id).name);mGoodsMap.remove(info.goods_id);refreshTotalPrice(); // 刷新购物车中所有商品的总金额}// 展示购物车中的商品列表private void showCart() {ll_cart.removeAllViews(); // 移除下面的所有子视图mCartArray = mCartHelper.query("1=1"); // 查询购物车数据库中所有的商品记录Log.d(TAG, "mCartArray.size()=" + mCartArray.size());if (mCartArray == null || mCartArray.size() <= 0) {return;}for (int i = 0; i < mCartArray.size(); i++) {final CartInfo info = mCartArray.get(i);// 根据商品编号查询商品数据库中的商品记录final GoodsInfo goods = mGoodsHelper.queryById(info.goods_id);Log.d(TAG, "name=" + goods.name + ",price=" + goods.price + ",desc=" + goods.desc);mGoodsMap.put(info.goods_id, goods);// 获取布局文件item_goods.xml的根视图View view = LayoutInflater.from(this).inflate(R.layout.item_cart, null);ImageView iv_thumb = view.findViewById(R.id.iv_thumb);TextView tv_name = view.findViewById(R.id.tv_name);TextView tv_desc = view.findViewById(R.id.tv_desc);TextView tv_count = view.findViewById(R.id.tv_count);TextView tv_price = view.findViewById(R.id.tv_price);TextView tv_sum = view.findViewById(R.id.tv_sum);// 给商品行添加点击事件。点击商品行跳到商品的详情页view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(ShoppingCartActivity.this, ShoppingDetailActivity.class);intent.putExtra("goods_id", info.goods_id);startActivity(intent); // 跳到商品详情页面}});// 给商品行添加长按事件。长按商品行就删除该商品view.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(final View v) {AlertDialog.Builder builder = new AlertDialog.Builder(ShoppingCartActivity.this);builder.setMessage("是否从购物车删除"+goods.name+"?");builder.setPositiveButton("是", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ll_cart.removeView(v); // 移除当前视图deleteGoods(info); // 删除该商品}});builder.setNegativeButton("否", null);builder.create().show(); // 显示提醒对话框return true;}});iv_thumb.setImageURI(Uri.parse(goods.pic_path)); // 设置商品图片tv_name.setText(goods.name); // 设置商品名称tv_desc.setText(goods.desc); // 设置商品描述tv_count.setText("" + info.count); // 设置商品数量tv_price.setText("" + (int)goods.price); // 设置商品单价tv_sum.setText("" + (int)(info.count * goods.price)); // 设置商品总价ll_cart.addView(view); // 往购物车列表添加该商品行}refreshTotalPrice(); // 重新计算购物车中的商品总金额}// 重新计算购物车中的商品总金额private void refreshTotalPrice() {int total_price = 0;for (CartInfo info : mCartArray) {GoodsInfo goods = mGoodsMap.get(info.goods_id);total_price += goods.price * info.count;}tv_total_price.setText("" + total_price);}private String mFirst = "true"; // 是否首次打开// 模拟网络数据,初始化数据库中的商品信息private void downloadGoods() {// 获取共享参数保存的是否首次打开参数mFirst = SharedUtil.getIntance(this).readString("first", "true");// 获取当前App的私有下载路径String path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/";if (mFirst.equals("true")) { // 如果是首次打开ArrayList goodsList = GoodsInfo.getDefaultList(); // 模拟网络图片下载for (int i = 0; i < goodsList.size(); i++) {GoodsInfo info = goodsList.get(i);long rowid = mGoodsHelper.insert(info); // 往商品数据库插入一条该商品的记录info.rowid = rowid;Bitmap pic = BitmapFactory.decodeResource(getResources(), info.pic);String pic_path = path + rowid + ".jpg";FileUtil.saveImage(pic_path, pic); // 往存储卡保存商品图片pic.recycle(); // 回收位图对象info.pic_path = pic_path;mGoodsHelper.update(info); // 更新商品数据库中该商品记录的图片路径}}// 把是否首次打开写入共享参数SharedUtil.getIntance(this).writeString("first", "false");}}

XML文件如下

------------------------------

创作不易 觉得有帮助请点赞关注收藏~~~