如何使用 MVVM 和 Room 数据库构建一个简单的 Note Android 应用程序?
Android 为我们提供了一项功能,我们可以使用该功能将用户的数据存储在他们的手机中,并使用不同的存储选项,例如共享首选项、SQLite 数据库和房间数据库。所有数据存储技术都有不同的用例。在本文中,我们将专门研究在 Android 中使用带有架构组件的 Room 数据库。在本文中,我们将专门研究 LiveData、ViewModel 和 Room 等组件。我们可以详细查看每个组件的说明。下面是它的指南。
下面列出了我们将在应用程序中使用的组件,并附有详细说明:
- LiveData:Live Data 是一个可以被观察的数据持有者类。它保存并缓存最新版本的数据,并在数据更新或更改时通知我们的观察者。 Live Data 会自动管理所有这些,因为它在观察的同时了解相关的生命周期状态变化。
- ViewModel:View Modal 充当存储库和 UI 之间的通信中心。 UI 不再需要担心数据的来源。 ViewModel 实例在 Activity/Fragment 重新创建后仍然存在。
- Repository :Repository 是一个主要用于管理多个数据源的类。
- Entity : Entity 是一个带注释的类,在我们使用 Room 时用于描述数据库表。
- 房间数据库:房间数据库是 SQLite 数据库的临时版本。它简化了数据库工作并充当了底层 SQLite 数据库的访问点。该房间使用 DAO 向 SQLite 数据库发出查询。
- DAO:DAO 是一个数据访问对象,用于将 SQL 查询映射到函数。
我们将在本文中构建什么?
我们将构建一个简单的 Notes 应用程序,在该应用程序中,我们将在用户添加的 Recycler View 中显示注释列表。除此之外,我们还可以向您的应用程序添加新注释。我们将使用 Room Database 将数据存储在用户的设备中。在下面的视频中,我们将看到我们将在本文中构建的内容。请注意,我们将使用Kotlin语言来实现这个项目。
分步实施
第 1 步:创建一个新项目
要在 Android Studio 中创建新项目,请参阅如何在 Android Studio 中创建/启动新项目。请注意,选择Kotlin作为编程语言。
第 2 步:更新 build.gradle(项目)文件。
导航到app > Gradle Scripts > build.gradle(:project)级别文件,在此文件中,我们必须在该文件中添加以下代码,这些代码将用于我们的依赖项版本。所以我们必须在这个文件的最后一个添加下面的代码块。
ext {
activityVersion = '1.2.3'
appCompatVersion = '1.3.0'
constraintLayoutVersion = '2.0.4'
coreTestingVersion = '2.1.0'
coroutines = '1.5.0'
lifecycleVersion = '2.3.1'
materialVersion = '1.3.0'
roomVersion = '2.3.0'
// testing
junitVersion = '4.13.2'
espressoVersion = '3.1.0'
androidxJunitVersion = '1.1.2'
}
第 3 步:更新 build.gradle (:app) 文件
导航到app > build.gradle并在其中,在第一个插件部分,我们必须为另一个插件添加 id。下面是我们 build.gradle 中存在的所有插件的代码。
plugins {
id 'com.android.application'
id 'kotlin-android'
// add below plugin
id 'kotlin-kapt'
}
现在在同一文件的 android 部分中,我们必须在 android 部分的最后一部分添加以下块。下面是该块的代码。
packagingOptions {
exclude 'META-INF/atomicfu.kotlin_module'
}
现在在 build.gradle 文件中添加以下依赖项。我们必须简单地使用以下内容更新依赖项部分。
dependencies {
implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion"
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
// Dependencies for working with Architecture components
// You'll probably have to update the version numbers in build.gradle (Project)
// Room components
implementation "androidx.room:room-ktx:$rootProject.roomVersion"
kapt "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion"
// Kotlin components
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
// UI
implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion"
implementation "com.google.android.material:material:$rootProject.materialVersion"
// Testing
testImplementation "junit:junit:$rootProject.junitVersion"
androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion"
androidTestImplementation ("androidx.test.espresso:espresso-core:$rootProject.espressoVersion", {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestImplementation "androidx.test.ext:junit:$rootProject.androidxJunitVersion"
}
更新您的 gradle 文件后。现在我们必须同步您的项目,然后我们必须通过单击右上角的“立即同步”选项在应用程序中安装所有依赖项。在我们的应用程序中安装所有依赖项后,我们现在将开始为我们的应用程序创建一个实体。
第 4 步:在 colors.xml 文件中添加新颜色
导航到应用程序 > res > values > colors.xml并向其添加以下颜色。
XML
#296D98
#296D98
#296D98
#FF03DAC5
#FF018786
#FF000000
#FFFFFFFF
#0e2433
#1C4966
#22252D
#424242
#ffa500
#0D2162
#17388E
#12B2E6
Kotlin
package com.gtappdevelopers.noteapplication
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
// on below line we are specifying our table name
@Entity(tableName = "notesTable")
// on below line we are specifying our column infor
// and inside that we are passing our column name
class Note (@ColumnInfo(name = "title")val noteTitle :String,@ColumnInfo(name = "description")val noteDescription :String,@ColumnInfo(name = "timestamp")val timeStamp :String) {
// on below line we are specifying our key and
// then auto generate as true and we are
// specifying its initial value as 0
@PrimaryKey(autoGenerate = true) var id = 0
}
Kotlin
package com.gtappdevelopers.noteapplication
import androidx.lifecycle.LiveData
import androidx.room.*
// annotation for dao class.
@Dao
interface NotesDao {
// below is the insert method for
// adding a new entry to our database.
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(note :Note)
// below is the delete method
// for deleting our note.
@Delete
suspend fun delete(note: Note)
// below is the method to read all the notes
// from our database we have specified the query for it.
// inside the query we are arranging it in ascending
// order on below line and we are specifying
// the table name from which
// we have to get the data.
@Query("Select * from notesTable order by id ASC")
fun getAllNotes(): LiveData>
// below method is use to update the note.
@Update
suspend fun update(note: Note)
}
Kotlin
package com.gtappdevelopers.noteapplication
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = arrayOf(Note::class), version = 1, exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {
abstract fun getNotesDao(): NotesDao
companion object {
// Singleton prevents multiple
// instances of database opening at the
// same time.
@Volatile
private var INSTANCE: NoteDatabase? = null
fun getDatabase(context: Context): NoteDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java,
"note_database"
).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
Kotlin
package com.gtappdevelopers.noteapplication
import androidx.annotation.WorkerThread
import androidx.lifecycle.LiveData
import java.util.concurrent.Flow
class NoteRepository(private val notesDao: NotesDao) {
// on below line we are creating a variable for our list
// and we are getting all the notes from our DAO class.
val allNotes: LiveData> = notesDao.getAllNotes()
// on below line we are creating an insert method
// for adding the note to our database.
suspend fun insert(note: Note) {
notesDao.insert(note)
}
// on below line we are creating a delete method
// for deleting our note from database.
suspend fun delete(note: Note){
notesDao.delete(note)
}
// on below line we are creating a update method for
// updating our note from database.
suspend fun update(note: Note){
notesDao.update(note)
}
}
Kotlin
package com.gtappdevelopers.noteapplication
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class NoteViewModal (application: Application) :AndroidViewModel(application) {
// on below line we are creating a variable
// for our all notes list and repository
val allNotes : LiveData>
val repository : NoteRepository
// on below line we are initializing
// our dao, repository and all notes
init {
val dao = NoteDatabase.getDatabase(application).getNotesDao()
repository = NoteRepository(dao)
allNotes = repository.allNotes
}
// on below line we are creating a new method for deleting a note. In this we are
// calling a delete method from our repository to delete our note.
fun deleteNote (note: Note) = viewModelScope.launch(Dispatchers.IO) {
repository.delete(note)
}
// on below line we are creating a new method for updating a note. In this we are
// calling a update method from our repository to update our note.
fun updateNote(note: Note) = viewModelScope.launch(Dispatchers.IO) {
repository.update(note)
}
// on below line we are creating a new method for adding a new note to our database
// we are calling a method from our repository to add a new note.
fun addNote(note: Note) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(note)
}
}
XML
XML
Kotlin
package com.gtappdevelopers.noteapplication
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class NoteRVAdapter(
val context: Context,
val noteClickDeleteInterface: NoteClickDeleteInterface,
val noteClickInterface: NoteClickInterface
) :
RecyclerView.Adapter() {
// on below line we are creating a
// variable for our all notes list.
private val allNotes = ArrayList()
// on below line we are creating a view holder class.
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// on below line we are creating an initializing all our
// variables which we have added in layout file.
val noteTV = itemView.findViewById(R.id.idTVNote)
val dateTV = itemView.findViewById(R.id.idTVDate)
val deleteIV = itemView.findViewById(R.id.idIVDelete)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// inflating our layout file for each item of recycler view.
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.note_rv_item,
parent, false
)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// on below line we are setting data to item of recycler view.
holder.noteTV.setText(allNotes.get(position).noteTitle)
holder.dateTV.setText("Last Updated : " + allNotes.get(position).timeStamp)
// on below line we are adding click listener to our delete image view icon.
holder.deleteIV.setOnClickListener {
// on below line we are calling a note click
// interface and we are passing a position to it.
noteClickDeleteInterface.onDeleteIconClick(allNotes.get(position))
}
// on below line we are adding click listener
// to our recycler view item.
holder.itemView.setOnClickListener {
// on below line we are calling a note click interface
// and we are passing a position to it.
noteClickInterface.onNoteClick(allNotes.get(position))
}
}
override fun getItemCount(): Int {
// on below line we are
// returning our list size.
return allNotes.size
}
// below method is use to update our list of notes.
fun updateList(newList: List) {
// on below line we are clearing
// our notes array list
allNotes.clear()
// on below line we are adding a
// new list to our all notes list.
allNotes.addAll(newList)
// on below line we are calling notify data
// change method to notify our adapter.
notifyDataSetChanged()
}
}
interface NoteClickDeleteInterface {
// creating a method for click
// action on delete image view.
fun onDeleteIconClick(note: Note)
}
interface NoteClickInterface {
// creating a method for click action
// on recycler view item for updating it.
fun onNoteClick(note: Note)
}
XML
Kotlin
package com.gtappdevelopers.noteapplication
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.util.*
class MainActivity : AppCompatActivity(), NoteClickInterface, NoteClickDeleteInterface {
// on below line we are creating a variable
// for our recycler view, exit text, button and viewmodel.
lateinit var viewModal: NoteViewModal
lateinit var notesRV: RecyclerView
lateinit var addFAB: FloatingActionButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// on below line we are initializing
// all our variables.
notesRV = findViewById(R.id.notesRV)
addFAB = findViewById(R.id.idFAB)
// on below line we are setting layout
// manager to our recycler view.
notesRV.layoutManager = LinearLayoutManager(this)
// on below line we are initializing our adapter class.
val noteRVAdapter = NoteRVAdapter(this, this, this)
// on below line we are setting
// adapter to our recycler view.
notesRV.adapter = noteRVAdapter
// on below line we are
// initializing our view modal.
viewModal = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
).get(NoteViewModal::class.java)
// on below line we are calling all notes method
// from our view modal class to observer the changes on list.
viewModal.allNotes.observe(this, Observer { list ->
list?.let {
// on below line we are updating our list.
noteRVAdapter.updateList(it)
}
})
addFAB.setOnClickListener {
// adding a click listener for fab button
// and opening a new intent to add a new note.
val intent = Intent(this@MainActivity, AddEditNoteActivity::class.java)
startActivity(intent)
this.finish()
}
}
override fun onNoteClick(note: Note) {
// opening a new intent and passing a data to it.
val intent = Intent(this@MainActivity, AddEditNoteActivity::class.java)
intent.putExtra("noteType", "Edit")
intent.putExtra("noteTitle", note.noteTitle)
intent.putExtra("noteDescription", note.noteDescription)
intent.putExtra("noteId", note.id)
startActivity(intent)
this.finish()
}
override fun onDeleteIconClick(note: Note) {
// in on note click method we are calling delete
// method from our view modal to delete our not.
viewModal.deleteNote(note)
// displaying a toast message
Toast.makeText(this, "${note.noteTitle} Deleted", Toast.LENGTH_LONG).show()
}
}
Kotlin
package com.gtappdevelopers.noteapplication
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import java.text.SimpleDateFormat
import java.util.*
class AddEditNoteActivity : AppCompatActivity() {
// on below line we are creating
// variables for our UI components.
lateinit var noteTitleEdt: EditText
lateinit var noteEdt: EditText
lateinit var saveBtn: Button
// on below line we are creating variable for
// viewmodal and and integer for our note id.
lateinit var viewModal: NoteViewModal
var noteID = -1;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_edit_note)
// on below line we are initialing our view modal.
viewModal = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
).get(NoteViewModal::class.java)
// on below line we are initializing all our variables.
noteTitleEdt = findViewById(R.id.idEdtNoteName)
noteEdt = findViewById(R.id.idEdtNoteDesc)
saveBtn = findViewById(R.id.idBtn)
// on below line we are getting data passed via an intent.
val noteType = intent.getStringExtra("noteType")
if (noteType.equals("Edit")) {
// on below line we are setting data to edit text.
val noteTitle = intent.getStringExtra("noteTitle")
val noteDescription = intent.getStringExtra("noteDescription")
noteID = intent.getIntExtra("noteId", -1)
saveBtn.setText("Update Note")
noteTitleEdt.setText(noteTitle)
noteEdt.setText(noteDescription)
} else {
saveBtn.setText("Save Note")
}
// on below line we are adding
// click listener to our save button.
saveBtn.setOnClickListener {
// on below line we are getting
// title and desc from edit text.
val noteTitle = noteTitleEdt.text.toString()
val noteDescription = noteEdt.text.toString()
// on below line we are checking the type
// and then saving or updating the data.
if (noteType.equals("Edit")) {
if (noteTitle.isNotEmpty() && noteDescription.isNotEmpty()) {
val sdf = SimpleDateFormat("dd MMM, yyyy - HH:mm")
val currentDateAndTime: String = sdf.format(Date())
val updatedNote = Note(noteTitle, noteDescription, currentDateAndTime)
updatedNote.id = noteID
viewModal.updateNote(updatedNote)
Toast.makeText(this, "Note Updated..", Toast.LENGTH_LONG).show()
}
} else {
if (noteTitle.isNotEmpty() && noteDescription.isNotEmpty()) {
val sdf = SimpleDateFormat("dd MMM, yyyy - HH:mm")
val currentDateAndTime: String = sdf.format(Date())
// if the string is not empty we are calling a
// add note method to add data to our room database.
viewModal.addNote(Note(noteTitle, noteDescription, currentDateAndTime))
Toast.makeText(this, "$noteTitle Added", Toast.LENGTH_LONG).show()
}
}
// opening the new activity on below line
startActivity(Intent(applicationContext, MainActivity::class.java))
this.finish()
}
}
}
第 5 步:创建实体
实体基本上是我们数据库的模态类或结构,我们将在其中创建数据库结构。在我们的应用程序中,我们将有一个简单的表格,它只有两列,例如 ID 和一个文本。 ID 将用于标识一个条目,而 text 是用于标识文本列的列。下面是我们数据库的表结构。下面是数据库的图像。
为了创建一个新的实体,我们必须创建一个新的 Kotlin 类。为了创建它,我们只需导航到您的应用程序的包名称。右键单击它然后新建并选择 Kotlin 文件/类,然后将类名称指定为Note并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
// on below line we are specifying our table name
@Entity(tableName = "notesTable")
// on below line we are specifying our column infor
// and inside that we are passing our column name
class Note (@ColumnInfo(name = "title")val noteTitle :String,@ColumnInfo(name = "description")val noteDescription :String,@ColumnInfo(name = "timestamp")val timeStamp :String) {
// on below line we are specifying our key and
// then auto generate as true and we are
// specifying its initial value as 0
@PrimaryKey(autoGenerate = true) var id = 0
}
第 6 步:创建 DAO 类
DAO 是一个数据访问对象,用于指定 SQL 查询,然后将它们与不同的方法调用相关联。 DAO 可以是抽象类或接口。在 DAO 类中,我们必须创建不同的方法,例如插入、删除数据和从数据库中读取数据。所以这个类基本上会与我们的数据库进行交互,以添加或删除我们数据库中的数据。
要创建 DAO 接口,我们只需导航到应用程序的包名称,右键单击它 > 新建 Kotlin 文件,我们必须选择接口并将其命名为NotesDao,然后我们必须创建此文件。创建此文件后,我们必须向其中添加以下代码。
科特林
package com.gtappdevelopers.noteapplication
import androidx.lifecycle.LiveData
import androidx.room.*
// annotation for dao class.
@Dao
interface NotesDao {
// below is the insert method for
// adding a new entry to our database.
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(note :Note)
// below is the delete method
// for deleting our note.
@Delete
suspend fun delete(note: Note)
// below is the method to read all the notes
// from our database we have specified the query for it.
// inside the query we are arranging it in ascending
// order on below line and we are specifying
// the table name from which
// we have to get the data.
@Query("Select * from notesTable order by id ASC")
fun getAllNotes(): LiveData>
// below method is use to update the note.
@Update
suspend fun update(note: Note)
}
第 7 步:为我们的应用程序添加房间数据库
什么是房间数据库?
- Room 基本上是 SQLite 数据库之上的一个数据库层。
- Room 负责处理您过去使用 SQLite Open Helper 处理的日常任务。
- Room 使用 DAO 向其数据库发出查询。
- Room 提供 SQLite 语句的编译时检查。
现在要将数据存储在用户的设备中,我们必须创建一个 Room 数据库来存储数据。因此,为了创建数据库,我们必须创建一个抽象类来创建我们的数据库。在这里,我们将简单地使用 Room 构建我们的数据库,我们将指定我们的数据库名称。要创建一个新的抽象类,我们必须转到应用程序的包名称 > 右键单击它 > 新建 > Kotlin 文件,我们将其命名为NoteDatabase 。创建此类后,我们必须向其中添加以下代码。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = arrayOf(Note::class), version = 1, exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {
abstract fun getNotesDao(): NotesDao
companion object {
// Singleton prevents multiple
// instances of database opening at the
// same time.
@Volatile
private var INSTANCE: NoteDatabase? = null
fun getDatabase(context: Context): NoteDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java,
"note_database"
).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
步骤 8:创建存储库类
Repository 类基本上抽象了对多个数据源的访问,例如从 API 获取数据或从 Room 数据库获取数据。存储库类将为我们提供一个干净的 API,用于应用程序其余部分的数据访问。存储库将包含一个逻辑,该逻辑将决定我们是必须从网络中获取数据还是必须从数据库中获取数据。
创建存储库类。要创建存储库,我们必须通过简单地右键单击应用程序的包名称 > 右键单击它 > 新建 > Kotlin 类并将类名指定为NoteRepository并将以下代码添加到其中来创建一个新的 Kotlin 文件。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import androidx.annotation.WorkerThread
import androidx.lifecycle.LiveData
import java.util.concurrent.Flow
class NoteRepository(private val notesDao: NotesDao) {
// on below line we are creating a variable for our list
// and we are getting all the notes from our DAO class.
val allNotes: LiveData> = notesDao.getAllNotes()
// on below line we are creating an insert method
// for adding the note to our database.
suspend fun insert(note: Note) {
notesDao.insert(note)
}
// on below line we are creating a delete method
// for deleting our note from database.
suspend fun delete(note: Note){
notesDao.delete(note)
}
// on below line we are creating a update method for
// updating our note from database.
suspend fun update(note: Note){
notesDao.update(note)
}
}
第 9 步:创建一个 ViewModel
ViewModel 基本上用于向我们的用户界面提供数据。它充当 Repository 和 UI 之间的通信层。我们可以使用 View Modal 在我们的片段之间共享数据。要创建视图模式,我们必须简单地创建一个新的 Kotlin 类,然后我们必须将其命名为NoteViewModal并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class NoteViewModal (application: Application) :AndroidViewModel(application) {
// on below line we are creating a variable
// for our all notes list and repository
val allNotes : LiveData>
val repository : NoteRepository
// on below line we are initializing
// our dao, repository and all notes
init {
val dao = NoteDatabase.getDatabase(application).getNotesDao()
repository = NoteRepository(dao)
allNotes = repository.allNotes
}
// on below line we are creating a new method for deleting a note. In this we are
// calling a delete method from our repository to delete our note.
fun deleteNote (note: Note) = viewModelScope.launch(Dispatchers.IO) {
repository.delete(note)
}
// on below line we are creating a new method for updating a note. In this we are
// calling a update method from our repository to update our note.
fun updateNote(note: Note) = viewModelScope.launch(Dispatchers.IO) {
repository.update(note)
}
// on below line we are creating a new method for adding a new note to our database
// we are calling a method from our repository to add a new note.
fun addNote(note: Note) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(note)
}
}
步骤 10:使用 activity_main.xml 文件
现在我们将处理我们应用程序的 UI 部分。现在我们必须导航到app > res > activity_main.xml并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
XML
第 11 步:为我们的 RecyclerView 项创建一个新的布局文件
导航到应用程序 > res > 布局 > 右键单击它 > 新建 > 布局资源文件并将您的文件命名为note_rv_item并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
XML
第 12 步:创建 Adapter 类
现在我们将创建一个适配器类,用于将数据设置到我们的回收器视图的每个项目。要创建 Adapter 类,我们必须导航到应用程序 > Java > 您应用程序的包名称 > 右键单击它 > 新建 > Kotlin/Class并将其命名为NoteRVAdapter并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class NoteRVAdapter(
val context: Context,
val noteClickDeleteInterface: NoteClickDeleteInterface,
val noteClickInterface: NoteClickInterface
) :
RecyclerView.Adapter() {
// on below line we are creating a
// variable for our all notes list.
private val allNotes = ArrayList()
// on below line we are creating a view holder class.
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// on below line we are creating an initializing all our
// variables which we have added in layout file.
val noteTV = itemView.findViewById(R.id.idTVNote)
val dateTV = itemView.findViewById(R.id.idTVDate)
val deleteIV = itemView.findViewById(R.id.idIVDelete)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// inflating our layout file for each item of recycler view.
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.note_rv_item,
parent, false
)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// on below line we are setting data to item of recycler view.
holder.noteTV.setText(allNotes.get(position).noteTitle)
holder.dateTV.setText("Last Updated : " + allNotes.get(position).timeStamp)
// on below line we are adding click listener to our delete image view icon.
holder.deleteIV.setOnClickListener {
// on below line we are calling a note click
// interface and we are passing a position to it.
noteClickDeleteInterface.onDeleteIconClick(allNotes.get(position))
}
// on below line we are adding click listener
// to our recycler view item.
holder.itemView.setOnClickListener {
// on below line we are calling a note click interface
// and we are passing a position to it.
noteClickInterface.onNoteClick(allNotes.get(position))
}
}
override fun getItemCount(): Int {
// on below line we are
// returning our list size.
return allNotes.size
}
// below method is use to update our list of notes.
fun updateList(newList: List) {
// on below line we are clearing
// our notes array list
allNotes.clear()
// on below line we are adding a
// new list to our all notes list.
allNotes.addAll(newList)
// on below line we are calling notify data
// change method to notify our adapter.
notifyDataSetChanged()
}
}
interface NoteClickDeleteInterface {
// creating a method for click
// action on delete image view.
fun onDeleteIconClick(note: Note)
}
interface NoteClickInterface {
// creating a method for click action
// on recycler view item for updating it.
fun onNoteClick(note: Note)
}
第 13 步:创建一个用于添加和编辑笔记的新活动
导航到应用程序 > Java > 您应用程序的包名称 > 右键单击它 > 新建 > 选择空活动并将其命名为AddEditNoteActivity并创建一个新活动。之后导航到app > res > layout > activity_add_edit_note.xml文件并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
XML
第 14 步:使用 MainActivity.kt 文件
导航到应用程序 > Java > 您应用程序的包名称 > MainActivity.kt文件并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.util.*
class MainActivity : AppCompatActivity(), NoteClickInterface, NoteClickDeleteInterface {
// on below line we are creating a variable
// for our recycler view, exit text, button and viewmodel.
lateinit var viewModal: NoteViewModal
lateinit var notesRV: RecyclerView
lateinit var addFAB: FloatingActionButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// on below line we are initializing
// all our variables.
notesRV = findViewById(R.id.notesRV)
addFAB = findViewById(R.id.idFAB)
// on below line we are setting layout
// manager to our recycler view.
notesRV.layoutManager = LinearLayoutManager(this)
// on below line we are initializing our adapter class.
val noteRVAdapter = NoteRVAdapter(this, this, this)
// on below line we are setting
// adapter to our recycler view.
notesRV.adapter = noteRVAdapter
// on below line we are
// initializing our view modal.
viewModal = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
).get(NoteViewModal::class.java)
// on below line we are calling all notes method
// from our view modal class to observer the changes on list.
viewModal.allNotes.observe(this, Observer { list ->
list?.let {
// on below line we are updating our list.
noteRVAdapter.updateList(it)
}
})
addFAB.setOnClickListener {
// adding a click listener for fab button
// and opening a new intent to add a new note.
val intent = Intent(this@MainActivity, AddEditNoteActivity::class.java)
startActivity(intent)
this.finish()
}
}
override fun onNoteClick(note: Note) {
// opening a new intent and passing a data to it.
val intent = Intent(this@MainActivity, AddEditNoteActivity::class.java)
intent.putExtra("noteType", "Edit")
intent.putExtra("noteTitle", note.noteTitle)
intent.putExtra("noteDescription", note.noteDescription)
intent.putExtra("noteId", note.id)
startActivity(intent)
this.finish()
}
override fun onDeleteIconClick(note: Note) {
// in on note click method we are calling delete
// method from our view modal to delete our not.
viewModal.deleteNote(note)
// displaying a toast message
Toast.makeText(this, "${note.noteTitle} Deleted", Toast.LENGTH_LONG).show()
}
}
第 15 步:使用 AddEditNoteActivity。Java
导航到应用程序 > Java > 您应用程序的包名称 > AddEditNoteActivity。 Java并将以下代码添加到其中。代码中添加了注释,以便更详细地了解。
科特林
package com.gtappdevelopers.noteapplication
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import java.text.SimpleDateFormat
import java.util.*
class AddEditNoteActivity : AppCompatActivity() {
// on below line we are creating
// variables for our UI components.
lateinit var noteTitleEdt: EditText
lateinit var noteEdt: EditText
lateinit var saveBtn: Button
// on below line we are creating variable for
// viewmodal and and integer for our note id.
lateinit var viewModal: NoteViewModal
var noteID = -1;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_edit_note)
// on below line we are initialing our view modal.
viewModal = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
).get(NoteViewModal::class.java)
// on below line we are initializing all our variables.
noteTitleEdt = findViewById(R.id.idEdtNoteName)
noteEdt = findViewById(R.id.idEdtNoteDesc)
saveBtn = findViewById(R.id.idBtn)
// on below line we are getting data passed via an intent.
val noteType = intent.getStringExtra("noteType")
if (noteType.equals("Edit")) {
// on below line we are setting data to edit text.
val noteTitle = intent.getStringExtra("noteTitle")
val noteDescription = intent.getStringExtra("noteDescription")
noteID = intent.getIntExtra("noteId", -1)
saveBtn.setText("Update Note")
noteTitleEdt.setText(noteTitle)
noteEdt.setText(noteDescription)
} else {
saveBtn.setText("Save Note")
}
// on below line we are adding
// click listener to our save button.
saveBtn.setOnClickListener {
// on below line we are getting
// title and desc from edit text.
val noteTitle = noteTitleEdt.text.toString()
val noteDescription = noteEdt.text.toString()
// on below line we are checking the type
// and then saving or updating the data.
if (noteType.equals("Edit")) {
if (noteTitle.isNotEmpty() && noteDescription.isNotEmpty()) {
val sdf = SimpleDateFormat("dd MMM, yyyy - HH:mm")
val currentDateAndTime: String = sdf.format(Date())
val updatedNote = Note(noteTitle, noteDescription, currentDateAndTime)
updatedNote.id = noteID
viewModal.updateNote(updatedNote)
Toast.makeText(this, "Note Updated..", Toast.LENGTH_LONG).show()
}
} else {
if (noteTitle.isNotEmpty() && noteDescription.isNotEmpty()) {
val sdf = SimpleDateFormat("dd MMM, yyyy - HH:mm")
val currentDateAndTime: String = sdf.format(Date())
// if the string is not empty we are calling a
// add note method to add data to our room database.
viewModal.addNote(Note(noteTitle, noteDescription, currentDateAndTime))
Toast.makeText(this, "$noteTitle Added", Toast.LENGTH_LONG).show()
}
}
// opening the new activity on below line
startActivity(Intent(applicationContext, MainActivity::class.java))
this.finish()
}
}
}
现在运行您的应用程序以查看应用程序的输出。
输出:
从这里获取完整的项目。