24.11.03
1.修改项目目录
从今天开始将正式进行功能的设计,首先需要对原来的项目结构进行修改,主要是添加新的文件夹用于存放新的文件。下面进行展示和讲解:
我用红圈圈出了新添加的文件夹,介绍下它们都是干啥的:
-
data:负责应用数据的管理。
- model:定义数据模型类,比如
Expense
表示支出记录。 - database:存放与数据库相关的代码。
ExpenseDatabase
是 Room 数据库类,ExpenseDao
是用于定义数据库查询的接口。
- model:定义数据模型类,比如
-
ui:负责 UI 层的代码。
- activity:放置主界面 Activity,如
MainActivity
。 - fragment:每个页面或子模块用一个 Fragment,比如
OverviewFragment
展示支出概览,AddExpenseFragment
添加新的支出记录。 - adapter:RecyclerView 适配器,例如
CategoryAdapter
,用于展示分类支出的列表。 - theme:存放主题文件,如颜色、字体和样式的配置。
- viewmodel:放置 ViewModel 类,负责管理 UI 所需的数据逻辑。例如
MainViewModel
用于管理首页的数据,ExpenseViewModel
用于管理支出记录。
- activity:放置主界面 Activity,如
-
util:放置工具类,例如
DateUtils
用于日期格式化等辅助功能。 -
res:包含所有的资源文件。
- layout:XML 布局文件,比如
activity_main.xml
是主界面的布局,fragment_overview.xml
和fragment_add_expense.xml
是 Fragment 的布局。
- layout:XML 布局文件,比如
目前就只需要做这些更改,下面基于这些进行开发
2.调整依赖项文件
可以看到整个项目用的是kotlin语言,所以需要一步步转换成Java的,首先从依赖项文件build.gradle.kts入手,把它的内容转换成build.gradle,也就是将 Kotlin DSL (build.gradle.kts
) 转换为 Groovy DSL (build.gradle
),Kotlin DSL (build.gradle.kts
) 和 Groovy DSL (build.gradle
) 是 Android Gradle 构建系统中用于定义构建脚本的两种不同语言,具体细节大家自己查询进一步了解。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
namespace 'com.example.personalexpensetracker'
compileSdk 34
defaultConfig {
applicationId "com.example.personalexpensetracker"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false // 这里不再使用 isMinifyEnabled
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.lifecycle.runtime.ktx
implementation libs.androidx.activity.compose
implementation platform(libs.androidx.compose.bom)
implementation libs.androidx.ui
implementation libs.androidx.ui.graphics
implementation libs.androidx.ui.tooling.preview
implementation libs.androidx.material3
implementation libs.androidx.appcompat
// 添加 Room 依赖项
implementation 'androidx.room:room-runtime:2.4.3'
annotationProcessor 'androidx.room:room-compiler:2.4.3' // Java项目
// 如果是Kotlin项目,使用下面的行
// kapt 'androidx.room:room-compiler:2.4.3'
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
androidTestImplementation platform(libs.androidx.compose.bom)
androidTestImplementation libs.androidx.ui.test.junit4
debugImplementation libs.androidx.ui.tooling
debugImplementation libs.androidx.ui.test.manifest
}
文件转换完以后重新构建一下,同时我添加了用于操作数据库的依赖项ROOM,大家可以把这个理解为python中用pip安装需要的包一样。
3.创建用户类
完成依赖项的安装后,为了实现登录注册功能,肯定需要使用数据库来存储相关数据,而管理这些数据最合适的数据模型就是构建一个用户类,因为是一个个用户进行登录注册,所以在用户类中定义每个用户的数据项即可。我们在data下的model中创建一个User.java文件,来表示用户类。
下面是具体代码,其中的Java语法包括:
-
类定义:
public class User
:定义了一个名为User
的公共类,任何其他类都可以访问。 -
注解:
注解(如@Entity
和@PrimaryKey
)是元数据,用于提供关于代码元素(如类、方法、字段)的额外信息。 -
访问修饰符:
public
:表示该类或方法对所有其他类可见。private
:表示字段只能在类内部访问,封装了数据,保护了类的内部状态。 -
构造函数:
构造函数的名称与类名相同,用于初始化对象的状态。 -
方法:
方法是执行特定操作的代码块。Getter 和 Setter 是常见的 Java 方法,常用于访问和修改私有属性。
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
private int id;
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
// Getter 和 Setter 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
4.创建DAO
在 Android 中,DAO(Data Access Object)是一个接口或抽象类,用于定义与数据库交互的方法。DAO 是 Room 数据库架构组件的一个重要部分,负责提供对数据库的访问,以便进行增删改查(CRUD)操作。我们创建一个新的包名为dao,里面存放dao文件。
package com.example.personalexpensetracker.data.dao;
import com.example.personalexpensetracker.data.model.User;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
@Dao
public interface UserDao {
@Insert
void insert(User user);
@Query("SELECT * FROM users WHERE username = :username AND password = :password LIMIT 1")
User login(String username, String password);
}
5.创建Room数据库
AppDatabase
类用于定义 Room 数据库的基本信息,包括数据库包含的实体和访问 DAO 的方法。该类通常放在 database
包中,以便于管理。
package com.example.personalexpensetracker.data.database;
import com.example.personalexpensetracker.data.dao.UserDao;
import com.example.personalexpensetracker.data.model.User;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import android.content.Context;
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase instance;
public abstract UserDao userDao();
public static synchronized AppDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "user_database")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
}
至此我们就简单的把用户数据模型和基础的数据库操作结构配置完毕,当然后续需要再更改,当前写的更多是为了项目完整和便于理解学习。
6.分别创建注册和登录界面xml文件
接下来我们分别把注册和登录界面的布局文件创建好,写在res/layout文件夹中。
activity_register.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/phoneEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入手机号"
android:inputType="phone"/>
<CheckBox
android:id="@+id/agreeCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已阅读并同意《用户协议》和《隐私协议》"/>
<Button
android:id="@+id/registerButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="注册"/>
</LinearLayout>
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/etUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="用户名" />
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码"
android:inputType="textPassword" />
<Button
android:id="@+id/btnLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录" />
<TextView
android:id="@+id/tvRegister"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="没有账户? 注册"
android:layout_marginTop="16dp" />
</LinearLayout>
启动测试效果如下:
可以看到页面启动成功,我们下面先把逻辑代码编写完成,然后再调整ui。具体逻辑包括注册后数据保存;首次注册后直接进入主页;下次启动判断是否需要再次登录还是直接进入主页;登录界面输入密码登录;高阶操作包括用手机号注册。
篇幅所限,下篇文章见。