Android App开发实战项目之购物车(附源码 超详细必看)
需要源码请点赞关注收藏后评论区留言~~~
一、需求描述
电商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; // 声明一个购物车数据库的帮助器对象 @Override protected 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); } @Override public 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, "成功添加至购物车"); } @Override protected void onResume() { super.onResume(); // 获取商品数据库的帮助器对象 mGoodsHelper = GoodsDBHelper.getInstance(this, 1); mGoodsHelper.openReadLink(); // 打开商品数据库的读连接 // 获取购物车数据库的帮助器对象 mCartHelper = CartDBHelper.getInstance(this, 1); mCartHelper.openWriteLink(); // 打开购物车数据库的写连接 showDetail(); // 展示商品详情 } @Override protected 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; // 声明一个购物车数据库的帮助器对象 @Override protected 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); } @Override public 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 + "到购物车"); } @Override protected void onResume() { super.onResume(); tv_count.setText("" + MainApplication.goodsCount); // 获取商品数据库的帮助器对象 mGoodsHelper = GoodsDBHelper.getInstance(this, 1); mGoodsHelper.openReadLink(); // 打开商品数据库的读连接 // 获取购物车数据库的帮助器对象 mCartHelper = CartDBHelper.getInstance(this, 1); mCartHelper.openWriteLink(); // 打开购物车数据库的写连接 showGoods(); // 展示商品列表 } @Override protected 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() { @Override public 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() { @Override public 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; // 声明一个购物车数据库的帮助器对象 @Override protected 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); } } @Override public 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(); // 显示提醒对话框 } } @Override protected void onResume() { super.onResume(); showCount(); // 显示购物车的商品数量 // 获取商品数据库的帮助器对象 mGoodsHelper = GoodsDBHelper.getInstance(this, 1); mGoodsHelper.openWriteLink(); // 打开商品数据库的写连接 // 获取购物车数据库的帮助器对象 mCartHelper = CartDBHelper.getInstance(this, 1); mCartHelper.openWriteLink(); // 打开购物车数据库的写连接 downloadGoods(); // 模拟从网络下载商品图片 showCart(); // 展示购物车中的商品列表 } @Override protected 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() { @Override public 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() { @Override public boolean onLongClick(final View v) { AlertDialog.Builder builder = new AlertDialog.Builder(ShoppingCartActivity.this); builder.setMessage("是否从购物车删除"+goods.name+"?"); builder.setPositiveButton("是", new DialogInterface.OnClickListener() { @Override public 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文件如下
--------------- ---------------
创作不易 觉得有帮助请点赞关注收藏~~~
来源地址:https://blog.csdn.net/jiebaoshayebuhui/article/details/127738664
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341