以下是基于推荐技术栈的 Android 笔记 App 初始化项目结构。我们将使用 Kotlin、Jetpack Compose、Room、Hilt 等技术栈。
1. 项目初始化
-
创建项目
- 打开 Android Studio,选择 New Project。
- 选择 Empty Compose Activity 模板。
- 设置项目名称(如
NotesApp
),包名(如com.example.notes
),语言选择 Kotlin。 - 最低 API 级别选择 21(Android 5.0)。
-
配置 Gradle
- 在
build.gradle.kts
中添加必要的依赖项。
- 在
2. 项目结构
bash
NotesApp/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/notes/
│ │ │ │ ├── ui/ # UI 层(Compose)
│ │ │ │ ├── viewmodel/ # ViewModel
│ │ │ │ ├── repository/ # 数据仓库
│ │ │ │ ├── database/ # Room 数据库
│ │ │ │ ├── model/ # 数据模型
│ │ │ │ └── di/ # 依赖注入
│ │ │ └── res/ # 资源文件
│ │ └── test/ # 单元测试
│ └── build.gradle.kts # 模块构建脚本
├── build.gradle.kts # 项目构建脚本
└── settings.gradle.kts # 项目设置
3. 配置 Gradle 文件
build.gradle.kts
(项目级)
kotlin
plugins {
alias(libs.plugins.gradle.versions)
alias(libs.plugins.version.catalog.update)
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.compose) apply false
}
build.gradle.kts
(模块级)
kotlin
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.compose)
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
}
android {
compileSdk = libs.versions.compileSdk.get().toInt()
namespace = "com.example.jetnews"
defaultConfig {
applicationId = "com.nemo.notes"
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
// Important: change the keystore for a production deployment
val userKeystore = File(System.getProperty("user.home"), ".android/debug.keystore")
val localKeystore = rootProject.file("debug_2.keystore")
val hasKeyInfo = userKeystore.exists()
create("release") {
storeFile = if (hasKeyInfo) userKeystore else localKeystore
storePassword = if (hasKeyInfo) "android" else System.getenv("compose_store_password")
keyAlias = if (hasKeyInfo) "androiddebugkey" else System.getenv("compose_key_alias")
keyPassword = if (hasKeyInfo) "android" else System.getenv("compose_key_password")
}
}
buildTypes {
getByName("debug") {
}
getByName("release") {
isMinifyEnabled = true
signingConfig = signingConfigs.getByName("release")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
compose = true
}
packaging.resources {
// Multiple dependency bring these files in. Exclude them to enable
// our test APK to build (has no effect on our AARs)
excludes += "/META-INF/AL2.0"
excludes += "/META-INF/LGPL2.1"
}
hilt {
enableAggregatingTask = false
}
}
kotlin {
jvmToolchain(17)
}
dependencies {
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
androidTestImplementation(composeBom)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
// Compose
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.animation)
implementation(libs.androidx.compose.foundation.layout)
implementation("com.google.android.material:material:1.10.0")
implementation(libs.androidx.compose.material.iconsExtended)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.materialWindow)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.lifecycle.viewModelCompose)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.activity.compose)
// Core
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity.ktx)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.glance)
implementation(libs.androidx.glance.appwidget)
implementation(libs.androidx.glance.material3)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.androidx.lifecycle.viewmodel.savedstate)
implementation(libs.androidx.lifecycle.livedata.ktx)
implementation(libs.androidx.window)
// Room
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
// Hilt
implementation(libs.hilt.android)
implementation(libs.androidx.hilt.navigation.compose)
ksp(libs.hilt.compiler)
implementation("com.squareup:javapoet:1.13.0")
// Testing
androidTestImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.core)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.androidx.test.rules)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.kotlinx.coroutines.test)
androidTestImplementation(libs.androidx.compose.ui.test)
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
// Debug
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)
}
libs.versions.toml
(模块级)
kotlin
#####
# This file is duplicated to individual samples from the global scripts/libs.versions.toml
# Do not add a dependency to an individual sample, edit the global version instead.
#####
[versions]
accompanist = "0.37.0"
androidGradlePlugin = "8.7.3"
androidx-activity-compose = "1.9.3"
androidx-appcompat = "1.7.0"
androidx-compose-bom = "2024.12.01"
androidx-constraintlayout = "1.1.0"
androidx-core-splashscreen = "1.0.1"
androidx-corektx = "1.15.0"
androidx-glance = "1.1.1"
androidx-lifecycle = "2.8.2"
androidx-lifecycle-compose = "2.8.7"
androidx-lifecycle-runtime-compose = "2.8.7"
androidx-navigation = "2.8.5"
androidx-palette = "1.0.0"
androidx-test = "1.6.1"
androidx-test-espresso = "3.6.1"
androidx-test-ext-junit = "1.2.1"
androidx-test-ext-truth = "1.6.0"
androidx-tv-foundation = "1.0.0-alpha11"
androidx-tv-material = "1.0.0"
androidx-wear-compose = "1.4.0"
androidx-window = "1.3.0"
androidxHiltNavigationCompose = "1.2.0"
androix-test-uiautomator = "2.3.0"
coil = "2.7.0"
# @keep
compileSdk = "35"
coroutines = "1.9.0"
google-maps = "18.2.0"
gradle-versions = "0.51.0"
hilt = "2.53.1"
hiltExt = "1.2.0"
horologist = "0.6.22"
# @pin When updating to AGP 7.4.0-alpha10 and up we can update this https://developer.android.com/studio/write/java8-support#library-desugaring-versions
jdkDesugar = "1.2.2"
junit = "4.13.2"
kotlin = "2.1.0"
kotlinx-serialization-json = "1.7.3"
kotlinx_immutable = "0.3.8"
ksp = "2.1.0-1.0.29"
maps-compose = "3.1.1"
# @keep
minSdk = "21"
okhttp = "4.12.0"
play-services-wearable = "18.1.0"
robolectric = "4.14.1"
roborazzi = "1.37.0"
rome = "1.18.0"
room = "2.6.1"
secrets = "2.0.1"
# @keep
targetSdk = "33"
version-catalog-update = "0.8.5"
[libraries]
accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" }
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" }
androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity-compose" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-compose-animation = { module = "androidx.compose.animation:animation" }
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" }
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" }
androidx-compose-material-iconsExtended = { module = "androidx.compose.material:material-icons-extended" }
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
androidx-compose-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" }
androidx-compose-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout" }
androidx-compose-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation" }
androidx-compose-material3-adaptive-navigationSuite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite" }
androidx-compose-materialWindow = { module = "androidx.compose.material3:material3-window-size-class" }
androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" }
androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" }
androidx-compose-ui = { module = "androidx.compose.ui:ui" }
androidx-compose-ui-googlefonts = { module = "androidx.compose.ui:ui-text-google-fonts" }
androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" }
androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test" }
androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" }
androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }
androidx-compose-ui-text = { module = "androidx.compose.ui:ui-text" }
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util" }
androidx-compose-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding" }
androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "androidx-constraintlayout" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-corektx" }
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-core-splashscreen" }
androidx-glance = { module = "androidx.glance:glance", version.ref = "androidx-glance" }
androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "androidx-glance" }
androidx-glance-material3 = { module = "androidx.glance:glance-material3", version.ref = "androidx-glance" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-compose" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle-compose" }
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle-runtime-compose" }
androidx-lifecycle-viewModelCompose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle-compose" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-compose" }
androidx-lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "androidx-lifecycle-compose" }
androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }
androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" }
androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidx-navigation" }
androidx-palette = { module = "androidx.palette:palette", version.ref = "androidx-palette" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-ext-junit" }
androidx-test-ext-truth = { module = "androidx.test.ext:truth", version.ref = "androidx-test-ext-truth" }
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" }
androidx-test-runner = "androidx.test:runner:1.6.2"
androidx-test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version.ref = "androix-test-uiautomator" }
androidx-tv-foundation = { module = "androidx.tv:tv-foundation", version.ref = "androidx-tv-foundation" }
androidx-tv-material = { module = "androidx.tv:tv-material", version.ref = "androidx-tv-material" }
androidx-wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "androidx-wear-compose" }
androidx-wear-compose-material = { module = "androidx.wear.compose:compose-material", version.ref = "androidx-wear-compose" }
androidx-wear-compose-navigation = { module = "androidx.wear.compose:compose-navigation", version.ref = "androidx-wear-compose" }
androidx-wear-compose-ui-tooling = { module = "androidx.wear.compose:compose-ui-tooling", version.ref = "androidx-wear-compose" }
androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" }
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window" }
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
core-jdk-desugaring = { module = "com.android.tools:desugar_jdk_libs", version.ref = "jdkDesugar" }
dagger-hiltandroidplugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hilt" }
googlemaps-compose = { module = "com.google.maps.android:maps-compose", version.ref = "maps-compose" }
googlemaps-maps = { module = "com.google.android.gms:play-services-maps", version.ref = "google-maps" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
hilt-ext-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltExt" }
horologist-audio-ui = { module = "com.google.android.horologist:horologist-audio-ui", version.ref = "horologist" }
horologist-composables = { module = "com.google.android.horologist:horologist-composables", version.ref = "horologist" }
horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" }
horologist-compose-material = { module = "com.google.android.horologist:horologist-compose-material", version.ref = "horologist" }
horologist-compose-tools = { module = "com.google.android.horologist:horologist-compose-tools", version.ref = "horologist" }
horologist-images-coil = { module = "com.google.android.horologist:horologist-images-coil", version.ref = "horologist" }
horologist-media-data = { module = "com.google.android.horologist:horologist-media-data", version.ref = "horologist" }
horologist-media-ui = { module = "com.google.android.horologist:horologist-media-ui", version.ref = "horologist" }
horologist-roboscreenshots = { module = "com.google.android.horologist:horologist-roboscreenshots", version.ref = "horologist" }
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinx_immutable" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "play-services-wearable" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" }
roborazzi-compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" }
roborazzi-rule = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" }
rometools-modules = { module = "com.rometools:rome-modules", version.ref = "rome" }
rometools-rome = { module = "com.rometools:rome", version.ref = "rome" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" }
compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
gradle-versions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" }
version-catalog-update = { id = "nl.littlerobots.version-catalog-update", version.ref = "version-catalog-update" }
4. 创建数据模型
Note.kt
kotlin
// Note.kt
package com.nemo.notes.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.Date
@Entity(tableName = "notes")
data class Note(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String,
val createdAt: Date = Date(),
val updatedAt: Date = Date()
)
5. 创建 Room 数据库
NoteDatabase.kt
kotlin
package com.nemo.notes.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.nemo.notes.model.Note
@Database(entities = [Note::class], version = 1, exportSchema = false)
@TypeConverters(DateConverter::class)
abstract class NoteDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDao
companion object {
@Volatile
private var INSTANCE: NoteDatabase? = null
fun getDatabase(context: Context): NoteDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java,
"note_database"
).build()
INSTANCE = instance
instance
}
}
}
}
NoteDao.kt
kotlin
package com.nemo.notes.database
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import com.nemo.notes.model.Note
import kotlinx.coroutines.flow.Flow
@Dao
interface NoteDao {
@Query("SELECT * FROM notes ORDER BY updatedAt DESC")
fun getAllNotes(): Flow<List<Note>>
@Insert
suspend fun insert(note: Note): Long
@Query("DELETE FROM notes WHERE id = :noteId")
suspend fun delete(noteId: Long): Int
@Delete
suspend fun delete(note: Note): Int
}
DateConverter.kt
kotlin
// DateConverter.kt
package com.nemo.notes.database
import androidx.room.TypeConverter
import java.util.Date
class DateConverter {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time
}
}
6. 创建 Repository
NoteRepository.kt
kotlin
package com.nemo.notes.repository
import com.nemo.notes.database.NoteDao
import com.nemo.notes.model.Note
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class NoteRepository @Inject constructor(
private val noteDao: NoteDao
) {
fun getAllNotes(): Flow<List<Note>> = noteDao.getAllNotes()
suspend fun insert(note: Note) = noteDao.insert(note)
suspend fun delete(noteId: Long) = noteDao.delete(noteId)
}
7. 配置 Hilt
AppModule.kt
kotlin
package com.nemo.notes.di
import android.content.Context
import com.nemo.notes.database.NoteDatabase
import com.nemo.notes.repository.NoteRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideNoteDatabase(@ApplicationContext context: Context): NoteDatabase {
return NoteDatabase.getDatabase(context)
}
@Provides
@Singleton
fun provideNoteRepository(database: NoteDatabase): NoteRepository {
return NoteRepository(database.noteDao())
}
}
NotesApp.kt
kotlin
package com.nemo.notes
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class NotesApp : Application() {
companion object {
const val NOTES_APP_URI = "https://developer.android.com/notes"
}
}
8. 创建 ViewModel
NoteViewModel.kt
kotlin
package com.nemo.notes.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nemo.notes.model.Note
import com.nemo.notes.repository.NoteRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class NoteViewModel @Inject constructor(
private val repository: NoteRepository
) : ViewModel() {
val allNotes: Flow<List<Note>> = repository.getAllNotes()
fun insert(note: Note) = viewModelScope.launch {
repository.insert(note)
}
fun addNote(note: Note) {
viewModelScope.launch {
repository.insert(note)
}
}
fun delete(noteId: Long) = viewModelScope.launch {
repository.delete(noteId)
}
}
9. 创建 UI
MainActivity.kt
kotlin
package com.nemo.notes.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.nemo.notes.ui.theme.NotesAppTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
NotesAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "noteList") {
composable("noteList") {
NoteListScreen(navController)
}
composable("noteEdit/{noteId}") { backStackEntry ->
val noteId = backStackEntry.arguments?.getString("noteId")?.toLongOrNull()
NoteEditScreen(navController, noteId)
}
}
}
}
}
}
}
NoteListScreen.kt
kotlin
package com.nemo.notes.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import com.nemo.notes.viewmodel.NoteViewModel
@Composable
fun NoteListScreen(navController: NavHostController) {
val viewModel: NoteViewModel = hiltViewModel()
val notes by viewModel.allNotes.collectAsState(initial = emptyList())
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "My Notes", style = MaterialTheme.typography.headlineMedium)
LazyColumn {
items(notes) { note ->
Text(text = note.title, modifier = Modifier.padding(8.dp))
}
}
}
}
@Composable
fun NoteEditScreen(navController: NavHostController, noteId: Long?) {
// ...
}
以上为主要部分
项目代码参考地址:github.com/wxxzy/Notes...
10. 运行项目
- 连接 Android 设备或启动模拟器。
- 点击 Android Studio 中的 Run 按钮。
- 应用将启动并显示一个简单的笔记列表界面。
11. 下一步
- 添加笔记创建和编辑功能。
- 实现搜索和标签管理功能。
- 集成云同步和备份功能。
- 优化 UI 和用户体验。
通过以上步骤,您已经成功初始化了一个基于 Kotlin、Jetpack Compose、Room 和 Hilt 的 Android 笔记应用项目。如果您有更多需求或问题,欢迎进一步讨论!