從零開始:Android Studio開發購物車(第二個實戰項目)

一年經驗的全棧程序員,目前頭發健在,但不知道能撐多久。

文章目錄

前言

?一、頁面編寫

1. 頂部標簽欄title_shopping.xml

2. 商品展現列表activity_shopping_channel.xml

?3. 商品詳情頁面activity_shopping_detail.xml

4. 購物車頁面activity_shopping_cart.xml

5. 創建商品展示單元格item_goods.xml

?6. 創建購物車商品展示單元格

?二、Room基礎配置

1. 添加依賴

2. 添加購物車實體

3. 添加商品實體

4. 創建數據庫DAO層

?5. 創建數據庫實例方法

?三、Application全局化

1.引導入相關依賴

2.相關代碼編寫

3. 修改AndroidManifest.xml

四、業務邏輯代碼

1. 商品展示頁面

2.? 商品詳情頁面

3.? 購物車頁面

?五、項目結構示意圖?

六、成果展示?

總結

🙌?求點贊、收藏、關注!?


前言

經過前幾天的Android速成學習,我決定需要用一個實戰項目來鞏固知識。所以選擇了購物車是剛剛好的。

購物車功能作為電商應用的核心組件之一,其實現方式和性能表現直接影響用戶體驗。傳統的購物車實現往往只存儲商品ID和數量等基本信息,當用戶離線查看購物車時,商品圖片需要重新從網絡加載,這不僅增加了流量消耗,也降低了用戶體驗的連貫性。

本文將帶你深入探索如何利用Android官方推薦的Room持久化庫,構建一個功能完善且性能優異的購物車模塊。與常規實現不同,我們的方案將重點解決以下技術難點:

  1. 本地圖片存儲:直接將商品圖片以Blob形式存入Room數據庫,確保用戶離線狀態下仍能完整查看購物車內容

  2. 數據關系建模:使用Room的關系型數據庫特性,建立商品與購物車項之間的關聯

  3. 性能優化:針對圖片存儲可能帶來的性能問題,提供切實可行的解決方案

  4. UI與數據同步:實現RecyclerView與數據庫的實時聯動更新

通過本實戰項目,你不僅能掌握Room數據庫的高級用法,還能學習到如何在實際項目中平衡功能需求與技術實現。無論你是Android開發新手還是有一定經驗的開發者,相信這篇實戰指南都能為你帶來有價值的參考。


?一、頁面編寫

1. 頂部標簽欄title_shopping.xml

<!-- 相對布局:作為標題欄容器,高度固定為50dp,背景為淺藍色 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"  <!-- 寬度撐滿父容器 -->android:layout_height="50dp"        <!-- 固定高度50dp -->android:background="#aaaaff" >      <!-- 背景色(淺藍色) --><!-- 返回按鈕圖標 --><!-- 左對齊父布局,固定寬度50dp,高度撐滿父布局 --><ImageViewandroid:id="@+id/iv_back"      <!-- 控件ID(代碼中可通過findViewById操作) -->android:layout_width="50dp"     <!-- 固定寬度 -->android:layout_height="match_parent"  <!-- 高度撐滿父布局 -->android:layout_alignParentLeft="true"  <!-- 對齊父布局左側 -->android:padding="10dp"          <!-- 內邊距(讓圖標看起來更協調) -->android:scaleType="fitCenter"    <!-- 圖片縮放模式:居中適應 -->android:src="@drawable/ic_back" />  <!-- 圖標資源 --><!-- 居中標題文本 --><TextViewandroid:id="@+id/tv_title"      <!-- 控件ID -->android:layout_width="wrap_content"  <!-- 寬度根據文本內容自適應 -->android:layout_height="match_parent"  <!-- 高度撐滿父布局 -->android:layout_centerInParent="true"  <!-- 在父布局中居中 -->android:gravity="center"        <!-- 文本內容居中顯示 -->android:textColor="@color/black"  <!-- 文本顏色 -->android:textSize="20sp" />      <!-- 文本大小 --><!-- 購物車圖標 --><!-- 右對齊父布局,固定寬度50dp,高度撐滿父布局 --><ImageViewandroid:id="@+id/iv_cart"       <!-- 控件ID -->android:layout_width="50dp"     <!-- 固定寬度 -->android:layout_height="match_parent"  <!-- 高度撐滿父布局 -->android:layout_alignParentRight="true"  <!-- 對齊父布局右側 -->android:scaleType="fitCenter"    <!-- 圖片縮放模式:居中適應 -->android:src="@drawable/cart" />  <!-- 圖標資源 --><!-- 購物車商品數量角標(紅色圓形背景+白色數字) --><TextViewandroid:id="@+id/tv_count"      <!-- 控件ID -->android:layout_width="20dp"    <!-- 固定寬度 -->android:layout_height="20dp"   <!-- 固定高度 -->android:layout_alignParentTop="true"  <!-- 對齊父布局頂部 -->android:layout_toRightOf="@+id/iv_cart"  <!-- 位于購物車圖標右側 -->android:layout_marginLeft="-20dp"  <!-- 負邊距實現與購物車圖標重疊 -->android:gravity="center"       <!-- 文本居中 -->android:background="@drawable/shape_oval_red"  <!-- 紅色圓形背景(需自定義shape) -->android:text="0"               <!-- 默認顯示數量0 -->android:textColor="@color/white"  <!-- 文本顏色(白色) -->android:textSize="15sp" />      <!-- 文本大小 --></RelativeLayout>

2. 商品展現列表activity_shopping_channel.xml

<!-- 根布局:垂直方向的LinearLayout,占滿整個屏幕,背景為橙色 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"  <!-- 寬度匹配父容器 -->android:layout_height="match_parent" <!-- 高度匹配父容器 -->android:orientation="vertical" >    <!-- 子元素垂直排列 --><!-- 引入標題欄布局(復用公共標題欄) --><!-- 說明:此處通過include標簽復用預先定義的標題欄布局文件title_shopping.xml --><include layout="@layout/title_shopping" /><!-- 可滾動的容器:ScrollView(解決內容超出屏幕時的滾動問題) --><ScrollViewandroid:layout_width="match_parent"  <!-- 寬度匹配父容器 -->android:layout_height="wrap_content"> <!-- 高度根據內容自適應 --><!-- 網格布局:GridLayout(用于實現2列的網格排列) --><GridLayoutandroid:id="@+id/gl_channel"     <!-- 設置ID便于代碼中動態操作 -->android:layout_width="match_parent"  <!-- 寬度匹配父容器(ScrollView) -->android:layout_height="wrap_content"  <!-- 高度根據內容自適應 -->android:columnCount="2" />      <!-- 指定網格列數為2(關鍵屬性) --></ScrollView></LinearLayout>

?3. 商品詳情頁面activity_shopping_detail.xml

<!-- 主布局:垂直方向的LinearLayout,占滿整個屏幕,背景為橙色 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"  <!-- 寬度匹配父容器(全屏寬度) -->android:layout_height="match_parent" <!-- 高度匹配父容器(全屏高度) -->android:background="@color/orange"   <!-- 設置背景顏色為橙色 -->android:orientation="vertical">     <!-- 子元素垂直排列 --><!-- 引入標題欄布局 --><!-- 通過include標簽復用定義好的標題欄布局文件(title_shopping.xml) --><include layout="@layout/title_shopping" /><!-- 可滾動視圖:用于支持內容超出屏幕時的滾動 --><ScrollViewandroid:layout_width="match_parent"  <!-- 寬度匹配父容器 -->android:layout_height="wrap_content"> <!-- 高度根據內容自適應 --><!-- 內容容器:垂直方向的LinearLayout,包含商品詳情各個元素 --><LinearLayoutandroid:layout_width="match_parent"  <!-- 寬度匹配父容器(ScrollView) -->android:layout_height="wrap_content"  <!-- 高度根據內容自適應 -->android:orientation="vertical">      <!-- 子元素垂直排列 --><!-- 商品圖片展示區域 --><ImageViewandroid:id="@+id/iv_goods_pic"  <!-- 控件ID(用于代碼中訪問) -->android:layout_width="match_parent"  <!-- 寬度撐滿父容器 -->android:layout_height="350dp"    <!-- 固定高度350dp -->android:scaleType="fitCenter" /> <!-- 圖片縮放模式:居中適應 --><!-- 商品價格顯示 --><TextViewandroid:id="@+id/tv_goods_price"  <!-- 控件ID -->android:layout_width="match_parent"  <!-- 寬度撐滿父容器 -->android:layout_height="wrap_content"  <!-- 高度根據內容自適應 -->android:paddingLeft="5dp"        <!-- 左側內邊距5dp -->android:textColor="@color/red"  <!-- 文本顏色為紅色 -->android:textSize="22sp" />      <!-- 文本大小22sp --><!-- 商品描述文本 --><TextViewandroid:id="@+id/tv_goods_desc"  <!-- 控件ID -->android:layout_width="match_parent"  <!-- 寬度撐滿父容器 -->android:layout_height="wrap_content"  <!-- 高度根據內容自適應 -->android:paddingLeft="5dp"        <!-- 左側內邊距5dp -->android:textColor="@color/black"  <!-- 文本顏色為黑色 -->android:textSize="15sp" />       <!-- 文本大小15sp --><!-- 加入購物車按鈕 --><Buttonandroid:id="@+id/btn_add_cart"  <!-- 控件ID -->android:layout_width="match_parent"  <!-- 寬度撐滿父容器 -->android:layout_height="wrap_content"  <!-- 高度根據內容自適應 -->android:text="加入購物車"        <!-- 按鈕文本 -->android:textColor="@color/black"  <!-- 文本顏色為黑色 -->android:textSize="17sp" />       <!-- 文本大小17sp --></LinearLayout></ScrollView>
</LinearLayout>

4. 購物車頁面activity_shopping_cart.xml

<!-- 主布局:垂直方向的LinearLayout,占滿整個屏幕 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"  <!-- 寬度匹配父容器(全屏寬度) -->android:layout_height="match_parent" <!-- 高度匹配父容器(全屏高度) -->android:orientation="vertical">     <!-- 子元素垂直排列 --><!-- 引入標題欄布局 --><!-- 復用定義好的標題欄布局文件(title_shopping.xml) --><include layout="@layout/title_shopping" /><!-- 可滾動視圖:支持內容超出屏幕時的滾動 --><ScrollViewandroid:layout_width="match_parent"  <!-- 寬度匹配父容器 -->android:layout_height="wrap_content"> <!-- 高度根據內容自適應 --><!-- 相對布局容器:用于切換顯示購物車內容/空狀態 --><RelativeLayoutandroid:layout_width="match_parent"  <!-- 寬度匹配父容器 -->android:layout_height="wrap_content"> <!-- 高度根據內容自適應 --><!-- 購物車內容區域(默認顯示) --><LinearLayoutandroid:id="@+id/ll_content"android:layout_width="match_parent"  <!-- 寬度撐滿父容器 -->android:layout_height="wrap_content"  <!-- 高度根據內容自適應 -->android:orientation="vertical"      <!-- 子元素垂直排列 -->android:visibility="visible">      <!-- 初始可見 --><!-- 表頭布局:水平排列的商品信息標題 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"> <!-- 子元素水平排列 --><!-- 圖片標題(固定寬度85dp) --><TextViewandroid:layout_width="85dp"android:layout_height="wrap_content"android:gravity="center"  <!-- 文本居中 -->android:text="圖片"android:textColor="@color/black"android:textSize="15sp" /><!-- 商品名稱標題(權重3,占比最大) --><TextViewandroid:layout_width="0dp"  <!-- 權重布局必須設為0dp -->android:layout_height="wrap_content"android:layout_weight="3"   <!-- 寬度權重占比 -->android:gravity="center"android:text="名稱"android:textColor="@color/black"android:textSize="15sp" /><!-- 數量標題 --><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="數量"android:textColor="@color/black"android:textSize="15sp" /><!-- 單價標題 --><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="單價"android:textColor="@color/black"android:textSize="15sp" /><!-- 總價標題 --><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:text="總價"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout><!-- 動態內容容器:用于代碼中添加購物車商品條目 --><LinearLayoutandroid:id="@+id/ll_cart"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical" /><!-- 底部操作欄:水平排列 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="0dp">  <!-- 去除內邊距 --><!-- 清空按鈕 --><Buttonandroid:id="@+id/btn_clear"android:layout_width="wrap_content"  <!-- 寬度根據文本自適應 -->android:layout_height="wrap_content"android:gravity="center"android:text="清空"android:textColor="@color/black"android:textSize="17sp" /><!-- 占位文本(自動擴展剩余空間) --><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"  <!-- 占據剩余空間 -->android:gravity="center|right"  <!-- 右對齊且垂直居中 -->android:text="總金額:"android:textColor="@color/black"android:textSize="17sp" /><!-- 總金額顯示(紅色突出) --><TextViewandroid:id="@+id/tv_total_price"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="10dp"  <!-- 右側外邊距 -->android:gravity="center|left"       <!-- 左對齊且垂直居中 -->android:textColor="@color/red"      <!-- 紅色文本 -->android:textSize="25sp" />          <!-- 大號字體 --><!-- 結算按鈕 --><Buttonandroid:id="@+id/btn_settle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:text="結算"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></LinearLayout><!-- 空狀態提示區域(默認隱藏) --><LinearLayoutandroid:id="@+id/ll_empty"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="gone">  <!-- 初始不可見 --><!-- 提示文本(上下外邊距各100dp) --><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="100dp"android:layout_marginTop="100dp"android:gravity="center"android:text="哎呀,購物車空空如也,快去選購商品吧"android:textColor="@color/black"android:textSize="17sp" /><!-- 跳轉按鈕 --><Buttonandroid:id="@+id/btn_shopping_channel"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="逛逛手機商場"android:textColor="@color/black"android:textSize="17sp" /></LinearLayout></RelativeLayout></ScrollView>
</LinearLayout>

5. 創建商品展示單元格item_goods.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/ll_item"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:background="@color/white"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textColor="@color/black"android:textSize="17sp" /><ImageViewandroid:id="@+id/iv_thumb"android:layout_width="180dp"android:layout_height="150dp"android:scaleType="fitCenter" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="45dp"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_price"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="2"android:gravity="center"android:textColor="@color/red"android:textSize="15sp" /><Buttonandroid:id="@+id/btn_add"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:gravity="center"android:text="加入購物車"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout></LinearLayout>

?6. 創建購物車商品展示單元格

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"android:orientation="horizontal"><ImageViewandroid:id="@+id/iv_thumb"android:layout_width="85dp"android:layout_height="85dp"android:scaleType="fitCenter" /><LinearLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="2"android:gravity="left|center"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:id="@+id/tv_desc"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="3"android:gravity="left|center"android:textColor="@color/black"android:textSize="12sp" /></LinearLayout><TextViewandroid:id="@+id/tv_count"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="center"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:id="@+id/tv_price"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:id="@+id/tv_sum"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1.2"android:gravity="right|center"android:textColor="@color/red"android:textSize="17sp" /></LinearLayout>


?二Room基礎配置

1. 添加依賴

首先在app模塊的build.gradle中添加依賴:

dependencies {    ........
// datastore庫各版本見 https://mvnrepository.com/artifact/androidx.datastore/datastore-preferencesimplementation 'androidx.datastore:datastore-preferences:1.0.0'// datastore庫各版本見 https://mvnrepository.com/artifact/androidx.datastore/datastore-rxjava2implementation 'androidx.datastore:datastore-preferences-rxjava2:1.0.0'def room_version = "2.5.0" // 請使用最新版本// room庫各版本見 https://mvnrepository.com/artifact/androidx.room/room-runtimeimplementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version"
}

2. 添加購物車實體

在java/com/example/shopping/entity/CartInfo.java添加購物車實體信息

package com.example.shopping.entity;import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;//購物車信息
@Entity
public class CartInfo {@PrimaryKey(autoGenerate = true) // 該字段是自增主鍵private long id; // 序號private long goodsId; // 商品編號private int count; // 商品數量private String updateTime; // 更新時間public void setId(long id) {this.id = id;}public long getId() {return this.id;}public void setGoodsId(long goodsId) {this.goodsId = goodsId;}public long getGoodsId() {return this.goodsId;}public void setCount(int count) {this.count = count;}public int getCount() {return this.count;}public void setUpdateTime(String updateTime) {this.updateTime = updateTime;}public String getUpdateTime() {return this.updateTime;}}

3. 添加商品實體

在java/com/example/shopping/entity/GoodsInfo.java添加商品實體

package com.example.shopping.entity;import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;import com.example.shopping.R;import java.util.ArrayList;//商品信息
@Entity
public class GoodsInfo {@PrimaryKey(autoGenerate = true) // 該字段是自增主鍵private long id; // 序號private String name; // 名稱private String desc; // 描述private double price; // 價格private String picPath; // 大圖的保存路徑private int picRes; // 大圖的資源編號public void setId(long id) {this.id = id;}public long getId() {return this.id;}public void setName(String name) {this.name = name;}public String getName() {return this.name;}public void setDesc(String desc) {this.desc = desc;}public String getDesc() {return this.desc;}public void setPrice(double price) {this.price = price;}public double getPrice() {return this.price;}public void setPicPath(String picPath) {this.picPath = picPath;}public String getPicPath() {return this.picPath;}public void setPicRes(int picRes) {this.picRes = picRes;}public int getPicRes() {return this.picRes;}// 聲明一個手機商品的名稱數組private static String[] mNameArray = {"iPhone11", "Mate30", "小米10", "OPPO Reno3", "vivo X30", "榮耀30S"};// 聲明一個手機商品的描述數組private static String[] mDescArray = {"Apple iPhone11 256GB 綠色 4G全網通手機","華為 HUAWEI Mate30 8GB+256GB 丹霞橙 5G全網通 全面屏手機","小米 MI10 8GB+128GB 鈦銀黑 5G手機 游戲拍照手機","OPPO Reno3 8GB+128GB 藍色星夜 雙模5G 拍照游戲智能手機","vivo X30 8GB+128GB 緋云 5G全網通 美顏拍照手機","榮耀30S 8GB+128GB 蝶羽紅 5G芯片 自拍全面屏手機"};// 聲明一個手機商品的價格數組private static float[] mPriceArray = {6299, 4999, 3999, 2999, 2998, 2399};// 聲明一個手機商品的大圖數組private static int[] mPicArray = {R.drawable.iphone, R.drawable.huawei, R.drawable.xiaomi,R.drawable.oppo, R.drawable.vivo, R.drawable.rongyao};// 獲取默認的手機信息列表public static ArrayList<GoodsInfo> getDefaultList() {ArrayList<GoodsInfo> goodsList = new ArrayList<GoodsInfo>();for (int i = 0; i < mNameArray.length; i++) {GoodsInfo info = new GoodsInfo();info.name = mNameArray[i];info.desc = mDescArray[i];info.price = mPriceArray[i];info.picRes = mPicArray[i];goodsList.add(info);}return goodsList;}}

商品數據先進行寫死后期在結合后端進行改進。

4. 創建數據庫DAO層

專門對數據庫進行操作的層級,在java/com/example/shopping/dao/CartDao.java編寫購物車數據庫操作

package com.example.shopping.dao;import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;import com.example.shopping.util.DateUtil;
import com.example.shopping.entity.CartInfo;import java.util.List;@Dao
public interface CartDao {@Query("SELECT * FROM CartInfo") // 設置查詢語句List<CartInfo> queryAllCart(); // 加載所有購物車信息@Query("SELECT * FROM CartInfo WHERE goodsId = :goodsId") // 設置帶條件的查詢語句CartInfo queryCartByGoodsId(long goodsId); // 根據名字加載購物車@Insert(onConflict = OnConflictStrategy.REPLACE) // 記錄重復時替換原記錄void insertOneCart(CartInfo cart); // 插入一條購物車信息@Insertvoid insertCartList(List<CartInfo> cartList); // 插入多條購物車信息@Update(onConflict = OnConflictStrategy.REPLACE)// 出現重復記錄時替換原記錄int updateCart(CartInfo cart); // 更新購物車信息@Deletevoid deleteCart(CartInfo cart); // 刪除購物車信息@Query("DELETE FROM CartInfo WHERE goodsId = :goodsId") // 設置刪除語句void deleteOneCart(long goodsId); // 刪除一條購物車信息@Query("DELETE FROM CartInfo WHERE 1=1") // 設置刪除語句void deleteAllCart(); // 刪除所有購物車信息default void save(long goodsId) {CartInfo cartInfo = queryCartByGoodsId(goodsId);if (cartInfo == null) {cartInfo = new CartInfo();cartInfo.setGoodsId(goodsId);cartInfo.setCount(1);cartInfo.setUpdateTime(DateUtil.getNowDateTime(""));insertOneCart(cartInfo);} else {cartInfo.setCount(cartInfo.getCount()+1);cartInfo.setUpdateTime(DateUtil.getNowDateTime(""));updateCart(cartInfo);}}
}

在java/com/example/shopping/dao/GoodsDao.java編寫商品數據庫操作

package com.example.shopping.dao;import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;import com.example.shopping.entity.GoodsInfo;import java.util.List;@Dao
public interface GoodsDao {@Query("SELECT * FROM GoodsInfo") // 設置查詢語句List<GoodsInfo> queryAllGoods(); // 加載所有商品信息@Query("SELECT * FROM GoodsInfo WHERE id = :id") // 設置帶條件的查詢語句GoodsInfo queryGoodsById(long id); // 根據名字加載商品@Insert(onConflict = OnConflictStrategy.REPLACE) // 記錄重復時替換原記錄long insertOneGoods(GoodsInfo goods); // 插入一條商品信息@Insertvoid insertGoodsList(List<GoodsInfo> goodsList); // 插入多條商品信息@Update(onConflict = OnConflictStrategy.REPLACE)// 出現重復記錄時替換原記錄int updateGoods(GoodsInfo goods); // 更新商品信息@Deletevoid deleteGoods(GoodsInfo goods); // 刪除商品信息@Query("DELETE FROM GoodsInfo WHERE 1=1") // 設置刪除語句void deleteAllGoods(); // 刪除所有商品信息
}

?5. 創建數據庫實例方法

在java/com/example/shopping/database/CartDatabase.java創建購物車數據庫實例?

package com.example.shopping.database;import androidx.room.Database;
import androidx.room.RoomDatabase;import com.example.shopping.dao.CartDao;
import com.example.shopping.entity.CartInfo;//entities表示該數據庫有哪些表,version表示數據庫的版本號
//exportSchema表示是否導出數據庫信息的json串,建議設為false,若設為true還需指定json文件的保存路徑
@Database(entities = {CartInfo.class},version = 1, exportSchema = false)
public abstract class CartDatabase extends RoomDatabase {// 獲取該數據庫中某張表的持久化對象public abstract CartDao cartDao();
}

在java/com/example/shopping/database/GoodsDatabase.java創建商品數據庫實例

package com.example.shopping.database;import androidx.room.Database;
import androidx.room.RoomDatabase;import com.example.shopping.dao.GoodsDao;
import com.example.shopping.entity.GoodsInfo;//entities表示該數據庫有哪些表,version表示數據庫的版本號
//exportSchema表示是否導出數據庫信息的json串,建議設為false,若設為true還需指定json文件的保存路徑
@Database(entities = {GoodsInfo.class},version = 1, exportSchema = false)
public abstract class GoodsDatabase extends RoomDatabase {// 獲取該數據庫中某張表的持久化對象public abstract GoodsDao goodsDao();
}

?三、Application全局化

由于購物車存儲信息不只一個頁面需要進行獲取數據,所以需要把這些數據庫實例變成全局實例。

1.引導入相關依賴

在app模塊的build.gradle中添加依賴:

    implementation 'androidx.multidex:multidex:2.0.1'

這次編寫的應用類(MainApplication),它繼承自MultiDexApplication,主要用于全局初始化和管理應用級別的資源和狀態。

2.相關代碼編寫

package com.example.shopping;import android.annotation.SuppressLint;
import android.app.AlertDialog;
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.shopping.dao.CartDao;
import com.example.shopping.dao.GoodsDao;
import com.example.shopping.entity.CartInfo;
import com.example.shopping.entity.GoodsInfo;
import com.example.shopping.util.FileUtil;
import com.example.shopping.util.SharedUtil;
import com.example.shopping.util.ToastUtil;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;@SuppressLint("SetTextI18n")
public class ShoppingCartActivity extends AppCompatActivity {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 CartDao cartDao; // 聲明一個購物車的持久化對象private GoodsDao goodsDao; // 聲明一個商品的持久化對象@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(v -> finish());findViewById(R.id.btn_shopping_channel).setOnClickListener(v -> {// 從購物車頁面跳到商場頁面Intent intent = new Intent(this, ShoppingChannelActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 設置啟動標志startActivity(intent); // 跳轉到手機商場頁面});findViewById(R.id.btn_clear).setOnClickListener(v -> {cartDao.deleteAllCart(); // 清空購物車數據庫MainApplication.goodsCount = 0;showCount(); // 顯示最新的商品數量ToastUtil.show(this, "購物車已清空");});findViewById(R.id.btn_settle).setOnClickListener(v -> {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("結算商品");builder.setMessage("客官抱歉,支付功能尚未開通,請下次再來");builder.setPositiveButton("我知道了", null);builder.create().show(); // 顯示提醒對話框});// 從App實例中獲取唯一的購物車持久化對象cartDao = MainApplication.getInstance().getCartDB().cartDao();// 從App實例中獲取唯一的商品持久化對象goodsDao = MainApplication.getInstance().getGoodsDB().goodsDao();MainApplication.goodsCount = cartDao.queryAllCart().size();}// 顯示購物車圖標中的商品數量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);}}@Overrideprotected void onResume() {super.onResume();showCount(); // 顯示購物車的商品數量downloadGoods(); // 模擬從網絡下載商品圖片showCart(); // 展示購物車中的商品列表}// 聲明一個購物車中的商品信息列表private List<CartInfo> mCartList = new ArrayList<CartInfo>();// 聲明一個根據商品編號查找商品信息的映射private final HashMap<Long, GoodsInfo> mGoodsMap = new HashMap<Long, GoodsInfo>();private void deleteGoods(CartInfo info) {MainApplication.goodsCount -= info.getCount();// 從購物車的數據庫中刪除商品cartDao.deleteOneCart(info.getGoodsId());// 從購物車的列表中刪除商品for (int i = 0; i < mCartList.size(); i++) {if (info.getGoodsId() == mCartList.get(i).getGoodsId()) {mCartList.remove(i);break;}}showCount(); // 顯示最新的商品數量ToastUtil.show(this, "已從購物車刪除" + mGoodsMap.get(info.getGoodsId()).getName());mGoodsMap.remove(info.getGoodsId());refreshTotalPrice(); // 刷新購物車中所有商品的總金額}// 展示購物車中的商品列表private void showCart() {ll_cart.removeAllViews(); // 移除下面的所有子視圖mCartList = cartDao.queryAllCart(); // 查詢購物車數據庫中所有的商品記錄Log.d(TAG, "mCartList.size()=" + mCartList.size());if (mCartList == null || mCartList.size() <= 0) {return;}for (int i = 0; i < mCartList.size(); i++) {final CartInfo info = mCartList.get(i);// 根據商品編號查詢商品數據庫中的商品記錄final GoodsInfo goods = goodsDao.queryGoodsById(info.getGoodsId());Log.d(TAG, "name=" + goods.getName() + ",price=" + goods.getPrice() + ",desc=" + goods.getDesc());mGoodsMap.put(info.getGoodsId(), 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(v -> {Intent intent = new Intent(this, ShoppingDetailActivity.class);intent.putExtra("goods_id", info.getGoodsId());startActivity(intent); // 跳到商品詳情頁面});// 給商品行添加長按事件。長按商品行就刪除該商品view.setOnLongClickListener(v -> {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setMessage("是否從購物車刪除"+goods.getName()+"?");builder.setPositiveButton("是", (dialog, which) -> {ll_cart.removeView(v); // 移除當前視圖deleteGoods(info); // 刪除該商品});builder.setNegativeButton("否", null);builder.create().show(); // 顯示提醒對話框return true;});iv_thumb.setImageURI(Uri.parse(goods.getPicPath())); // 設置商品圖片tv_name.setText(goods.getName()); // 設置商品名稱tv_desc.setText(goods.getDesc()); // 設置商品描述tv_count.setText("" + info.getCount()); // 設置商品數量tv_price.setText("" + (int)goods.getPrice()); // 設置商品單價tv_sum.setText("" + (int)(info.getCount() * goods.getPrice())); // 設置商品總價ll_cart.addView(view); // 往購物車列表添加該商品行}refreshTotalPrice(); // 重新計算購物車中的商品總金額}// 重新計算購物車中的商品總金額private void refreshTotalPrice() {int total_price = 0;for (CartInfo info : mCartList) {GoodsInfo goods = mGoodsMap.get(info.getGoodsId());total_price += goods.getPrice() * info.getCount();}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<GoodsInfo> goodsList = GoodsInfo.getDefaultList(); // 模擬網絡圖片下載for (int i = 0; i < goodsList.size(); i++) {GoodsInfo info = goodsList.get(i);long id = goodsDao.insertOneGoods(info); // 往商品數據庫插入一條該商品的記錄info.setId(id);Bitmap pic = BitmapFactory.decodeResource(getResources(), info.getPicRes());String pic_path = path + id + ".jpg";FileUtil.saveImage(pic_path, pic); // 往存儲卡保存商品圖片info.setPicPath(pic_path);goodsDao.updateGoods(info); // 更新商品數據庫中該商品記錄的圖片路徑}}// 把是否首次打開寫入共享參數SharedUtil.getIntance(this).writeString("first", "false");}}

3. 修改AndroidManifest.xml

主要是添加這一行


四、業務邏輯代碼

1. 商品展示頁面

在java/com/example/shopping/ShoppingChannelActivity.java

// 使用@SuppressLint注解忽略"SetTextI18n"警告(直接設置文本時可能缺少國際化處理的警告)
@SuppressLint("SetTextI18n")
public class ShoppingChannelActivity extends AppCompatActivity {// 聲明控件成員變量private TextView tv_count;         // 顯示購物車商品數量的文本視圖private GridLayout gl_channel;    // 商品展示區域的網格布局private CartDao cartDao;          // 購物車數據庫訪問對象(用于操作購物車數據)private GoodsDao goodsDao;        // 商品數據庫訪問對象(用于操作商品數據)@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 設置當前Activity的布局文件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(v -> finish());// 購物車圖標點擊事件findViewById(R.id.iv_cart).setOnClickListener(v -> {// 跳轉到購物車頁面Intent intent = new Intent(this, ShoppingCartActivity.class);// 清除Activity棧中位于目標Activity之上的所有Activityintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent);});// 顯示當前購物車商品總數tv_count.setText("" + MainApplication.goodsCount);// 從全局Application中獲取數據庫訪問對象cartDao = MainApplication.getInstance().getCartDB().cartDao();  // 購物車DAOgoodsDao = MainApplication.getInstance().getGoodsDB().goodsDao(); // 商品DAO}/*** 將指定商品添加到購物車* @param goods_id 商品ID* @param goods_name 商品名稱(用于Toast提示)*/private void addToCart(long goods_id, String goods_name) {// 增加全局商品計數MainApplication.goodsCount++;// 更新界面顯示tv_count.setText("" + MainApplication.goodsCount);// 將商品ID保存到購物車數據庫cartDao.save(goods_id);// 顯示添加成功的提示ToastUtil.show(this, "已添加一部" + goods_name + "到購物車");}@Overrideprotected void onResume() {super.onResume();// 每次返回Activity時更新購物車數量顯示tv_count.setText("" + MainApplication.goodsCount);// 刷新商品列表showGoods(); }/*** 展示商品列表*/private void showGoods() {// 獲取屏幕寬度用于計算商品項寬度int screenWidth = Utils.getScreenWidth(this);// 設置網格布局中子項的布局參數(寬度為屏幕一半,高度自適應)LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(screenWidth/2, LinearLayout.LayoutParams.WRAP_CONTENT);// 清空現有商品視圖gl_channel.removeAllViews(); // 從數據庫獲取所有商品數據List<GoodsInfo> goodsList = goodsDao.queryAllGoods();// 遍歷商品列表for (final GoodsInfo info : goodsList) {// 加載單個商品項的布局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.getName());  // 設置商品名稱iv_thumb.setImageURI(Uri.parse(info.getPicPath())); // 加載商品圖片// 商品圖片點擊事件(跳轉到詳情頁)iv_thumb.setOnClickListener(v -> {Intent intent = new Intent(this, ShoppingDetailActivity.class);intent.putExtra("goods_id", info.getId());  // 傳遞商品IDstartActivity(intent);});// 設置商品價格(去掉小數部分)tv_price.setText("" + (int)info.getPrice());// 加入購物車按鈕點擊事件btn_add.setOnClickListener(v -> addToCart(info.getId(), info.getName()));// 將商品項添加到網格布局gl_channel.addView(view, params);}}
}

2.? 商品詳情頁面

在java/com/example/shopping/ShoppingDetailActivity.java

@SuppressLint("SetTextI18n")
public class ShoppingDetailActivity extends AppCompatActivity {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 CartDao cartDao; // 聲明一個購物車的持久化對象private GoodsDao goodsDao; // 聲明一個商品的持久化對象@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(v -> finish());findViewById(R.id.iv_cart).setOnClickListener(v -> {startActivity(new Intent(this, ShoppingCartActivity.class)); // 跳轉到購物車頁面});findViewById(R.id.btn_add_cart).setOnClickListener(v -> addToCart(mGoodsId));tv_count.setText("" + MainApplication.goodsCount);// 從App實例中獲取唯一的購物車持久化對象cartDao = MainApplication.getInstance().getCartDB().cartDao();// 從App實例中獲取唯一的商品持久化對象goodsDao = MainApplication.getInstance().getGoodsDB().goodsDao();}// 把指定編號的商品添加到購物車private void addToCart(long goods_id) {MainApplication.goodsCount++;tv_count.setText("" + MainApplication.goodsCount);cartDao.save(goods_id); // 把該商品填入購物車數據庫ToastUtil.show(this, "成功添加至購物車");}@Overrideprotected void onResume() {super.onResume();showDetail(); // 展示商品詳情}private void showDetail() {// 獲取上一個頁面傳來的商品編號mGoodsId = getIntent().getLongExtra("goods_id", 0L);if (mGoodsId > 0) {// 根據商品編號查詢商品數據庫中的商品記錄GoodsInfo info = goodsDao.queryGoodsById(mGoodsId);tv_title.setText(info.getName()); // 設置商品名稱tv_goods_desc.setText(info.getDesc()); // 設置商品描述tv_goods_price.setText("" + (int)info.getPrice()); // 設置商品價格iv_goods_pic.setImageURI(Uri.parse(info.getPicPath())); // 設置商品圖片}}}

3.? 購物車頁面

@SuppressLint("SetTextI18n")
public class ShoppingCartActivity extends AppCompatActivity {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 CartDao cartDao; // 聲明一個購物車的持久化對象private GoodsDao goodsDao; // 聲明一個商品的持久化對象@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(v -> finish());findViewById(R.id.btn_shopping_channel).setOnClickListener(v -> {// 從購物車頁面跳到商場頁面Intent intent = new Intent(this, ShoppingChannelActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 設置啟動標志startActivity(intent); // 跳轉到手機商場頁面});findViewById(R.id.btn_clear).setOnClickListener(v -> {cartDao.deleteAllCart(); // 清空購物車數據庫MainApplication.goodsCount = 0;showCount(); // 顯示最新的商品數量ToastUtil.show(this, "購物車已清空");});findViewById(R.id.btn_settle).setOnClickListener(v -> {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("結算商品");builder.setMessage("客官抱歉,支付功能尚未開通,請下次再來");builder.setPositiveButton("我知道了", null);builder.create().show(); // 顯示提醒對話框});// 從App實例中獲取唯一的購物車持久化對象cartDao = MainApplication.getInstance().getCartDB().cartDao();// 從App實例中獲取唯一的商品持久化對象goodsDao = MainApplication.getInstance().getGoodsDB().goodsDao();MainApplication.goodsCount = cartDao.queryAllCart().size();}// 顯示購物車圖標中的商品數量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);}}@Overrideprotected void onResume() {super.onResume();showCount(); // 顯示購物車的商品數量downloadGoods(); // 模擬從網絡下載商品圖片showCart(); // 展示購物車中的商品列表}// 聲明一個購物車中的商品信息列表private List<CartInfo> mCartList = new ArrayList<CartInfo>();// 聲明一個根據商品編號查找商品信息的映射private final HashMap<Long, GoodsInfo> mGoodsMap = new HashMap<Long, GoodsInfo>();private void deleteGoods(CartInfo info) {MainApplication.goodsCount -= info.getCount();// 從購物車的數據庫中刪除商品cartDao.deleteOneCart(info.getGoodsId());// 從購物車的列表中刪除商品for (int i = 0; i < mCartList.size(); i++) {if (info.getGoodsId() == mCartList.get(i).getGoodsId()) {mCartList.remove(i);break;}}showCount(); // 顯示最新的商品數量ToastUtil.show(this, "已從購物車刪除" + mGoodsMap.get(info.getGoodsId()).getName());mGoodsMap.remove(info.getGoodsId());refreshTotalPrice(); // 刷新購物車中所有商品的總金額}// 展示購物車中的商品列表private void showCart() {ll_cart.removeAllViews(); // 移除下面的所有子視圖mCartList = cartDao.queryAllCart(); // 查詢購物車數據庫中所有的商品記錄Log.d(TAG, "mCartList.size()=" + mCartList.size());if (mCartList == null || mCartList.size() <= 0) {return;}for (int i = 0; i < mCartList.size(); i++) {final CartInfo info = mCartList.get(i);// 根據商品編號查詢商品數據庫中的商品記錄final GoodsInfo goods = goodsDao.queryGoodsById(info.getGoodsId());Log.d(TAG, "name=" + goods.getName() + ",price=" + goods.getPrice() + ",desc=" + goods.getDesc());mGoodsMap.put(info.getGoodsId(), 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(v -> {Intent intent = new Intent(this, ShoppingDetailActivity.class);intent.putExtra("goods_id", info.getGoodsId());startActivity(intent); // 跳到商品詳情頁面});// 給商品行添加長按事件。長按商品行就刪除該商品view.setOnLongClickListener(v -> {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setMessage("是否從購物車刪除"+goods.getName()+"?");builder.setPositiveButton("是", (dialog, which) -> {ll_cart.removeView(v); // 移除當前視圖deleteGoods(info); // 刪除該商品});builder.setNegativeButton("否", null);builder.create().show(); // 顯示提醒對話框return true;});iv_thumb.setImageURI(Uri.parse(goods.getPicPath())); // 設置商品圖片tv_name.setText(goods.getName()); // 設置商品名稱tv_desc.setText(goods.getDesc()); // 設置商品描述tv_count.setText("" + info.getCount()); // 設置商品數量tv_price.setText("" + (int)goods.getPrice()); // 設置商品單價tv_sum.setText("" + (int)(info.getCount() * goods.getPrice())); // 設置商品總價ll_cart.addView(view); // 往購物車列表添加該商品行}refreshTotalPrice(); // 重新計算購物車中的商品總金額}// 重新計算購物車中的商品總金額private void refreshTotalPrice() {int total_price = 0;for (CartInfo info : mCartList) {GoodsInfo goods = mGoodsMap.get(info.getGoodsId());total_price += goods.getPrice() * info.getCount();}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<GoodsInfo> goodsList = GoodsInfo.getDefaultList(); // 模擬網絡圖片下載for (int i = 0; i < goodsList.size(); i++) {GoodsInfo info = goodsList.get(i);long id = goodsDao.insertOneGoods(info); // 往商品數據庫插入一條該商品的記錄info.setId(id);Bitmap pic = BitmapFactory.decodeResource(getResources(), info.getPicRes());String pic_path = path + id + ".jpg";FileUtil.saveImage(pic_path, pic); // 往存儲卡保存商品圖片info.setPicPath(pic_path);goodsDao.updateGoods(info); // 更新商品數據庫中該商品記錄的圖片路徑}}// 把是否首次打開寫入共享參數SharedUtil.getIntance(this).writeString("first", "false");}}

?五、項目結構示意圖?


六、成果展示?

總結

本次實戰項目通過Room數據庫實現了購物車功能,重點解決了本地圖片存儲、數據關系建模等核心問題,提升了離線狀態下的用戶體驗。借助Room的關系型特性與RecyclerView聯動,確保了數據與UI的高效同步。項目不僅鞏固了Android開發的基礎知識,還深入探討了性能優化的實用方案。無論是技術實現還是實戰經驗,都為后續開發提供了有價值的參考。

🙌?求點贊、收藏、關注!?

如果這篇文章對你有幫助,不妨:
👍?點個贊?→ 讓更多人看到這篇干貨!
??收藏一下?→ 方便以后隨時查閱!
🔔?加關注?→ 獲取更多?前端/后端/全棧技術深度解析

你的支持,是我持續創作的最大動力!?🚀

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79721.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79721.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79721.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PostgteSQL for Everybody基礎部分筆記

筆記分享內容參考密歇根大學 Charles Russell Severance 開設的PostgreSQL課程&#xff1a;postgresql-for-everybody&#xff0c;網址為&#xff1a;https://www.coursera.org/specializations/postgresql-for-everybody#courses&#xff0c;在B站等也有相關視頻分享。 我分享…

Python項目源碼63:病歷管理系統1.0(tkinter+sqlite3+matplotlib)

1.病歷管理系統包含以下主要功能&#xff1a; 核心功能&#xff1a;病歷信息錄入&#xff08;患者姓名、年齡、性別、診斷結果、主治醫生&#xff09;&#xff0c;自動記錄就診時間&#xff0c;病歷信息展示&#xff08;使用Treeview表格&#xff09;&#xff0c;病歷信息查詢…

MCP底層協議完整通信過程

2025 年是智能體的元年, 也注定是智能體集中爆發的一年! 兩個互聯領域的重大挑戰: 第一、 Agent 與 Tools (工具)的交互 Agent 需要調用外部工具和 API

docker:制作鏡像+上傳鏡像+拉取鏡像

1.dockerfile制作鏡像 示例內容&#xff1a; 1.創建一個index.js的文件 console.log("hello world")2.在相同目錄下創建名為dockerfile的文件 FROM node:alpine COPY index.js /index.js CMD node /index.js3.構建鏡像 docker build -t minterra/hello-docker . …

docker制作python大模型鏡像(miniconda環境),工程改造記錄

**環境說明&#xff1a;**從系統鏡像開始打造python大模型鏡像&#xff0c;之前是人工手動裝的方式&#xff0c;并且模型和依賴在公網中&#xff0c;對于離線交付環境不太友好&#xff0c;所以打造的離線化交付版本 Dockerfile: FROM centos:7.9 ENV PYTHONIOENCODINGutf-8 E…

Rust中避免過度使用鎖導致性能問題的策略

一、引言 在 Rust 多線程編程中&#xff0c;鎖是實現線程同步的重要工具&#xff0c;它可以防止多個線程同時訪問和修改共享數據&#xff0c;從而避免數據競爭和不一致的問題。然而&#xff0c;過度使用鎖會帶來嚴重的性能問題&#xff0c;如鎖競爭導致的線程阻塞、上下文切換…

數據結構每日一題day15(鏈表)★★★★★

題目描述&#xff1a;將一個帶頭結點的單鏈表A分解為兩個帶頭結點的單鏈表A和 B,使得A表中含有原表中序號為奇數的元素,而B表中含有原表中序號為偶數的元素,且保持相對順不變&#xff0c;最后返回 B 表。 算法思想&#xff1a; 1.初始化&#xff1a; 創建新鏈表 B 的頭結點。…

【雜談】-探索 NVIDIA Dynamo 的高性能架構

探索 NVIDIA Dynamo 的高性能架構 文章目錄 探索 NVIDIA Dynamo 的高性能架構1. 大規模人工智能推理的日益嚴峻的挑戰2. 使用 NVIDIA Dynamo 優化 AI 推理3. 實際應用和行業影響4. 競爭優勢&#xff1a;Dynamo 與其他方案對比5. 總結 隨著人工智能&#xff08;AI&#xff09;技…

postgresql數據庫基本操作

1. 連接 PostgreSQL 數據庫 首先&#xff0c;使用 psql 命令行工具連接到數據庫。如果是本地連接&#xff0c;命令格式如下&#xff1a; psql -U postgres -d <數據庫名稱> -h <主機地址>其中&#xff1a; -U postgres&#xff1a;表示以 postgres 用戶身份登錄…

工業大模型:從設備診斷到工藝重構

引言 工業大模型正在引發制造業認知革命。據埃森哲研究,到2026年全球工業大模型市場規模將突破280億美元,其中工藝優化應用占比達42%。本文將系統解析工業大模型的"預訓練-領域適配-應用落地"技術路徑,并通過設備健康診斷與工藝參數生成的實踐案例,展示如何構建…

PyQt5基本介紹

PyQt5是基于Digia公司強大圖形框架Qt5的python接口&#xff0c;由一組python模塊構成。是一個用于創建桌面應用程序的Python庫&#xff0c;它是Qt圖形用戶界面工具包的Python綁定。 Qt是一個跨平臺的C庫&#xff0c;提供了一套豐富的工具和功能&#xff0c;用于開發圖形用戶界…

Tire 樹(字典樹/前綴樹)

一、定義與結構 用來快速存儲查找字符串集合的一種數據結構 將字符串按順序連接根節點上&#xff0c;并在字符串結束的地方打上標記并計數。 二、模板題 acwing 835 Trie 樹的字符串統計 題目&#xff1a; 維護一個字符串集合&#xff0c;支持兩種操作&#xff1a; I x 向…

【時時三省】(C語言基礎)怎樣定義和引用一維數組

山不在高&#xff0c;有仙則名。水不在深&#xff0c;有龍則靈。 ----CSDN 時時三省 一維數組是數組中最簡單的&#xff0c;它的元素只需要用數組名加一個下標&#xff0c;就能唯一地確定。如上面介紹的學生成績數組s就是一維數組。有的數組&#xff0c;其元素要指定兩個下標才…

編譯faiss

編譯faiss-1.10.0 首先確保自己cmake的版本&#xff1a; cmake --version 確保其版本至少為CMake 3.24.0 or higher is required。 其次安裝OpenBLAS&#xff1a; https://github.com/OpenMathLib/OpenBLAS 去這里去安轉Openblas內容&#xff0c;然后確保自己的CPU的指令集是存…

Linux 入門:操作系統進程詳解

目錄 一.馮諾依曼體系結構 一&#xff09;. 軟件運行前為什么要先加載&#xff1f;程序運行之前在哪里&#xff1f; 二&#xff09;.理解數據流動 二.操作系統OS(Operator System) 一&#xff09;.概念 二&#xff09;.設計OS的目的 三&#xff09;.如何理解操作系統…

word交叉引用圖片、表格——只引用編號的處理方法

交叉引用圖片/表格 在“引用”選項卡上的“題注”組中&#xff0c;單擊“插入題注”。勾選【從題注中排除標簽】。在文中插入題注。 【注 意】 這時候插入的題注只有編號項了。然后手動打上標簽【TABLE】&#xff0c;并在標簽和編號項之間加上【樣式分隔符&#xff0c;AltCt…

rails 8 CSS不起效問題解決

很久沒用rails了&#xff0c;最近打算重新復習一下。在配置好環境后&#xff0c;創建了項目&#xff0c;通過腳手架創建了數據庫表&#xff0c;和相關的文件。但我發現卻沒有生成相應的CSS文件&#xff0c;可能是rails8 取消了吧。于是自己手動創建了相應的css文件。但是刷新頁…

【nlohmann\json.hpp】‘_snprintf‘: is not a member of ‘std‘

這個問題時有發生但是為啥現在更新了vs2022 后,發生了這些報錯:2>(compiling source file ../worker/src/fargo/PacedVideoSenderGo.cpp) 2>D:\XTRANS\thunderbolt\ayame

數據結構--【二叉樹】

目錄 定義結構體&#xff1a; 初始化&#xff1a; 手動創建一個二叉樹&#xff1a; 前序遍歷&#xff1a; 中序遍歷&#xff1a; 后序遍歷 二叉樹節點個數&#xff1a; 葉子節點個數&#xff1a; 二叉樹第k層節點個數&#xff1a; 二叉樹的高度&#xff1a; 查找值為x…

深入解析Linux進程間通信(IPC):機制、應用與最佳實踐

引言 在多任務操作系統中&#xff0c;進程間通信&#xff08;Inter-Process Communication, IPC&#xff09;是協同工作的核心機制。Linux作為現代操作系統的典范&#xff0c;提供了8種主要IPC方式&#xff0c;從傳統的管道到面向網絡的套接字&#xff0c;每種方法都暗藏獨特的…