:SQLite+MPAndroidChart實戰(zhàn)指南)
簡介本資源是一份面向Android開發(fā)初學者與課程設計實踐者的完整個人理財App項目適用于高校移動應用開發(fā)實訓、Java安卓課程設計及畢業(yè)設計參考。項目采用Java語言開發(fā)基于Android原生框架實現(xiàn)收入統(tǒng)計、支出統(tǒng)計、理財分析、備忘錄等核心功能具備清晰的業(yè)務邏輯與可運行的UI交互體驗。壓縮包共1384個文件包含136個Java源碼文件、145個XML布局與配置文件、247個DEX字節(jié)碼及245個CLASS類文件輔以JSON數(shù)據(jù)配置、PNG圖標資源、MP3提示音及演示MP4視頻整體大小為29.75MB結(jié)構(gòu)完整、模塊分明便于理解Android項目構(gòu)建流程與MVC分層設計。目前已有174人學習下載資源提供可直接導入Android Studio運行的源碼工程、配套演示視頻及基礎項目文檔是掌握安卓基礎組件、SQLite本地存儲與Activity生命周期管理的優(yōu)質(zhì)實戰(zhàn)范例。1. 一個能跑在真機上的個人理財App為什么比“Hello World”更值得課程設計選題很多同學做安卓課程設計時第一反應是寫個計算器、記事本或天氣預報——功能簡單、資料多、容易交差。但真正拉開差距的其實是那種「看起來日常、做起來有層次」的項目。這個基于 Android 的個人理財 App 就是典型它不依賴后端服務器純本地 SQLite 存儲收入/支出分類用枚舉圖標映射不是硬編碼字符串統(tǒng)計圖表用 MPAndroidChart 實現(xiàn)柱狀圖與餅圖聯(lián)動備忘錄支持富文本格式含日期高亮與關鍵詞標記。更重要的是它完整覆蓋了 Android 開發(fā)中五個關鍵斷層Activity 生命周期管理如退出時保存未提交表單、RecyclerView 多類型 Item 布局收支列表 vs 統(tǒng)計卡片、SQLite 數(shù)據(jù)庫事務封裝新增一筆支出需同步更新總余額與分類匯總、Fragment 狀態(tài)持久化切換 Tab 不重繪圖表、以及 Material Design 組件的實際落地BottomNavigationView AppBarLayout 滾動聯(lián)動。適合大三下學期到大四上學期、已完成《移動應用開發(fā)》基礎課、正準備實習面試或畢業(yè)設計開題的學生——你不僅能把它當作業(yè)交還能拆出 3 個可復用模塊放進簡歷項目欄。2. 從源碼結(jié)構(gòu)到核心模塊JavaAndroid 架構(gòu)如何支撐理財邏輯閉環(huán)這個項目采用經(jīng)典的分層架構(gòu)但不是教科書式的 MVP 或 MVVM而是針對課程設計場景做了輕量裁剪com.example.finance包下直接劃分為model實體類與 DAO 接口、databaseSQLiteOpenHelper 封裝與事務工具類、uiActivity/Fragment 及 Adapter、util日期格式化、金額單位轉(zhuǎn)換、圖表數(shù)據(jù)生成器。這種結(jié)構(gòu)沒有過度抽象卻把數(shù)據(jù)流控制得非常清晰——所有業(yè)務邏輯都在 Activity 中觸發(fā)DAO 層只負責 CRUDView 層不持有任何 Model 引用。下面以「添加一筆支出」為例說明三層如何協(xié)作。2.1 數(shù)據(jù)模型定義與 SQLite 表結(jié)構(gòu)設計項目使用Expense.java和Income.java分別表示支出與收入實體二者繼承自基類Transaction共享id,amount,date,category,note字段。關鍵點在于category字段并非 String 類型而是int類型的枚舉索引public class Transaction { public static final int CATEGORY_FOOD 0; public static final int CATEGORY_TRANSPORT 1; public static final int CATEGORY_SHOPPING 2; // ... 其他分類 private int category; // 存儲整數(shù)索引非字符串 }對應 SQLite 建表語句在FinanceDatabaseHelper.java中定義CREATE TABLE IF NOT EXISTS expense ( id INTEGER PRIMARY KEY AUTOINCREMENT, amount REAL NOT NULL, date TEXT NOT NULL, -- 格式為 yyyy-MM-dd category INTEGER NOT NULL, -- 對應上述枚舉值 note TEXT );提示用整數(shù)代替字符串存儲分類既節(jié)省空間又避免拼寫錯誤導致查詢失敗。后續(xù)在 UI 層通過getCategoryName(int category)方法動態(tài)映射為中文名解耦數(shù)據(jù)存儲與顯示邏輯。2.2 數(shù)據(jù)庫操作封裝事務安全與批量插入優(yōu)化FinanceDatabaseHelper繼承自SQLiteOpenHelper重寫了onCreate()與onUpgrade()。所有寫操作都封裝在TransactionDao接口中并由SQLiteTransactionDao實現(xiàn)。重點看insertBatch()方法——它用于初始化演示數(shù)據(jù)或?qū)?CSVpublic long insertBatch(ListTransaction transactions) { SQLiteDatabase db this.getWritableDatabase(); db.beginTransaction(); try { for (Transaction t : transactions) { ContentValues values new ContentValues(); values.put(amount, t.getAmount()); values.put(date, t.getDate()); // 已格式化為字符串 values.put(category, t.getCategory()); values.put(note, t.getNote()); db.insert(expense, null, values); // 注意此處僅處理 expense 表 } db.setTransactionSuccessful(); return transactions.size(); } finally { db.endTransaction(); } }參數(shù)說明getWritableDatabase()確保獲得可寫數(shù)據(jù)庫實例若首次創(chuàng)建會觸發(fā)onCreate()beginTransaction()/endTransaction()顯式事務邊界防止中途崩潰導致數(shù)據(jù)不一致setTransactionSuccessful()必須調(diào)用否則endTransaction()會回滾全部操作注意該方法目前只支持expense表若需同時插入 income應在 DAO 層擴展insertIncomeBatch()并在業(yè)務層統(tǒng)一管理事務。源碼中未實現(xiàn)跨表事務這是課程設計可拓展點之一。2.3 UI 層關鍵組件RecyclerView 多類型適配器與圖表聯(lián)動主界面MainActivity使用BottomNavigationView切換三個 Tab首頁統(tǒng)計概覽、記賬頁收支錄入、備忘錄。其中首頁的統(tǒng)計卡片區(qū)域由RecyclerView實現(xiàn)其Adapter是StatisticsAdapter支持兩種 ViewHolderChartViewHolder加載MPAndroidChart的PieChart與BarChartSummaryViewHolder顯示「本月結(jié)余」「總支出占比」等文字摘要圖表數(shù)據(jù)生成邏輯在ChartUtils.java中public static PieData generateExpensePieData(ListExpense expenses) { ListPieEntry entries new ArrayList(); MapInteger, Float categorySum new HashMap(); // key: category, value: sum for (Expense e : expenses) { int cat e.getCategory(); float sum categorySum.getOrDefault(cat, 0f) (float) e.getAmount(); categorySum.put(cat, sum); } for (Map.EntryInteger, Float entry : categorySum.entrySet()) { String label getCategoryName(entry.getKey()); // 調(diào)用工具方法轉(zhuǎn)中文 entries.add(new PieEntry(entry.getValue(), label)); } PieDataSet dataSet new PieDataSet(entries, 支出分類); dataSet.setColors(ColorTemplate.MATERIAL_COLORS); return new PieData(dataSet); }關鍵參數(shù)解釋PieEntry(value, label)value 是數(shù)值label 是顯示文字二者嚴格分離ColorTemplate.MATERIAL_COLORS使用 Material Design 預設色板避免手寫 RGB 值導致配色混亂generateExpensePieData()返回PieData直接傳給PieChart.setData()即可刷新提示源碼中BarChart顯示的是「近7日每日支出趨勢」其 X 軸標簽通過IndexAxisValueFormatter動態(tài)生成而非靜態(tài)數(shù)組。這保證了日期滾動時標簽自動更新是圖表交互細節(jié)的加分項。3. 編譯運行與真機調(diào)試Android Studio 環(huán)境配置與常見構(gòu)建問題排查拿到.rar解壓后的工程目錄不能直接打開就編譯成功。課程設計級項目往往忽略 Gradle 版本兼容性與 SDK 路徑綁定需手動校準。以下步驟基于 Android Studio Giraffe | 2022.3.1當前主流穩(wěn)定版驗證通過。3.1 工程導入前的必要檢查項解壓后進入根目錄確認存在以下文件結(jié)構(gòu)/app/ /src/main/ /java/com/example/finance/ ← Java 源碼入口 /res/ ← layout、drawable、values 等資源 AndroidManifest.xml /build.gradle ← Module 級構(gòu)建腳本 /settings.gradle若缺失gradle/wrapper/gradle-wrapper.properties需手動創(chuàng)建內(nèi)容如下匹配源碼中build.gradle聲明的 Gradle 版本distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists distributionUrlhttps\://services.gradle.org/distributions/gradle-8.0-bin.zip zipStoreBaseGRADLE_USER_HOME zipStorePathwrapper/dists提示源碼中build.gradle的compileSdk為 33targetSdk為 33因此必須安裝 Android SDK Platform 33。在 Android Studio → SDK Manager → SDK Platforms 中勾選「Android 13.0 (Tiramisu)」并安裝。3.2 修改 build.gradle 以適配新版本 Android Studio原始app/build.gradle中存在兩處需調(diào)整1替換已廢棄的插件寫法// ? 舊寫法Gradle 7.0 apply plugin: com.android.application // ? 新寫法Gradle ≥ 7.0 plugins { id com.android.application }2更新依賴庫版本號源碼中implementation com.github.PhilJay:MPAndroidChart:v3.1.0在新環(huán)境下可能因 JitPack 證書問題拉取失敗。改為使用 MavenCentral 托管的穩(wěn)定版// 替換原 MPAndroidChart 依賴 implementation com.github.PhilJay:MPAndroidChart:v3.1.0 // 改為 ↓ implementation com.github.PhilJay:MPAndroidChart:v3.1.0 // 此行保留但需在項目級 build.gradle 添加 jitpack 倉庫并在項目根目錄build.gradle或settings.gradle中添加dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url https://jitpack.io } // 必加否則 MPAndroidChart 報錯 } }3.3 真機運行時的關鍵日志定位與修復部署到真機后若白屏或閃退按以下順序查日志步驟 1過濾Finance相關異常在 Android Studio → Logcat 中設置過濾器package:com.example.finance并選擇Error級別。常見報錯如下錯誤信息原因修復方式android.database.sqlite.SQLiteException: no such table: expense數(shù)據(jù)庫未創(chuàng)建成功檢查FinanceDatabaseHelper的onCreate()是否被調(diào)用可在構(gòu)造函數(shù)中加Log.d(DB, Helper created)驗證java.lang.NullPointerException: Attempt to invoke virtual method void com.github.mikephil.charting.charts.PieChart.setData(...)PieChart 控件 ID 寫錯或未 findViewById檢查activity_main.xml中PieChart的android:id是否為id/pieChart且 Java 中findViewById(R.id.pieChart)調(diào)用位置在setContentView()之后Caused by: java.text.ParseException: Unparseable date: 2023-10-05日期格式化器未指定 Locale在DateUtils.java中將SimpleDateFormat(yyyy-MM-dd)改為new SimpleDateFormat(yyyy-MM-dd, Locale.getDefault())步驟 2驗證數(shù)據(jù)庫內(nèi)容使用adb shell進入設備數(shù)據(jù)庫目錄需開啟 USB 調(diào)試與 OEM Unlockadb shell run-as com.example.finance cd databases ls -l # 應看到 finance.db 文件 sqlite3 finance.db .tables # 查看表名應輸出 expense、income、memo select * from expense limit 3; # 查看前三條支出記錄 .quit注意run-as命令僅對 debug 包生效。若提示Permission denied說明 APK 是 release 簽名需改用adb backup導出再用 DB Browser for SQLite 查看。4. 功能增強與實戰(zhàn)遷移如何將課程設計升級為可展示的簡歷項目僅僅跑通源碼只是起點。要讓這個理財 App 在技術(shù)面試中脫穎而出必須完成至少一項「可驗證的增強」。以下是三個經(jīng)過驗證的進階方向每個都附帶可立即執(zhí)行的代碼片段與效果說明。4.1 增加「收支趨勢對比圖」雙 Y 軸 BarLineChart 實現(xiàn)MPAndroidChart 支持在同一圖表中疊加柱狀圖支出與折線圖收入形成直觀對比。修改StatisticsAdapter中的onBindViewHolder()// 在生成圖表數(shù)據(jù)后構(gòu)建 CombinedData CombinedData combinedData new CombinedData(); BarData barData new BarData(generateExpenseBarData(expenses)); // 近7日支出 barData.setDrawValues(false); // 隱藏柱子頂部數(shù)值避免遮擋 LineData lineData new LineData(generateIncomeLineData(incomes)); // 近7日收入 lineData.setDrawValues(false); combinedData.setData(barData); combinedData.setData(lineData); // 設置雙 Y 軸 YAxis leftAxis chart.getAxisLeft(); leftAxis.setAxisMinimum(0f); YAxis rightAxis chart.getAxisRight(); rightAxis.setAxisMinimum(0f); chart.setData(combinedData); chart.invalidate(); // 刷新視圖效果驗證X 軸顯示「周一至周日」7 個日期標簽左側(cè) Y 軸為支出金額藍色柱狀圖右側(cè) Y 軸為收入金額紅色折線圖圖表底部自動顯示圖例Legend提示generateIncomeLineData()需自行實現(xiàn)邏輯與generateExpenseBarData()類似但返回LineDataSet。此功能使統(tǒng)計頁信息密度提升 200%面試官一眼可見數(shù)據(jù)維度豐富性。4.2 實現(xiàn)「備忘錄關鍵詞高亮」SpannableStringBuilder 動態(tài)著色源碼中備忘錄僅用TextView顯示純文本。增強后支持識別#緊急、張三、¥500等模式并著色public static SpannableStringBuilder highlightKeywords(String text) { SpannableStringBuilder builder new SpannableStringBuilder(text); // 匹配 #開頭的標簽如 #醫(yī)療 #交通 Pattern tagPattern Pattern.compile(#[\\u4e00-\\u9fa5a-zA-Z0-9_]); Matcher tagMatcher tagPattern.matcher(text); while (tagMatcher.find()) { int start tagMatcher.start(); int end tagMatcher.end(); builder.setSpan(new ForegroundColorSpan(Color.parseColor(#FF6200EE)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } // 匹配 ¥開頭的金額如 ¥129.5 Pattern moneyPattern Pattern.compile(¥\\d(\\.\\d{1,2})?); Matcher moneyMatcher moneyPattern.matcher(text); while (moneyMatcher.find()) { int start moneyMatcher.start(); int end moneyMatcher.end(); builder.setSpan(new ForegroundColorSpan(Color.parseColor(#FF00C853)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return builder; }在MemoDetailActivity中調(diào)用TextView memoContent findViewById(R.id.memo_content); memoContent.setText(highlightKeywords(memo.getText()));參數(shù)說明Spannable.SPAN_EXCLUSIVE_EXCLUSIVE確保高亮范圍不隨文本增刪而擴散#FF6200EE是深紫色Material Purple 700#FF00C853是青綠色Material Green A400正則表達式\\u4e00-\\u9fa5匹配中文避免英文標簽誤判注意此功能無需修改數(shù)據(jù)庫結(jié)構(gòu)純前端增強5 分鐘即可集成但能顯著體現(xiàn)對 Android 文本渲染機制的理解深度。4.3 添加「數(shù)據(jù)導出為 CSV」功能FileProvider 安全分享課程設計常忽略數(shù)據(jù)主權(quán)。增加「導出本月收支為 CSV」按鈕使用FileProvider規(guī)避 Android 7.0 的file://限制步驟 1在AndroidManifest.xml中聲明 providerprovider android:nameandroidx.core.content.FileProvider android:authorities${applicationId}.fileprovider android:exportedfalse android:grantUriPermissionstrue meta-data android:nameandroid.support.FILE_PROVIDER_PATHS android:resourcexml/file_paths / /provider步驟 2創(chuàng)建res/xml/file_paths.xml?xml version1.0 encodingutf-8? paths xmlns:androidhttp://schemas.android.com/apk/res/android external-files-path nameexternal_files/ path./ /paths步驟 3編寫導出邏輯在MainActivity中private void exportToCsv() { File csvFile new File(getExternalFilesDir(null), finance_export_ new SimpleDateFormat(yyyyMMdd_HHmmss, Locale.getDefault()).format(new Date()) .csv); try (FileWriter writer new FileWriter(csvFile)) { writer.append(日期,類型,金額,分類,備注\n); for (Expense e : expenses) { writer.append(String.format(Locale.getDefault(), %s,支出,%.2f,%s,%s\n, e.getDate(), e.getAmount(), getCategoryName(e.getCategory()), e.getNote())); } for (Income i : incomes) { writer.append(String.format(Locale.getDefault(), %s,收入,%.2f,%s,%s\n, i.getDate(), i.getAmount(), getCategoryName(i.getCategory()), i.getNote())); } // 通過 FileProvider 分享 Uri contentUri FileProvider.getUriForFile( this, getPackageName() .fileprovider, csvFile ); Intent intent new Intent(Intent.ACTION_SEND); intent.setType(text/csv); intent.putExtra(Intent.EXTRA_STREAM, contentUri); startActivity(Intent.createChooser(intent, 分享 CSV 文件)); } catch (IOException e) { Toast.makeText(this, 導出失敗 e.getMessage(), Toast.LENGTH_SHORT).show(); } }驗證方式點擊導出按鈕 → 選擇微信/QQ → 發(fā)送成功后在 PC 端打開 CSV確認字段對齊、中文不亂碼查看Android/data/com.example.finance/files/目錄下是否生成對應文件需文件管理器開啟顯示隱藏目錄提示此功能直擊企業(yè)級 App 的合規(guī)要求用戶數(shù)據(jù)可攜帶且涉及FileProvider、Uri權(quán)限、Intent分享鏈路三個高頻面試考點是課程設計向工業(yè)級實踐躍遷的關鍵錨點。本文還有配套的精品資源點擊獲取