示例:通过本地数据库,实现AutoCompleteTextView 的提示人名的数据填充
参考 : AndroidRoomWithAView
1.介绍
使用Google的推荐架构
架构
2.配置Gradle 文件
1.在build.gradle(Module:app)添加kapt插件
apply plugin: 'kotlin-kapt'
2.在该packagingOptions块内添加该块,android以将原子函数模块从包中排除,并防止出现警告
android {
// other configuration (buildTypes, defaultConfig, etc.)
packagingOptions {
exclude 'META-INF/atomicfu.kotlin_module'
}
}
3.在代码dependencies块的末尾添加以下代码。
// Room components
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
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-extensions:$rootProject.archLifecycleVersion"
kapt "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"
androidTestImplementation "androidx.arch.core:core-testing:$rootProject.androidxArchVersion"
// ViewModel Kotlin support
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.archLifecycleVersion"
// Coroutines
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
// UI
implementation "com.google.android.material:material:$rootProject.materialVersion"
// Testing
androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion"
4.在build.gradle(Project:RoomWordsSample)文件中,将版本号添加到文件的末尾,如下面的代码所示
ext {
roomVersion = '2.2.1'
archLifecycleVersion = '2.2.0-rc02'
androidxArchVersion = '2.1.0'
coreTestingVersion = "2.1.0"
coroutines = '1.3.2'
materialVersion = "1.0.0"
}
3.创建实体类
创建一个新的Kotlin类文件:Name数据类。
此类创建表SQLite表。类中的每个公共属性都代表表中的一列。
Room最终将使用这些属性来创建表并从数据库中的行实例化对象。
为了使Name类对Room数据库有意义,您需要对其进行注释。
注释标识此类的每个部分如何与数据库中的条目相关。Room使用此信息来生成代码。
@Entity(tableName = "name_table")
class Name (@PrimaryKey @ColumnInfo(name = "name") val name:String)
让我们看看这些注释的作用:
@Entity(tableName = "word_table")
每个@Entity类代表一个SQLite表。注释您的类声明以表明它是一个实体。如果希望表名与类名不同,则可以指定表名。这将表命名为“ word_table”。
@PrimaryKey
每个实体都需要一个主键。为了简单起见,每个名字都充当其自己的主键。
@ColumnInfo(name = "name")
如果您希望表中的列名称与成员变量的名称不同,则指定该名称。这将列命名为“name”。
数据库中存储的每个属性都必须具有公共可见性,这是Kotlin的默认设置。
4.创建DAO(数据访问对象)
在DAO(data access object数据访问对象)中,指定SQL查询并将其与方法调用关联。编译器检查SQL,并从便捷注释中生成常见查询(例如)的查询@Insert。Room使用DAO为您的代码创建一个干净的API。
DAO必须是接口或抽象类。
默认情况下,所有查询必须在单独的线程上执行。
Room具有协程支持,允许您的查询使用suspend修饰符进行注释,然后从协程或另一个suspension函数调用。
让我们编写一个DAO,它提供以下查询:
-
1.按字母顺序排列所有名字
-
2.插入一个名字
-
3.删除所有名字
@Dao
interface NameDao {
@Query("SELECT * from name_table ORDER BY name ASC")
fun getAllNames(): List<Name>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(name: Name)
@Query("DELETE FROM name_table")
suspend fun deleteAll()
}
让我们看看这些注释的作用:
- 1.NameDao是一个接口; DAO必须是接口或抽象类。
- 2.@Dao注解将其标识为Room的DAO类。
- 3.suspend fun insert(name: Name):声明一个suspend函数以插入一个名字。
- 4.@Insert注解是一种特殊的DAO方法注解,您无需提供任何SQL。(还有@Delete和@Update注解用于删除和更新行,但您未在此应用程序中使用它们。)
- 5.onConflict = OnConflictStrategy.IGNORE:如果所选的冲突策略与列表中的一个名字完全相同,则忽略该单词。要了解有关可用冲突策略的更多信息,请查阅文档。
- 6.suspend fun deleteAll():声明一个suspend函数以删除所有名字。
- 7.没有用于删除多个实体的便捷注释,因此使用通用@Query进行注释。
- 8.@Query("SELECT * from name_table ORDER BY name ASC"):@Query要求您提供SQL查询作为注释的字符串参数,以允许进行复杂的读取查询和其他操作。
- 9.fun getAllNames(): List<Name> 一种获取所有名字并使其返回名字列表的方法。
5.使用LiveData类
数据更改时,通常需要采取一些措施,例如在UI中显示更新的数据。这意味着您必须观察数据,以便在数据更改时可以做出反应。
根据数据的存储方式,这可能很棘手。观察应用程序多个组件之间的数据更改可以在组件之间创建明确的,严格的依赖路径。除其他事项外,这使测试和调试变得困难。
LiveData,用于数据观察的生命周期库类可解决此问题。LiveData在方法说明中使用类型的返回值,并且Room将生成所有必需的代码以在更新LiveData数据库时进行更新。
在中NameDao,更改getAllNames()方法,以便返回的List<Word>内容包含LiveData。
@Query("SELECT * from name_table ORDER BY name ASC")
fun getAllNames(): LiveData<List<Name>>
6.添加一个 Room database
创建数据库
@Database(entities = [Name::class], version = 1, exportSchema = false)
public abstract class NameRoomDatabase : RoomDatabase() {
abstract fun nameDao(): NameDao
companion object {
@Volatile
private var INSTANCE: NameRoomDatabase? = null
fun getDatabase(context: Context): NameRoomDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NameRoomDatabase::class.java,
"name_database"
).build()
INSTANCE = instance
instance
}
}
}
}
Room的数据库类必须为abstract并且扩展RoomDatabase
您使用注释该类为Room数据库,@Database并使用注释参数声明该数据库中的实体并设置版本号。
每个实体对应一个将在数据库中创建的表。数据库迁移不在此代码实验室的范围内,因此exportSchema在此处设置为false以避免生成警告。
在实际的应用程序中,您应考虑为Room设置目录以用于导出架构,以便可以将当前架构签入版本控制系统。
您通过为每个@Dao创建一个抽象的“ getter”方法来使数据库提供其DAO。
我们定义了singleton,NameRoomDatabase,以防止同时打开多个数据库实例。
getDatabase返回单例。它将在首次访问数据库时使用Room的数据库构建器RoomDatabase在类的应用程序上下文中创建一个对象NameRoomDatabase并将其命名,从而创建数据库"name_database"。
7.创建存储库
什么是存储库?
存储库类抽象了对多个数据源的访问。该存储库不是体系结构组件库的一部分,
但是建议用于代码分离和体系结构的最佳实践。Repository类提供了一个干净的API,用于对应用程序其余部分的数据访问。
存储库管理查询,并允许您使用多个后端。在最常见的示例中,存储库实现了用于确定是从网络中获取数据还是使用本地数据库中缓存的结果的逻辑。
创建一个名为的Kotlin类文件NameRepository,并将以下代码粘贴到其中:
class NameRepository(private val nameDao: NameDao) {
val allNames: LiveData<List<Name>> = nameDao.getAllNames()
suspend fun insert(name: Name) {
nameDao.insert(name)
}
}
主要要点
- 1.DAO被传递到存储库构造函数,而不是整个数据库。这是因为它只需要访问DAO,因为DAO包含数据库的所有读/写方法。无需将整个数据库公开到存储库。
- 2.这个列表里的名字是公有属性。通过从Room获取名字的LiveData列表进行初始化;之所以可以这样做,是因为我们通过定义getAllNames方法以在“ LiveData类”步骤中返回LiveData。 Room在单独的线程上执行所有查询。 然后,观察到的LiveData将在数据更改时通知主线程上的观察者。
- 3.suspend修饰符告诉编译器需要从协程或另一个suspend函数中调用它。
8.创建ViewModel
该ViewModel的作用是提供UI的数据和数据的修改。
一个ViewModel充当存储库和UI之间的通信。
您还可以使用ViewModel在Fragment之间共享数据。ViewModel是生命周期库的一部分
class NameViewModel(application: Application) : AndroidViewModel(application) {
private val repository: NameRepository
val allNames: LiveData<List<Name>>
init {
val namesDao = NameRoomDatabase.getDatabase(application).nameDao()
repository = NameRepository(namesDao)
allNames = repository.allNames
}
fun insert(name: Name) {
viewModelScope.launch {
repository.insert(name)
}
}
}
9.创建界面
<?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=".ui.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<AutoCompleteTextView
android:id="@+id/mMainActv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:hint="Input Name" />
<Button
android:id="@+id/mMainBtn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Save" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
10.实现功能
class MainActivity : AppCompatActivity() {
private lateinit var namesViewModel: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
namesViewModel = ViewModelProvider(this).get(NameViewModel::class.java)
initAutoView()
initListener()
}
private fun initAutoView() {
val adapter = ArrayAdapter(
this,
android.R.layout.simple_dropdown_item_1line, mutableListOf<String>()
)
mMainActv.threshold = 1
mMainActv.setAdapter(adapter)
mMainActv.dropDownHeight = 800
val noticeList = Transformations.map(namesViewModel.allNames) { it ->
val addressStrList = mutableListOf<String>()
it.forEach { name ->
addressStrList.add(name.name)
}
return@map addressStrList
}
noticeList.observeForever {
adapter.clear()
adapter.addAll(it)
adapter.notifyDataSetChanged()
}
}
private fun initListener() {
mMainBtn.setOnClickListener {
val text = mMainActv.text.toString()
if (text.isNotEmpty()) {
namesViewModel.insert(Name(text))
}
}
}
}
到此,运行APP即刻体验
网友评论