当前位置: 首页 > news >正文

电影网站加盟可以做么/如何进行网站的宣传和推广

电影网站加盟可以做么,如何进行网站的宣传和推广,做网站收入,蛙蛙写作网站Android Jetpack 体验-官方codelab 1. 实现功能 使用 Jetpack 架构组件 Room、ViewModel 和 LiveData 设计应用;从sqlite获取、保存、删除数据;sqlite数据预填充功能;使用 RecyclerView 展示数据列表; 2. 使用架构组件 架构组…

Android Jetpack 体验-官方codelab

1. 实现功能

  1. 使用 Jetpack 架构组件 Room、ViewModel 和 LiveData 设计应用;
  2. 从sqlite获取、保存、删除数据;
  3. sqlite数据预填充功能;
  4. 使用 RecyclerView 展示数据列表;

2. 使用架构组件

架构组件及其协作方式:

image.png

  • LiveData 是一种可观察的数据存储器,每当数据发生变化时,它都会通知观察者。 LiveData会根据负责监听变化的生命周期自动停止或恢复观察。
  • ViewModel 充当存储库和UI之间的通信中心,以及应用程序中其他部分的UI相关的数据的容器。activity和fragment负责将数据绘制到屏幕上,ViewModel负责保存并处理界面所需的所有数据。
  • Repository 管理数据源,可以是网络或本地的。
  • RoomDatabase 简化数据库工作,它使用 DAO 向 SQLite 数据库发起请求。
  • DAO 数据访问对象,一般是接口或抽象类
  • SQLite 设备存储空间
  • 实体:使用 Room 用于描述数据库表的带注解的类。

RoomWordSample 架构概览

image.png

3. 创建应用,配置依赖

环境:
Android Studio Flamingo | 2022.2.1 Patch 1
Android Gradle Plugin Version: 8.0.1
Gradle Version: 8.0
JDK 17
compileSdk 33
minSdk 24
targetSdk 33

统一项目依赖版本实现

  1. 在 build.gradle(root)下定义版本号,注意 buildscript 一定要在最上面
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {ext{appCompatVersion = '1.6.1'activityVersion = '1.6.0'roomVersion = '2.5.0'lifecycleVersion = '2.5.1'coroutines = '1.6.4'constraintLayoutVersion = '2.1.4'materialVersion = '1.9.0'// testingjunitVersion = '4.13.2'androidxJunitVersion = '1.1.5'espressoVersion = '3.5.1'}
}plugins {id 'com.android.application' version '8.0.1' apply falseid 'com.android.library' version '8.0.1' apply falseid 'org.jetbrains.kotlin.android' version '1.8.20' apply false
}
  1. 然后在 build.gradle(app)下添加依赖
dependencies {implementation "androidx.appcompat:appcompat:${rootProject.appCompatVersion}"// activity-ktx 提供了 Kotlin 对 Android Activity API 的扩展。// 这些扩展函数和属性使得在 Kotlin 中使用 Activity API 更加简洁和方便。implementation "androidx.activity:activity-ktx:${rootProject.activityVersion}"// Room componentsimplementation "androidx.room:room-ktx:${rootProject.roomVersion}"implementation "androidx.room:room-runtime:${rootProject.roomVersion}"annotationProcessor "androidx.room:room-compiler:${rootProject.roomVersion}"kapt "androidx.room:room-compiler:${rootProject.roomVersion}"testImplementation  "androidx.room:room-testing:${rootProject.roomVersion}"// Lifecycle componentsimplementation "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// core-ktx 提供了 Kotlin 对 Android 核心库的扩展。// 这些扩展函数和属性使得在 Kotlin 中使用 Android 核心库更加简洁和方便。implementation "androidx.core:core-ktx:1.8.0"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${rootProject.coroutines}"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${rootProject.coroutines}"// uiimplementation "com.google.android.material:material:${rootProject.materialVersion}"implementation "androidx.constraintlayout:constraintlayout:${rootProject.constraintLayoutVersion}"// TestingtestImplementation "junit:junit:${rootProject.junitVersion}"androidTestImplementation "androidx.test.ext:junit:${rootProject.androidxJunitVersion}"androidTestImplementation "androidx.test.espresso:espresso-core:${rootProject.espressoVersion}"
}
  1. build.gradle(app) 添加 kotlin 注解处理器插件
plugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'id 'kotlin-kapt'
}
  1. java 版本相关设置
    在build.gradle(app) 配置,解决 Execution failed for task ':app:kaptGenerateStubsDebugKotlin' 错误.
compileOptions {sourceCompatibility JavaVersion.VERSION_17targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {jvmTarget = '17'
}

AndroidX 版本查询:
https://developer.android.google.cn/jetpack/androidx/versions?hl=zh-cn

4. 创建实体

创建 Word 数据类,描述在数据库中存储单词的表:

package com.alex.roomwordssampleimport androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey/*** @Author      : alex* @Date        : on 2024/1/4 21:05.* @Description :Word数据类,用于定义数据库中的表*/
@Entity(tableName = "word_table")
data class Word(@PrimaryKey @ColumnInfo(name="word") val word: String)
  • 该类描述 word_table 表只有一个列:word;
  • @Entity(tableName = "word_table" ) : 表名
  • @PrimaryKey: 主键
  • @ColumnInfo(name = "word" ) :列名

5. 创建数据访问对象 DAO

DAO 定义 SQL 查询并将其与方法调用相关联。DAO 必须是一个接口或抽象类,默认情况下,所有查询必须在单独的线程上执行。
Room 支持 kotlin 协程,可以使用 suspend 修饰符对查询进行注解,然后从协程获取其他挂起函数对其进行调用。

WordDao 接口定义:

package com.alex.roomwordssampleimport androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow/*** @Author      : alex* @Date        : on 2024/1/4 21:07.* @Description :word 数据访问接口*/
@Dao
interface WordDao {// 按照字母顺序获取所有单词// 为了观察数据变化情况,返回值使用了Flow// 当数据库更新时,它会发出一个新的流,然后,您可以使用该流更新UI。// 当Room查询返回LiveData或Flow时,查询是在单独的线程上异步执行的。@Query("SELECT * from word_table ORDER BY word ASC")fun getAlphabetizedWords(): Flow<List<Word>>// 插入单词// 将忽略与列表中的现有字词完全相同的新字词。@Insert(onConflict = OnConflictStrategy.IGNORE)suspend fun insert(word:Word)// 删除所有单词@Query("DELETE FROM word_table")suspend fun deleteAll()
}

解释:

  • @Dao 注解该接口表示为 Room 的 DAO类。
  • 删除使用的 @Query,可以定义复杂SQL语句。
  • 使用 kotlin-coroutines 中的 Flow,定义返回数据类型,是为了观察数据变化情况,当数据发生变化时,Room 会更新 Flow

5. 添加 Room 数据库

Room 数据库类必须是抽象的,必须扩展 RoomDatabase,整个应用通常只需要一个 Room 数据库实例。
WordRoomDatabase 定义:

package com.alex.roomwordssampleimport android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch/*** @Author      : alex* @Date        : on 2024/1/4 21:10.* @Description : Room数据抽象类*/
@Database(entities = [Word::class], version = 1, exportSchema = false)
abstract class WordRoomDatabase: RoomDatabase(){abstract fun wordDao(): WordDaocompanion object{//单例,防止出现同时打开多个数据库实例的情况@Volatileprivate var INSTANCE: WordRoomDatabase? = nullfun getDatabase(context: Context,scope: CoroutineScope): WordRoomDatabase{return INSTANCE ?: synchronized(this){val instance = Room.databaseBuilder(context.applicationContext,WordRoomDatabase::class.java,"word_database").fallbackToDestructiveMigration().addCallback(WordDatabaseCallback(scope)).build()INSTANCE = instanceinstance}}// 为了在数据库创建时填充它,我们需要实现 RoomDatabase.Callback(),并覆盖 onCreate()。private class WordDatabaseCallback(private val scope: CoroutineScope): RoomDatabase.Callback(){override fun onCreate(db: SupportSQLiteDatabase) {super.onCreate(db)INSTANCE?.let { database ->scope.launch(Dispatchers.IO) {populateDatabase(database.wordDao())}}}}suspend fun populateDatabase(wordDao: WordDao){wordDao.deleteAll()var word=Word("Hello")wordDao.insert(word)word=Word("World")wordDao.insert(word)}}
}

代码解释:

  • Room 数据库类必须是抽象的,必须扩展 RoomDatabase
  • @Database 将该类注解为 Room 数据库,并使用注解参数声明数据库中的实体以及设置版本号。
  • 数据库通过每个 @Dao 的抽象getter方法公开 DAO
  • 定义单例 WordRoomDatabase,防止同时打开数据库的多个实例。
  • getDatabase 会返回单例,首次使用时会创建数据库,并删除旧数据,填充示例数据。

6. 创建存储库

Repository 会将多个数据源的访问权限抽象化,提供一个整洁的 API,用于获取对应用其余部分的数据访问权限。
WordRepository 定义:

package com.alex.roomwordssampleimport androidx.annotation.WorkerThread
import kotlinx.coroutines.flow.Flow/*** @Author      : alex* @Date        : on 2024/1/4 21:13.* @Description : word 存储库,可以用于管理多个数据源*/
class WordRepository(private val wordDao:WordDao) {// Room在单独的线程上执行所有查询// 观察数据变化情况,返回值使用了Flowval allWords:Flow<List<Word>> = wordDao.getAlphabetizedWords()// 在后台线程中执行操作@Suppress("RedundantSuspendModifier")@WorkerThreadsuspend fun insert(word: Word){wordDao.insert(word)}
}
  • DAO 会被传递到存储库构造函数中,而非整个数据库中。DAO 包含数据库的所有读取/写入方法,因此它只需要访问 DAO,无需向存储库公开整个数据库。
  • allWords 表具有公开属性。它通过从 Room 获取 Flow 字词列表来进行初始化;您之所以能够实现该操作,是因为您在“观察数据库变化”步骤中定义 getAlphabetizedWords 方法以返回 Flow 的方式。Room 将在单独的线程上执行所有查询。
  • Room 在主线程之外执行挂起查询。

7. 创建 ViewModel

ViewModel: 向界面提供数据,不受配置变化的影响。ViewModel 是 Lifecycle 库的一部分。

LiveData与ViewModel的关系

LiveData 是一种可观察的数据存储器,每当数据发生变化时,您都会收到通知。与 Flow 不同,LiveData 具有生命周期感知能力,即遵循其他应用组件(如 activity 或 fragment)的生命周期。LiveData 会根据负责监听变化的组件的生命周期自动停止或恢复观察。因此,LiveData 适用于界面使用或显示的可变数据。

ViewModel 会将存储库中的数据从 Flow 转换为 LiveData,并将字词列表作为 LiveData 传递给界面。这样可以确保每次数据库中的数据发生变化时,界面都会自动更新。

viewModelScope

AndroidX lifecycle-viewmodel-ktx 库将 viewModelScope 添加为 ViewModel 类的扩展函数。
WordViewModel 定义:

package com.alex.roomwordssampleimport androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch/*** @Author      : alex* @Date        : on 2024/1/4 21:15.* @Description : ViewModel 充当存储库和UI之间的通信中心,以及应用程序中其他部分的UI相关的数据的容器。*               ViewModel通过使用LiveData或Flow来保留数据,这样它就可以在配置更改后继续存在。*               ViewModel可以通过调用ViewModelProvider.Factory来创建。*               activity和fragment负责将数据绘制到屏幕上,ViewModel负责保存并处理界面所需的所有数据。*/
class WordViewModel(private val repository:WordRepository) :ViewModel(){// LiveData 是一种可观察的数据存储器,每当数据发生变化时,它都会通知观察者。// LiveData会根据负责监听变化的生命周期自动停止或恢复观察。// 使用 LiveData 并缓存 allWords 返回的内容有几个好处:// - 我们可以在数据上设置一个观察者(而不是轮询变化),并且只有当数据实际发生变化时才更新用户界面。// - 通过 ViewModel,仓库与用户界面完全分离。val allWords: LiveData<List<Word>> = repository.allWords.asLiveData()// 启动一个新的协程以非阻塞方式插入数据。fun insert(word: Word) = viewModelScope.launch {repository.insert(word)}
}/*** WordViewModelFactory 类的作用是创建 WordViewModel 实例,* 并确保 WordViewModel 可以接收到 WordRepository 实例,以便它可以与数据源进行交互。*/
class WordViewModelFactory(private val repository: WordRepository): ViewModelProvider.Factory{override fun <T : ViewModel> create(modelClass: Class<T>): T {// 检查modelClass是否是WordViewModel的子类if (modelClass.isAssignableFrom(WordViewModel::class.java)){@Suppress("UNCHECKED_CAST")return WordViewModel(repository) as T}throw IllegalArgumentException("Unknown ViewModel class")}
}

解析:

  • 创建了一个名为 WordViewModel 的类,该类可获取 WordRepository 作为参数并扩展 ViewModel。存储库是 ViewModel 需要的唯一依赖项。如果需要其他类,系统也会在构造函数中传递相应的类。
  • 添加了一个公开的 LiveData 成员变量以缓存字词列表。
  • 使用存储库中的 allWords Flow 初始化了 LiveData。然后,您通过调用 asLiveData(). 将该 Flow 转换成了 LiveData。
  • 创建了一个可调用存储库的 insert() 方法的封装容器 insert() 方法。这样一来,便可从界面封装 insert() 的实现。我们将启动新协程并调用存储库的挂起函数 insert。如上所述,ViewModel 的协程作用域基于它的名为 viewModelScope 的生命周期(您将在这里使用)。
  • 创建了 ViewModel,并实现了 ViewModelProvider.Factory,后者可获取创建 WordViewModel 所需的依赖项作为参数:WordRepository

使用 viewModelsViewModelProvider.Factory 后,框架将负责 ViewModel 的生命周期。它不受配置变化的影响,即使重建 activity,您始终能得到 WordViewModel 类的正确实例。

使用asLiveData,需要添加下面的依赖:

> 添加依赖:implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
> 引入:import androidx.lifecycle.asLiveData

8. 列表页面布局实现(MainActivity)

列表页面使用了 RecyclerView 组件,需要先定义列表项布局 recyclerview_item.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/textView"style="@style/word_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/holo_orange_light" /></LinearLayout>

在activity_main.xml 中引入 RecyclerView,并添加一个浮动按钮

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutxmlns: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"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerview"android:layout_width="0dp"android:layout_height="0dp"tools:listitem="@layout/recyclerview_item"android:padding="@dimen/big_padding"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"/><com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/fab"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="16dp"android:contentDescription="@string/add_word"android:src="@drawable/id_add_black_24db"/></androidx.constraintlayout.widget.ConstraintLayout>

浮动按钮图标的制作,使用了 Asset Studio 工具(File->New->Vector Asset)

9. RecyclerView

MainActivity 中 使用 RecyclerView 显示数据。
添加步骤:

  • 定义 WordListAdapter 类
  • 定义填充列表项行为
  • 在 MainActivity 中添加 RecyclerView

WordListAdapter 类定义:

package com.alex.roomwordssampleimport android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView/*** @Author      : alex* @Date        : on 2024/1/4 21:40.* @Description :RecyclerView的适配器*/
class WordListAdapter:ListAdapter<Word, WordListAdapter.WordViewHolder>(WordsComparator()) {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder {return WordViewHolder.create(parent)}override fun onBindViewHolder(holder: WordViewHolder, position: Int) {val current=getItem(position)holder.bind(current.word)}class WordViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {private val wordItemView:TextView= itemView.findViewById(R.id.textView)fun bind(text:String?){wordItemView.text=text}companion object{fun create(parent:ViewGroup):WordViewHolder{val view:View=LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item,parent,false)return WordViewHolder(view)}}}class WordsComparator : DiffUtil.ItemCallback<Word>() {override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {return oldItem === newItem}override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean {return oldItem.word == newItem.word}}
}

RecyclerView 是 Android 中用于显示大量数据集的一个组件,它优化了这些数据的显示,只创建和渲染屏幕上可见的部分,从而提高了性能。

RecyclerView 通过一个适配器来管理数据的显示,适配器负责将数据与每个列表项视图(item view)进行绑定。

RecyclerView.ViewHolder 是一个静态类,用于存储对列表项视图中的界面元素的引用。

在这个例子中,WordViewHolder 是 RecyclerView.ViewHolder 的一个子类,它存储了对 TextView 的引用,并提供了一个 bind 方法来更新 TextView 的内容。

class WordViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {private val wordItemView:TextView= itemView.findViewById(R.id.textView)fun bind(text:String?){wordItemView.text=text}...
}

ListAdapter 是 RecyclerView.Adapter 的一个子类,它使用 DiffUtil 来计算数据集的最小更新。当数据发生变化时,ListAdapter 会计算出新旧数据集之间的差异,并使用这些差异来更新 RecyclerView。

在这个例子中,WordListAdapter 是 ListAdapter 的一个子类,它使用 WordsComparator 来计算数据集的差异。
WordsComparator 是 DiffUtil.ItemCallback 的一个子类,它提供了两个方法:areItemsTheSame 和 areContentsTheSame。

areItemsTheSame 用于检查两个 Word 是否表示同一个对象,areContentsTheSame 用于检查两个 Word 的内容是否相同。

class WordsComparator : DiffUtil.ItemCallback<Word>() {override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {return oldItem === newItem}override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean {return oldItem.word == newItem.word}
}

WordViewHolder 中的 create 静态方法用于创建 WordViewHolder 的实例。这个方法接收一个 ViewGroup 类型的参数 parent,这通常是 RecyclerView。

在 create 方法中,首先通过 LayoutInflater 从 recyclerview_item.xml 布局文件中创建一个新的视图。然后,将这个新创建的视图作为参数传递给 WordViewHolder 的构造函数,创建一个 WordViewHolder 的实例。

这样做的好处是,WordViewHolder 的创建逻辑被封装在 WordViewHolder 类内部,使得 WordListAdapter 的代码更加简洁。同时,如果 WordViewHolder 的创建逻辑需要修改,只需要在 WordViewHolder 类内部修改,而不需要修改 WordListAdapter 的代码。

在MainActivity中添加 RecyclerView:

setContentView(R.layout.activity_main)val recyclerView=findViewById<RecyclerView>(R.id.recyclerview)
val adapter=WordListAdapter()
recyclerView.adapter=adapter
recyclerView.layoutManager= LinearLayoutManager(this)

10. 在应用中实例化Repository和Database

您希望应用中的数据库和存储库只有一个实例。实现该目的的一种简单的方法是,将它们作为 Application 类的成员进行创建。然后,在需要时只需从应用检索,而不是每次都进行构建。
创建 WordsApplication,继承自 Application:

package com.alex.roomwordssampleimport android.app.Application
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob/*** @Author      : alex* @Date        : on 2024/1/4 22:03.* @Description : 实现Application类,以便在整个应用程序中使用单个实例*/
class WordsApplication: Application(){// 为应用程序的生命周期创建一个作用域,以便在应用程序被销毁时取消所有协程// 不需要取消这个作用域,因为它会随着进程的结束而被销毁。// SupervisorJob() 创建了一个新的 Job 实例,并将其作为参数传递给 CoroutineScope 的构造函数,创建了一个新的协程作用域 applicationScope。// 这个作用域的特性是,它的子协程之间是相互独立的,一个子协程的失败不会导致其他子协程的取消。val applicationScope = CoroutineScope(SupervisorJob())val database by lazy {WordRoomDatabase.getDatabase(this,applicationScope)}val repository by lazy { WordRepository(database.wordDao()) }
}

在 AndroidManifest 文件将 WordApplication 设为 application android:name

11. 填充数据库

在 WordRoomDatabase 定义了 WordDatabaseCallback 用于在创建数据库的时候,删除旧数据,并添加示例数据

12. 添加 新增数据页面 NewWordActivity

布局 activity_new_word.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".NewWordActivity"><EditTextandroid:id="@+id/edit_word"android:layout_width="match_parent"android:layout_height="wrap_content"android:minHeight="@dimen/min_height"android:fontFamily="sans-serif-light"android:hint="@string/hint_word"android:inputType="textAutoComplete"android:layout_margin="@dimen/big_padding"android:textSize="18sp" /><Buttonandroid:id="@+id/button_save"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/purple_500"android:text="@string/button_save"android:layout_margin="@dimen/big_padding"android:textColor="@color/buttonLabel" /></LinearLayout>

NewWordActivity 代码:

package com.alex.roomwordssampleimport android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.widget.Button
import android.widget.EditTextclass NewWordActivity : AppCompatActivity() {private lateinit var editWordView:EditTextoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_new_word)editWordView = findViewById(R.id.edit_word)val button = findViewById<Button>(R.id.button_save)button.setOnClickListener {val replyIntent = Intent()if(TextUtils.isEmpty(editWordView.text)){setResult(Activity.RESULT_CANCELED,replyIntent)}else{val word = editWordView.text.toString()replyIntent.putExtra(EXTRA_REPLY,word)setResult(Activity.RESULT_OK,replyIntent)}finish()}}companion object{const val EXTRA_REPLY = "com.alex.roomwordssample.REPLY"}
}

13. 数据与页面关联

最后一步是将界面连接到数据库,方法是保存用户输入的新字词,并在 RecyclerView 中显示当前字词数据库的内容。

如需显示数据库的当前内容,请添加可观察 ViewModel 中的 LiveData 的观察者。

每当数据发生变化时,系统都会调用 onChanged() 回调,此操作会调用适配器的 setWords() 方法来更新此适配器的缓存数据并刷新显示的列表。

1. 在 MainActivity 中创建 ViewModel

// 通过 viewModels 委托属性实现ViewModel的实例化
private val wordViewModel: WordViewModel by viewModels {WordViewModelFactory((application as WordsApplication).repository)
}

这里使用了 viewModels 委托,并传入了 WordViewModelFactory 实例,该实例基于从 WordApplication 中检索的存储库构建而成。

当观察到数据发生变化且 activity 在前台显示时,将触发 onChanged() 方法:

// 通过调用 observe() 来观察 LiveData 对象,传入 LifecycleOwner 和 Observer。
wordViewModel.allWords.observe(this){ words ->words.let { adapter.submitList(it) }
}

浮动按钮点击事件,将打开 NewWordActivity ,这里使用了registerForActivityResult 方法和 ActivityResultContracts,因为 startActivityForResult 和 onActivityResult 方法在 Android 11(API 30)中已被弃用。

完整代码:

package com.alex.roomwordssampleimport android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButtonimport androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.ActivityResultLauncher
class MainActivity : AppCompatActivity() {// 请求代码,打开 NewWordActivity 时使用
//    private val newWordActivityRequestCode = 1private lateinit var newWordActivityLauncher: ActivityResultLauncher<Intent>// 通过 viewModels 委托属性实现ViewModel的实例化private val wordViewModel: WordViewModel by viewModels {WordViewModelFactory((application as WordsApplication).repository)}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val recyclerView=findViewById<RecyclerView>(R.id.recyclerview)val adapter=WordListAdapter()recyclerView.adapter=adapterrecyclerView.layoutManager= LinearLayoutManager(this)// 通过调用 observe() 来观察 LiveData 对象,传入 LifecycleOwner 和 Observer。wordViewModel.allWords.observe(this){ words ->words.let { adapter.submitList(it) }}val fab = findViewById<FloatingActionButton>(R.id.fab)newWordActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->if (result.resultCode == Activity.RESULT_OK) {val data: Intent? = result.datadata?.getStringExtra(NewWordActivity.EXTRA_REPLY)?.let { reply ->val word = Word(reply)wordViewModel.insert(word)}} else {Toast.makeText(applicationContext,R.string.empty_not_saved,Toast.LENGTH_LONG).show()}}fab.setOnClickListener {val intent = Intent(this@MainActivity, NewWordActivity::class.java)// startActivityForResult 和 onActivityResult 方法在 Android 11(API 30)中已被弃用。// 取而代之的是 registerForActivityResult 方法和 ActivityResultContracts 类。
//            startActivityForResult(intent, newWordActivityRequestCode)newWordActivityLauncher.launch(intent)}}//    override fun onActivityResult(requestCode: Int, resultCode: Int, intentData: Intent?) {
//        super.onActivityResult(requestCode, resultCode, intentData)
//
//        if (requestCode == newWordActivityRequestCode && resultCode == Activity.RESULT_OK) {
//            intentData?.getStringExtra(NewWordActivity.EXTRA_REPLY)?.let { reply ->
//                val word = Word(reply)
//                wordViewModel.insert(word)
//            }
//        } else {
//            Toast.makeText(
//                applicationContext,
//                R.string.empty_not_saved,
//                Toast.LENGTH_LONG
//            ).show()
//        }
//    }}

image.png

image.png

相关文章:

开启Android学习之旅-2-架构组件实现数据列表及添加(kotlin)

Android Jetpack 体验-官方codelab 1. 实现功能 使用 Jetpack 架构组件 Room、ViewModel 和 LiveData 设计应用&#xff1b;从sqlite获取、保存、删除数据&#xff1b;sqlite数据预填充功能&#xff1b;使用 RecyclerView 展示数据列表&#xff1b; 2. 使用架构组件 架构组…...

leetcode 动态规划(最后一块石头的重量II、目标和、一和零)

1049.最后一块石头的重量II 力扣题目链接(opens new window) 题目难度&#xff1a;中等 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < …...

JavaWeb-HTTP

一、概念 HTTP&#xff1a;HyperText Transfer Protocol&#xff0c;超文本传输协议。读者应该不是第一次接触这个名词&#xff0c;但可能仍然不是很理解&#xff0c;笔者将逐一解释。 HyperText&#xff08;超文本&#xff09;&#xff1a;根据维斯百科&#xff0c;Hypertex…...

算法训练营第四十二天|动态规划:01背包理论基础 416. 分割等和子集

目录 动态规划&#xff1a;01背包理论基础416. 分割等和子集 动态规划&#xff1a;01背包理论基础 文章链接&#xff1a;代码随想录 题目链接&#xff1a;卡码网&#xff1a;46. 携带研究材料 01背包问题 二维数组解法&#xff1a; #include <bits/stdc.h> using namesp…...

前端 JS篇快问快答

问题&#xff1a;常见的特殊字符&#xff08;不包括空格\s&#xff09; 正则表达式为&#xff1a; 回答&#xff1a;/[!#$%^&*()\-_{};:",.<>/?[\]~|]/ &#xff08;加粗的紫色字符都是特殊字符&#xff09; 问题&#xff1a;常见的特殊字符&#xff08;包括…...

vue/vue3/js来动态修改我们的界面浏览器上面的文字和图标

前言&#xff1a; 整理vue/vue3项目中修改界面浏览器上面的文字和图标的方法。 效果&#xff1a; vue2/vue3: 默认修改 public/index.html index.html <!DOCTYPE html> <html lang"en"><head><link rel"icon" type"image/sv…...

MobaXterm SSH 免密登录配置

文章目录 1.简介2.SSH 免密登录配置第一步&#xff1a;点击 Session第二步&#xff1a;选择 SSH第三步&#xff1a;输入服务器地址与用户名第四步&#xff1a;设置会话名称第五步&#xff1a;点击 OK 并输入密码 3.密码管理4.小结参考文献 1.简介 MobaXterm 是一个功能强大的终…...

霍兰德职业兴趣测试:找到与你性格匹配的职业

霍兰德职业兴趣理论 约翰霍兰德&#xff08;John Holland&#xff09;是美国约翰霍普金斯大学心理学教授&#xff0c;美国著名的职业指导专家。他于1959年提出了具有广泛社会影响的职业兴趣理论。认为人的人格类型、兴趣与职业密切相关&#xff0c;兴趣是人们活动的巨大动力&a…...

LVGL学习笔记 显示和隐藏 对象的属性标志位 配置

在显示GUI的过程中需要对某些对象进行临时隐藏或临时显示,因此需要对该对象的FLAG进行配置就可以实现对象的显示和隐藏了. 调用如下接口可以实现: lv_obj_add_flag(user_obj, LV_OBJ_FLAG_HIDDEN);//隐藏对象lv_obj_clear_flag(user_obj, LV_OBJ_FLAG_HIDDEN);//取消隐藏实现的…...

cuda上使用remap函数

在使用opencv中的remap函数时&#xff0c;发现运行时间太长了&#xff0c;如果使用视频流进行重映射时根本不能实时&#xff0c;因此只能加速 1.使用opencv里的cv::cuda::remap函数 cv::cuda::remap函数头文件是#include <opencv2/cudawarping.hpp>&#xff0c;编译ope…...

【JaveWeb教程】(18) MySQL数据库开发之 MySQL数据库设计-DDL 如何查询、创建、使用、删除数据库数据表 详细代码示例讲解

目录 2. 数据库设计-DDL2.1 项目开发流程2.2 数据库操作2.2.1 查询数据库2.2.2 创建数据库2.2.3 使用数据库2.2.4 删除数据库 2.3 图形化工具2.3.1 介绍2.3.2 安装2.3.3 使用2.2.3.1 连接数据库2.2.3.2 操作数据库 2.3 表操作2.3.1 创建2.3.1.1 语法2.3.1.2 约束2.3.1.3 数据类…...

ElasticSearch学习笔记-SpringBoot整合Elasticsearch7

项目最近需要接入Elasticsearch7&#xff0c;顺带记录下笔记。 Elasticsearch依赖包版本 <properties><elasticsearch.version>7.9.3</elasticsearch.version><elasticsearch.rest.version>7.9.3</elasticsearch.rest.version> </propertie…...

[足式机器人]Part2 Dr. CAN学习笔记 - Ch02动态系统建模与分析

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Ch02动态系统建模与分析 1. 课程介绍2. 电路系统建模、基尔霍夫定律3. 流体系统建模4. 拉普拉斯变换&#xff08;Laplace&#xff09;传递函数、微分方程4.1 Laplace Transform 拉式变换4.2 收…...

【一周年创作总结】人生是远方的无尽旷野呀

那一眼瞥见的伟大的灵魂&#xff0c;却似模糊的你和我 文章目录 &#x1f4d2;各个阶段的experience&#x1f50e;大一寒假&#x1f50e;大一下学期&#x1f50e;大一暑假&#x1f50e;大二上学期&#xff08;现在&#xff09; &#x1f354;相遇CSDN&#x1f6f8;自媒体&#…...

金融帝国实验室(Capitalism Lab)V10版本游戏平衡性优化与改进

即将推出的V10版本中的各种游戏平衡性优化与改进&#xff1a; ————————————— 一、当玩家被提议收购一家即将破产的公司时&#xff0c;显示商业秘密。 当一家公司濒临破产&#xff0c;玩家被提议收购该公司时&#xff0c;如果玩家有兴趣评估该公司&#xff0c;则无…...

[SpringBoot]接口的多实现:选择性注入SpringBoot接口的实现类

最近在项目中遇到两种情况&#xff0c;准备写个博客记录一下。 情况说明&#xff1a;Service层一个接口是否可以存在多个具体实现&#xff0c;此时应该如何调用Service&#xff08;的具体实现&#xff09;&#xff1f; 其实之前的项目中也遇到过这种情况&#xff0c;只不过我采…...

北京大学 wlw机器学习2022春季期末试题分析

北京大学 wlw机器学习2022春季期末试题分析 前言新的开始第一题第二题第三题 前言 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的开始 第…...

前端文件下载方法(包含get和post)

export const downloadFileWithIframe (url, name) > {const iframe document.createElement(iframe);iframe.style.display none; // 防止影响页面iframe.style.height 0; // 防止影响页面iframe.name name;iframe.src url;document.body.appendChild(iframe); // 这…...

高性能、可扩展、支持二次开发的企业电子招标采购系统源码

在数字化时代&#xff0c;企业需要借助先进的数字化技术来提高工程管理效率和质量。招投标管理系统作为企业内部业务项目管理的重要应用平台&#xff0c;涵盖了门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理等…...

2645. 构造有效字符串的最少插入数

Problem: 2645. 构造有效字符串的最少插入数 文章目录 解题思路解决方法复杂度分析代码实现 解题思路 解决此问题需要确定如何以最小的插入次数构造一个有效的字符串。首先&#xff0c;我们需要确定开头的差距&#xff0c;然后决定中间的补足&#xff0c;最后决定末尾的差距。…...

C#,快速排序算法(Quick Sort)的非递归实现与数据可视化

排序算法是编程的基础。 常见的四种排序算法是&#xff1a;简单选择排序、冒泡排序、插入排序和快速排序。其中的快速排序的优势明显&#xff0c;一般使用递归方式实现&#xff0c;但遇到数据量大的情况则无法适用。实际工程中一般使用“非递归”方式实现。 快速排序(Quick Sor…...

【操作系统xv6】学习记录2 -RISC-V Architecture

说明&#xff1a;看完这节&#xff0c;不会让你称为汇编程序员&#xff0c;知识操作系统的前置。 ref&#xff1a;https://binhack.readthedocs.io/zh/latest/assembly/mips.html https://www.bilibili.com/video/BV1w94y1a7i8/?p7 MIPS MIPS的意思是 “无内部互锁流水级的微…...

C++力扣题目111--二叉树的最小深度

力扣题目链接(opens new window) 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明: 叶子节点是指没有子节点的节点。 示例: 给定二叉树 [3,9,20,null,null,15,7], 返回它的最小深度 2 思路 看完了这篇104.二…...

【图像拼接】源码精读:Adaptive As-Natural-As-Possible Image Stitching(AANAP/ANAP)

第一次来请先看这篇文章:【图像拼接(Image Stitching)】关于【图像拼接论文源码精读】专栏的相关说明,包含专栏内文章结构说明、源码阅读顺序、培养代码能力、如何创新等(不定期更新) 【图像拼接论文源码精读】专栏文章目录 【源码精读】As-Projective-As-Possible Imag…...

解决docker run报错:Error response from daemon: No command specified.

将docker镜像export/import之后&#xff0c;对新的镜像执行docker run时报错&#xff1a; docker: Error response from daemon: No command specified. 解决方法&#xff1a; 方案1&#xff1a; 查看容器的command&#xff1a; docker ps --no-trunc 在docker run命令上增加…...

算法第十二天-最大整除子集

最大整除子集 题目要求 解题思路 来自[宫水三叶] 根据题意&#xff1a;对于符合要求的[整除子集]中的任意两个值&#xff0c;必然满足[较大数]是[较小数]的倍数 数据范围是 1 0 3 10^3 103&#xff0c;我们不可能采取获取所有子集&#xff0c;再检查子集是否合法的暴力搜解法…...

简单易懂的PyTorch 损失函数:优化机器学习模型的关键

目录 torch.nn子模块Loss Functions详解 nn.L1Loss 用途 用法 使用技巧 注意事项 代码示例 nn.MSELoss 用途 用法 使用技巧 注意事项 代码示例 nn.CrossEntropyLoss 用途 用法 使用技巧 注意事项 代码示例 使用类别索引 使用类别概率 nn.CTCLoss 用途 …...

Kubernetes/k8s的存储卷/数据卷

k8s的存储卷/数据卷 容器内的目录和宿主机的目录挂载 容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会回复到初始状态 一旦回到初始状态&#xff0c;所有的后天编辑的文件都会消失…...

【漏洞复现】锐捷RG-UAC统一上网行为管理系统信息泄露漏洞

Nx01 产品简介 锐捷网络成立于2000年1月&#xff0c;原名实达网络&#xff0c;2003年更名&#xff0c;自成立以来&#xff0c;一直扎根行业&#xff0c;深入场景进行解决方案设计和创新&#xff0c;并利用云计算、SDN、移动互联、大数据、物联网、AI等新技术为各行业用户提供场…...

Android - 串口通讯(SerialPort)

最早的博客Android 模拟串口通信过程_launch virtual serial port driver pro-CSDN博客里就是用过 Google 提供的 demo&#xff0c;最近想再写个其他的demo发现用起来有点麻烦&#xff0c;还需要导入其他 module&#xff0c;因此在网上找到了Android-SerialPort-API: https://g…...