一年經驗的全棧程序員,目前頭發健在,但不知道能撐多久。
文章目錄
前言
?一、頁面編寫
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持久化庫,構建一個功能完善且性能優異的購物車模塊。與常規實現不同,我們的方案將重點解決以下技術難點:
-
本地圖片存儲:直接將商品圖片以Blob形式存入Room數據庫,確保用戶離線狀態下仍能完整查看購物車內容
-
數據關系建模:使用Room的關系型數據庫特性,建立商品與購物車項之間的關聯
-
性能優化:針對圖片存儲可能帶來的性能問題,提供切實可行的解決方案
-
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開發的基礎知識,還深入探討了性能優化的實用方案。無論是技術實現還是實戰經驗,都為后續開發提供了有價值的參考。
🙌?求點贊、收藏、關注!?
如果這篇文章對你有幫助,不妨:
👍?點個贊?→ 讓更多人看到這篇干貨!
??收藏一下?→ 方便以后隨時查閱!
🔔?加關注?→ 獲取更多?前端/后端/全棧技術深度解析!
你的支持,是我持續創作的最大動力!?🚀