一、项目准备

1. 环境配置

    •    Android Studio 版本:建议 Arctic Fox 及以上(确保支持 Jetpack 组件)

    •    Gradle 配置:在 app/build.gradle 中添加依赖(同步后生效):
// 基础组件
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

// RecyclerView(列表展示)
implementation 'androidx.recyclerview:recyclerview:1.3.2'

// Room(本地数据库)
implementation 'androidx.room:room-runtime:2.5.2'
kapt 'androidx.room:room-compiler:2.5.2'
implementation 'androidx.room:room-ktx:2.5.2' // Kotlin 扩展

// ViewModel + LiveData(MVVM 架构)
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2'

// Kotlin 协程(异步操作)
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
二、数据模型设计(Entity)

创建 Todo 数据类,对应数据库表结构:
// 路径:app/src/main/java/com/example/todolist/model/Todo.kt
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "todo_table") // 数据库表名
data class Todo(
    @PrimaryKey(autoGenerate = true) val id: Int = 0, // 自增主键
    val title: String, // 待办事项标题
    val isCompleted: Boolean = false // 是否完成(默认未完成)
)
三、本地数据库(Room)

1. 数据访问接口(DAO)

定义对数据库的操作(增、删、查):
// 路径:app/src/main/java/com/example/todolist/data/TodoDao.kt
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import kotlinx.coroutines.flow.Flow

@Dao
interface TodoDao {
    // 查询所有待办事项(按 ID 升序),返回 Flow(实时监听数据变化)
    @Query("SELECT * FROM todo_table ORDER BY id ASC")
    fun getAllTodos(): Flow<List<Todo>>

    // 插入单个待办事项
    @Insert
    suspend fun insertTodo(todo: Todo)

    // 删除单个待办事项
    @Delete
    suspend fun deleteTodo(todo: Todo)
}
2. 数据库实例(Database)

创建数据库单例,关联表和 DAO:
// 路径:app/src/main/java/com/example/todolist/data/TodoDatabase.kt
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context

// 数据库版本号(升级时需递增)
@Database(entities = [Todo::class], version = 1, exportSchema = false)
abstract class TodoDatabase : RoomDatabase() {

    // 暴露 DAO 接口
    abstract fun todoDao(): TodoDao

    // 单例模式(避免重复创建数据库实例)
    companion object {
        @Volatile
        private var INSTANCE: TodoDatabase? = null

        fun getDatabase(context: Context): TodoDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TodoDatabase::class.java,
                    "todo_database" // 数据库文件名
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}
四、业务逻辑(Repository)

统一数据操作入口,隔离数据库与ViewModel:
// 路径:app/src/main/java/com/example/todolist/repository/TodoRepository.kt
import androidx.lifecycle.LiveData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class TodoRepository(private val todoDao: TodoDao) {

    // 从 DAO 获取所有待办事项(通过 Flow 实时更新)
    val allTodos: Flow<List<Todo>> = todoDao.getAllTodos()

    // 插入待办事项(协程中执行,避免主线程阻塞)
    suspend fun insert(todo: Todo) = withContext(Dispatchers.IO) {
        todoDao.insertTodo(todo)
    }

    // 删除待办事项(协程中执行)
    suspend fun delete(todo: Todo) = withContext(Dispatchers.IO) {
        todoDao.deleteTodo(todo)
    }
}
五、视图模型(ViewModel)

处理UI逻辑,关联Repository与界面:
// 路径:app/src/main/java/com/example/todolist/viewmodel/TodoViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

class TodoViewModel(private val repository: TodoRepository) : ViewModel() {

    // 暴露所有待办事项(供UI观察)
    val allTodos: Flow<List<Todo>> = repository.allTodos

    // 添加待办事项(调用Repository,在协程中执行)
    fun insertTodo(title: String) {
        if (title.isNotBlank()) { // 避免空内容
            viewModelScope.launch {
                repository.insert(Todo(title = title))
            }
        }
    }

    // 删除待办事项
    fun deleteTodo(todo: Todo) {
        viewModelScope.launch {
            repository.delete(todo)
        }
    }
}

// 用于创建ViewModel(关联Repository和Database)
class TodoViewModelFactory(private val repository: TodoRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(TodoViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return TodoViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
六、界面设计(UI)

1. 主布局(activity_main.xml)

包含输入框、添加按钮和RecyclerView列表:
<!-- 路径:app/src/main/res/layout/activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.MainActivity">

    <!-- 输入区域:输入框 + 添加按钮 -->
    <LinearLayout
        android:id="@+id/inputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="16dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <EditText
            android:id="@+id/etTodoTitle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="输入待办事项..."
            android:inputType="textCapSentences"/>

        <Button
            android:id="@+id/btnAdd"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:text="添加"/>
    </LinearLayout>

    <!-- 待办事项列表 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvTodoList"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/inputLayout"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        tools:listitem="@layout/item_todo"/>

</androidx.constraintlayout.widget.ConstraintLayout>
2. 列表项布局(item_todo.xml)

每个待办事项的布局(标题 + 删除按钮):
<!-- 路径:app/src/main/res/layout/item_todo.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="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp"
    android:gravity="center_vertical">

    <!-- 待办事项标题(如果完成,显示中划线) -->
    <TextView
        android:id="@+id/tvTodoTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="16sp"
        android:textStyle="bold"/>

    <!-- 删除按钮 -->
    <Button
        android:id="@+id/btnDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="删除"
        android:backgroundTint="@color/red"/>

</LinearLayout>
七、列表适配器(RecyclerView.Adapter)

连接数据与列表项视图:
// 路径:app/src/main/java/com/example/todolist/adapter/TodoAdapter.kt
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.todolist.databinding.ItemTodoBinding
import com.example.todolist.model.Todo

// 利用 DiffUtil 优化列表更新(只刷新变化的项)
class TodoAdapter(private val onDeleteClick: (Todo) -> Unit) :
    ListAdapter<Todo, TodoAdapter.TodoViewHolder>(TodoDiffCallback()) {

    // 视图持有者(绑定列表项视图)
    inner class TodoViewHolder(private val binding: ItemTodoBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(todo: Todo) {
            binding.tvTodoTitle.text = todo.title
            // 如果待办事项已完成,添加中划线
            binding.tvTodoTitle.paint.isStrikeThruText = todo.isCompleted

            // 删除按钮点击事件
            binding.btnDelete.setOnClickListener {
                onDeleteClick(todo)
            }
        }
    }

    // 创建视图持有者(加载列表项布局)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        val binding = ItemTodoBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return TodoViewHolder(binding)
    }

    // 绑定数据到视图
    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    // DiffUtil 回调(判断数据是否变化)
    class TodoDiffCallback : DiffUtil.ItemCallback<Todo>() {
        override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean {
            return oldItem.id == newItem.id // 按 ID 判断是否为同一 item
        }

        override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean {
            return oldItem == newItem // 按内容判断是否变化
        }
    }
}
八、主界面逻辑(MainActivity)

关联ViewModel与UI,处理用户交互:
// 路径:app/src/main/java/com/example/todolist/view/MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todolist.adapter.TodoAdapter
import com.example.todolist.data.TodoDatabase
import com.example.todolist.databinding.ActivityMainBinding
import com.example.todolist.repository.TodoRepository

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var todoViewModel: TodoViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化视图绑定
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 初始化 Repository(关联数据库)
        val todoDao = TodoDatabase.getDatabase(this).todoDao()
        val repository = TodoRepository(todoDao)

        // 初始化 ViewModel(通过 Factory 关联 Repository)
        val viewModelFactory = TodoViewModelFactory(repository)
        todoViewModel = ViewModelProvider(this, viewModelFactory)[TodoViewModel::class.java]

        // 初始化适配器(设置删除点击事件)
        val adapter = TodoAdapter { todo ->
            todoViewModel.deleteTodo(todo)
        }

        // 配置 RecyclerView(线性布局)
        binding.rvTodoList.adapter = adapter
        binding.rvTodoList.layoutManager = LinearLayoutManager(this)

        // 观察数据变化(当数据库数据更新时,自动刷新列表)
        todoViewModel.allTodos.observe(this) { todos ->
            adapter.submitList(todos)
        }

        // 添加按钮点击事件(获取输入内容并添加到数据库)
        binding.btnAdd.setOnClickListener {
            val title = binding.etTodoTitle.text.toString()
            todoViewModel.insertTodo(title)
            binding.etTodoTitle.text.clear() // 清空输入框
        }
    }
}
九、运行效果

    1.    启动应用后,界面显示输入框和“添加”按钮,下方为空白列表。

    2.    在输入框中输入内容(如“学习Android”),点击“添加”,列表会实时显示该待办事项。

    3.    点击列表项右侧的“删除”按钮,对应的待办事项会从列表

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐