以下是一个基于 Android Studio 的简单 TODO 待办事项应用实例,包含基本的增、删、改、查功能,适合入门学习。项目使用 RecyclerView 展示数据,结合 SharedPrefe

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

 

TODO 待办事项应用实现步骤

一、创建项目与配置

    1.    打开 Android Studio,选择 "Empty Activity",项目名称设为 "TodoApp",包名默认,语言选择 Kotlin,最小 SDK 选 API 21(兼容 Android 5.0+)。

    2.    无需额外依赖,默认的 RecyclerView 和 ConstraintLayout 已满足需求。

二、数据模型设计(Todo.kt)

创建待办事项的数据类,存储标题、是否完成状态:
data class Todo(
    val id: Int, // 唯一标识,用于区分不同待办项
    val title: String, // 待办事项标题
    var isCompleted: Boolean // 是否完成
)
三、布局文件设计

1. 主界面布局(activity_main.xml)

使用 ConstraintLayout 包含输入框、添加按钮和 RecyclerView:
<?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=".MainActivity">

    <!-- 输入框与添加按钮 -->
    <EditText
        android:id="@+id/etTodoTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="请输入待办事项..."
        android:inputType="textCapSentences"
        app:layout_constraintEnd_toStartOf="@id/btnAdd"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintMarginStart="16dp"
        app:layout_constraintMarginTop="16dp" />

    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/etTodoTitle"
        app:layout_constraintTop_toTopOf="@id/etTodoTitle"
        app:layout_constraintMarginStart="8dp"
        app:layout_constraintMarginEnd="16dp" />

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

</androidx.constraintlayout.widget.ConstraintLayout>
2. 列表项布局(item_todo.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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:background="?attr/selectableItemBackground">

    <CheckBox
        android:id="@+id/cbCompleted"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_marginStart="16dp"
        app:layout_constraintStart_toEndOf="@id/cbCompleted"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
四、适配器设计(TodoAdapter.kt)

连接数据与 RecyclerView,处理列表项的点击和状态更新:
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.todoapp.databinding.ItemTodoBinding

// 使用 ListAdapter 优化列表刷新(基于 DiffUtil)
class TodoAdapter(
    private val onTodoChecked: (Todo, Boolean) -> Unit, // 复选框状态变化回调
    private val onTodoLongClick: (Todo) -> Unit // 长按删除回调
) : ListAdapter<Todo, TodoAdapter.TodoViewHolder>(TodoDiffCallback()) {

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

        init {
            // 复选框状态变化监听
            binding.cbCompleted.setOnCheckedChangeListener { _, isChecked ->
                val todo = getItem(adapterPosition)
                onTodoChecked(todo, isChecked)
            }
            // 长按删除
            binding.root.setOnLongClickListener {
                val todo = getItem(adapterPosition)
                onTodoLongClick(todo)
                true
            }
        }

        // 绑定数据到视图
        fun bind(todo: Todo) {
            binding.tvTitle.text = todo.title
            binding.cbCompleted.isChecked = todo.isCompleted
            // 完成的项添加删除线
            binding.tvTitle.paint.isStrikeThruText = todo.isCompleted
        }
    }

    // 创建视图持有者
    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.kt)

处理添加待办、数据存储、列表更新等核心逻辑:
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todoapp.databinding.ActivityMainBinding
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var todoAdapter: TodoAdapter
    private lateinit var sharedPreferences: SharedPreferences
    private val gson = Gson() // 用于将对象转为 JSON 字符串(存储需要)
    private var todoList = mutableListOf<Todo>()
    private var nextId = 1 // 用于生成待办项的唯一 id

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 初始化 SharedPreferences(本地存储)
        sharedPreferences = getSharedPreferences("TodoPrefs", MODE_PRIVATE)
        // 从本地加载数据
        loadTodos()

        // 初始化适配器
        todoAdapter = TodoAdapter(
            onTodoChecked = { todo, isCompleted ->
                // 更新待办项的完成状态
                todo.isCompleted = isCompleted
                saveTodos() // 保存到本地
                todoAdapter.notifyDataSetChanged() // 刷新列表
            },
            onTodoLongClick = { todo ->
                // 长按删除
                todoList.remove(todo)
                saveTodos() // 保存到本地
                todoAdapter.submitList(todoList.toList()) // 提交新列表
                Toast.makeText(this, "已删除", Toast.LENGTH_SHORT).show()
            }
        )

        // 配置 RecyclerView
        binding.rvTodoList.apply {
            adapter = todoAdapter
            layoutManager = LinearLayoutManager(this@MainActivity)
            setHasFixedSize(true) // 列表大小固定,优化性能
        }

        // 提交新列表(初始化显示)
        todoAdapter.submitList(todoList.toList())

        // 添加待办项按钮点击事件
        binding.btnAdd.setOnClickListener {
            val title = binding.etTodoTitle.text.toString().trim()
            if (title.isNotEmpty()) {
                // 创建新待办项
                val newTodo = Todo(
                    id = nextId++,
                    title = title,
                    isCompleted = false
                )
                todoList.add(newTodo)
                saveTodos() // 保存到本地
                todoAdapter.submitList(todoList.toList()) // 刷新列表
                binding.etTodoTitle.text.clear() // 清空输入框
            } else {
                Toast.makeText(this, "请输入待办事项", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // 从 SharedPreferences 加载待办列表
    private fun loadTodos() {
        val json = sharedPreferences.getString("todoList", null)
        if (json != null) {
            // 解析 JSON 为 List<Todo>
            val type = object : TypeToken<MutableList<Todo>>() {}.type
            todoList = gson.fromJson(json, type)
            // 更新 nextId(避免新增项 id 重复)
            if (todoList.isNotEmpty()) {
                nextId = todoList.maxOf { it.id } + 1
            }
        }
    }

    // 将待办列表保存到 SharedPreferences
    private fun saveTodos() {
        val json = gson.toJson(todoList)
        sharedPreferences.edit().putString("todoList", json).apply()
    }
}
六、功能说明与扩展方向

已实现功能:

    •    输入标题并点击“添加”,新增待办事项到列表;

    •    点击复选框标记待办项为“已完成”(标题显示删除线);

    •    长按待办项可删除;

    •    数据通过 SharedPreferences 本地存储,重启应用后不丢失。

扩展方向:

    •    添加“编辑待办项”功能(点击项弹出编辑框);

    •    按“已完成/未完成”筛选列表;

    •    使用 Room 数据库替代 SharedPreferences,支持更复杂的查询;

    •    添加动画效果(如添加/删除项时的过渡动画)。

运行效果

    1.    打开应用后,在输入框中输入“学习Android”,点击“添加”,列表中新增对应项;

    2.    点击复选框,标题显示删除线,标记为完成;

    3.    长按某一项,弹出“已删除”提示,该项从列表中移除;

    4.    关闭应用后重新打开,之前的待办项依然存在。

该实例覆盖了 Android 开发的核心知识点:布局设计、RecyclerView 用法、数据存储、事件处理等,适合初学者上手实践。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值