Jetpack Compose:使用 TextField 修改房间数据 class
Jetpack Compose: Modify Room data class using TextField
使用 EditText
修改简单值和 data class
es 非常简单,通常看起来像这样:
data class Person(var firstName: String, var lastName: Int)
// ...
val (person, setPerson) = remember { mutableStateOf(Person()) }
// common `onChange` function handles both class properties, ensuring maximum code re-use
fun <T> onChange(field: KMutableProperty1<Person, T>, value: T) {
val nextPerson = person.copy()
field.set(nextPerson, value)
setPerson(nextPerson)
}
// text field for first name
TextField(
value = person.firstName,
onChange = { it -> onChange(Person::firstName, it) })
// text field for last name name
TextField(
value = person.lastName,
onChange = { it -> onChange(Person::lastName, it) })
如您所见,这个例子中的代码是高度可重用的:由于 Kotlin 的反射特性,我们可以使用单个 onChange
函数来修改这个 [=57= 中的每个 属性 ].
但是,当 Person
class 不是从头开始实例化,而是通过 Room
从磁盘中提取时,就会出现问题。例如,PersonDao
可能包含一个 `findOne() 函数,如下所示:
@Query("SELECT * FROM peopleTable WHERE id=:personId LIMIT 1")
fun findOne(personId: String): LiveData<Person>
但是,由于多种原因,您不能真正在 remember {}
中使用此 LiveData:
- 虽然
LiveData
有一个名为 observeAsState()
的函数,但它 returns State<T>
而不是 MutableState<T>
,这意味着您不能使用 [= 修改它24=]。因此,这不起作用:
remember { personFromDb.observeAsState()}
- 您不能
.copy()
您从数据库中获取的 Person
因为您的组件将在 Room 查询返回之前呈现,这意味着您不能这样做,因为 Person
class 个实例将被记住为 null
:
remember { mutableStateOf(findPersonQueryResult.value) }
鉴于此,处理此问题的正确方法是什么?包含 TextField
的组件是否应该包装在另一个处理 Room 查询的组件中,并且只在返回查询时显示表单? LiveData<Person>
的这种情况会是什么样子?
我会用一个副本和一个不可变数据来做到这一点class
typealias PersonID = Long?
@Entity
data class Person(val firstName: String, val lastName: String) {
@PrimaryKey(autoGenerate = true)
val personID: PersonID = null
}
//VM or sth
object VM {
val liveData: LiveData<Person> = MutableLiveData() // your db call
val personDao: PersonDao? = null // Pretending it exists
}
@Dao
abstract class PersonDao {
abstract fun upsert(person: Person)
}
@Composable
fun test() {
val personState = VM.liveData.observeAsState(Person("", ""))
TextField(
value = personState.value.firstName,
onValueChange = { fName -> VM.personDao?.upsert(personState.value.copy(firstName = fName))}
)
}
使用 EditText
修改简单值和 data class
es 非常简单,通常看起来像这样:
data class Person(var firstName: String, var lastName: Int)
// ...
val (person, setPerson) = remember { mutableStateOf(Person()) }
// common `onChange` function handles both class properties, ensuring maximum code re-use
fun <T> onChange(field: KMutableProperty1<Person, T>, value: T) {
val nextPerson = person.copy()
field.set(nextPerson, value)
setPerson(nextPerson)
}
// text field for first name
TextField(
value = person.firstName,
onChange = { it -> onChange(Person::firstName, it) })
// text field for last name name
TextField(
value = person.lastName,
onChange = { it -> onChange(Person::lastName, it) })
如您所见,这个例子中的代码是高度可重用的:由于 Kotlin 的反射特性,我们可以使用单个 onChange
函数来修改这个 [=57= 中的每个 属性 ].
但是,当 Person
class 不是从头开始实例化,而是通过 Room
从磁盘中提取时,就会出现问题。例如,PersonDao
可能包含一个 `findOne() 函数,如下所示:
@Query("SELECT * FROM peopleTable WHERE id=:personId LIMIT 1")
fun findOne(personId: String): LiveData<Person>
但是,由于多种原因,您不能真正在 remember {}
中使用此 LiveData:
- 虽然
LiveData
有一个名为observeAsState()
的函数,但它 returnsState<T>
而不是MutableState<T>
,这意味着您不能使用 [= 修改它24=]。因此,这不起作用:remember { personFromDb.observeAsState()}
- 您不能
.copy()
您从数据库中获取的Person
因为您的组件将在 Room 查询返回之前呈现,这意味着您不能这样做,因为Person
class 个实例将被记住为null
:remember { mutableStateOf(findPersonQueryResult.value) }
鉴于此,处理此问题的正确方法是什么?包含 TextField
的组件是否应该包装在另一个处理 Room 查询的组件中,并且只在返回查询时显示表单? LiveData<Person>
的这种情况会是什么样子?
我会用一个副本和一个不可变数据来做到这一点class
typealias PersonID = Long?
@Entity
data class Person(val firstName: String, val lastName: String) {
@PrimaryKey(autoGenerate = true)
val personID: PersonID = null
}
//VM or sth
object VM {
val liveData: LiveData<Person> = MutableLiveData() // your db call
val personDao: PersonDao? = null // Pretending it exists
}
@Dao
abstract class PersonDao {
abstract fun upsert(person: Person)
}
@Composable
fun test() {
val personState = VM.liveData.observeAsState(Person("", ""))
TextField(
value = personState.value.firstName,
onValueChange = { fName -> VM.personDao?.upsert(personState.value.copy(firstName = fName))}
)
}