📌  相关文章
📜  如何使用 MVVM 和 Room 数据库构建一个简单的 Note Android 应用程序?(1)

📅  最后修改于: 2023-12-03 15:08:22.922000             🧑  作者: Mango

如何使用 MVVM 和 Room 数据库构建一个简单的 Note Android 应用程序?

介绍

MVVM 是一种使用数据绑定的软件架构模式,是 Model-View-ViewModel(模型-视图-视图模型)的缩写。它是将我们的代码分为三个部分:

  • Model:数据模型,代表真实存在的业务数据。
  • View:用户界面,代表用户最终看到的界面。
  • ViewModel:连接 Model 和 View 的中间件,处理 View 的状态和行为,并更新 Model。ViewModel 向 View 提供数据和通知 View 要进行的操作。View 可以通过数据绑定自动更新视图。

Room 是一个带有类型检查的 SQL 数据库,可以帮助我们轻松地在 Android 应用程序中存储数据。

在本文中,我们将讲解如何使用 MVVM 和 Room 数据库构建一个简单的 Note Android 应用程序。

步骤
创建项目

首先,我们需要创建一个新的 Android Studio 项目。选择一个空的活动,并将其命名为 "Notes"。

添加依赖项

要使用 Room 数据库,我们需要在项目中添加以下依赖项:

implementation "androidx.room:room-runtime:2.2.5"
implementation "androidx.room:room-ktx:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"
创建实体类

我们需要为 Note 对象创建一个实体类。实体类是一个在数据库中映射到表的 Java 类。它的属性对应于表中的列。

@Entity(tableName = "notes_table")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val content: String
)

在这里,我们使用了 @Entity 注释来指示这是一个实体类,并使用了 @PrimaryKey 注释来指定主键。

创建 DAO

接下来,我们将创建一个 Data Access Object(DAO),它定义了我们可以执行的操作。

@Dao
interface NoteDao {

    @Query("SELECT * FROM notes_table")
    fun getNotes(): LiveData<List<Note>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertOrUpdate(note: Note)

    @Delete
    suspend fun delete(note: Note)
}

在这里,我们使用了 @Dao 注释来指示这个接口是一个 DAO。

对于查询,我们使用 @Query 注释来指定 SQL 查询。

对于插入,我们使用 @Insert 注释来指定插入操作,并使用 @OnConflict 注释来指定当记录已经存在时应该执行的操作。

对于删除,我们使用 @Delete 注释来指定删除操作。

我们也使用了 suspend 关键字来指示这些操作是挂起函数,因为它们将在后台线程中运行。

创建数据库

我们需要创建一个 Room 数据库,它将包含我们的 Note 对象。

@Database(entities = [Note::class], version = 1, exportSchema = false)
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
            }
        }
    }
}

在这里,我们使用了 @Database 注释来指示这个类是一个 Room 数据库,并指定了它所包含的实体类和版本号。

我们定义了一个 getDatabase 函数,它将返回 NoteDatabase 对象。该函数使用单例模式来确保在整个应用程序中只创建一个实例。

创建 ViewModel

我们将创建一个 ViewModel 类,它将用来管理 Note 对象的生命周期和状态。

class NoteViewModel(application: Application) : AndroidViewModel(application) {

    private val noteDao = NoteDatabase.getDatabase(application).noteDao()

    val notes = noteDao.getNotes()

    fun insertOrUpdate(note: Note) {
        viewModelScope.launch(Dispatchers.IO) {
            noteDao.insertOrUpdate(note)
        }
    }

    fun delete(note: Note) {
        viewModelScope.launch(Dispatchers.IO) {
            noteDao.delete(note)
        }
    }
}

在这里,我们继承了 AndroidViewModel,并使用了 NoteDatabase.getDatabase 函数来获得 NoteDao 对象。我们使用 LiveData<List> 来存储我们的 Note 对象。

我们为 insertOrUpdate 和 delete 函数使用了协程来将它们从主线程移动到后台线程。

创建布局文件

我们将在布局文件中创建一个 RecyclerView 来显示所有 Note 对象。

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/notes_recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
创建 RecyclerViewAdapter

我们需要为 RecyclerView 创建一个适配器。适配器是将数据集绑定到 RecyclerView 上的关键组件之一。

class NotesRecyclerViewAdapter(private val notes: List<Note>, private val viewModel: NoteViewModel) : RecyclerView.Adapter<NotesRecyclerViewAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.note_item, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val note = notes[position]
        holder.titleView.text = note.title
        holder.contentView.text = note.content
        holder.itemView.setOnClickListener {
            val action = NotesFragmentDirections.actionNotesFragmentToNoteDetailFragment(note)
            it.findNavController().navigate(action)
        }
        holder.itemView.setOnLongClickListener {
            viewModel.delete(note)
            true
        }
    }

    override fun getItemCount() = notes.size

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val titleView: TextView = itemView.findViewById(R.id.note_item_title)
        val contentView: TextView = itemView.findViewById(R.id.note_item_content)
    }
}

在这里,我们定义了一个 ViewHolder 类,它包含我们在 RecyclerView 中显示的每个 item 的视图。

我们为 onCreateViewHolder 函数和 onBindViewHolder 函数定义了实现。onCreateViewHolder 函数用于创建 ViewHolder 对象,而 onBindViewHolder 函数用于将数据绑定到 ViewHolder 上。

我们为 getItemCount 函数返回列表项数量。

构建界面

我们将使用 Navigation 组件构建 NotesFragment 和 NoteDetailFragment。

<fragment
    android:id="@+id/nav_notes"
    android:name="com.example.notes.ui.notes.NotesFragment"
    android:label="Notes"
    tools:layout="@layout/fragment_notes" >
    <action
        android:id="@+id/action_notes_to_noteDetail"
        app:destination="@id/noteDetailFragment" />
</fragment>

<fragment
    android:id="@+id/noteDetailFragment"
    android:name="com.example.notes.ui.notes.NoteDetailFragment"
    android:label="Note Detail"
    tools:layout="@layout/fragment_note_detail" >
    <argument
        android:name="note"
        app:argType="com.example.notes.data.Note" />
</fragment>
创建 NotesFragment 和 NoteDetailFragment

我们将使用 NotesFragment 来显示所有 Note 对象,并使用 NoteDetailFragment 来显示特定的 Note 对象。

class NotesFragment : Fragment() {

    private lateinit var viewModel: NoteViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_notes, container, false)

        val recyclerView: RecyclerView = view.findViewById(R.id.notes_recyclerview)

        val adapter = NotesRecyclerViewAdapter(emptyList(), viewModel)

        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(requireContext())

        viewModel.notes.observe(viewLifecycleOwner, Observer {
            adapter.notes = it
        })

        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(requireActivity()).get(NoteViewModel::class.java)
    }
}

在这里,我们定义了一个 NotesFragment 类,并在 onCreateView 函数中创建了一个 RecyclerView 和一个 NotesRecyclerViewAdapter。

我们使用 ViewModelProvider 来获取 NoteViewModel 并将其存储在 viewModel 变量中。

我们在 onViewCreated 函数中获取 ViewModelProvider 并将其存储在 viewModel 变量中。

我们观察 LiveData<List>,并在 adapter 中更新数据。

class NoteDetailFragment : Fragment() {

    private lateinit var viewModel: NoteViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_note_detail, container, false)

        val args: NoteDetailFragmentArgs by navArgs()
        val note = args.note

        val titleEditText: EditText = view.findViewById(R.id.note_detail_title_edittext)
        val contentEditText: EditText = view.findViewById(R.id.note_detail_content_edittext)

        titleEditText.setText(note.title)
        contentEditText.setText(note.content)

        view.findViewById<Button>(R.id.update_button).setOnClickListener {
            note.title = titleEditText.text.toString()
            note.content = contentEditText.text.toString()
            viewModel.insertOrUpdate(note)

            it.findNavController().navigateUp()
        }

        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel = ViewModelProvider(requireActivity()).get(NoteViewModel::class.java)
    }
}

在这里,我们定义了一个 NoteDetailFragment 类,并在 onCreateView 函数中获取我们的 Note 对象和 EditText。

我们为 "update" 按钮设置了点击事件,以更新我们的 Note 对象,并使用 ViewModel 将其保存到数据库中。

我们使用 ViewModelProvider 来获取 NoteViewModel 并将其存储在 viewModel 变量中。

结论

在本文中,我们讲解了如何使用 MVVM 和 Room 数据库构建一个简单的 Note Android 应用程序。我们创建了一个实体类、DAO、数据库、ViewModel、布局文件、RecyclerViewAdapter 和两个 Fragment。我们将它们连接起来以构建一个完整的应用程序,使我们可以在 Android 设备上记录和查看笔记。