Android fragments 安卓数据绑定&x2B;Mediator实时数据-处理生命周期事件
在我的Android应用程序中,我有一个片段,用户可以同时查看和编辑某些对象的属性 我正在使用一个带有数据绑定的MVVM体系结构和一个保存正在编辑的关系对象的中介实时数据。下面是它的工作原理:Android fragments 安卓数据绑定&x2B;Mediator实时数据-处理生命周期事件,android-fragments,mvvm,android-lifecycle,android-databinding,android-livedata,Android Fragments,Mvvm,Android Lifecycle,Android Databinding,Android Livedata,在我的Android应用程序中,我有一个片段,用户可以同时查看和编辑某些对象的属性 我正在使用一个带有数据绑定的MVVM体系结构和一个保存正在编辑的关系对象的中介实时数据。下面是它的工作原理: 片段膨胀并绑定视图(布局xml) 在此过程中,片段生成了一个ViewModel ViewModel将从数据库中获取关系对象(及其属性),并将其放在LiveData中 由于数据绑定和绑定适配器,editText字段自动设置为对象的属性 然后,用户可以编辑这些编辑文本字段并保存 保存后,ViewModel将从
<?xml version="1.0" encoding="utf-8"?>
<layout
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"
tools:context=".presentation.relationdetail.RelationDetailFragment">
<data>
<variable
name="relationDetailViewModel"
type="be.pjvandamme.farfiled.presentation.relationdetail.RelationDetailViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/relationDetailLayoutMargin">
<TextView
android:id="@+id/nameLabelTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginBottom="8dp"
android:text="@string/nameLabel"
app:layout_constraintBottom_toTopOf="@+id/relationNameEditText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/relationNameEditText"
android:layout_width="@dimen/relationNameEditWidth"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/editTextChips"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/nameLabelTextView"
app:relationName="@{relationDetailViewModel.relation}" />
viewmodel:
class RelationDetailViewModel (
private val relationKey: Long?,
val relationDatabase: RelationDao,
val relationLifeAreaDatabase: RelationLifeAreaDao,
application: Application
): AndroidViewModel(application) {
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private val relation = MediatorLiveData<Relation?>()
fun getRelation() = relation
private val relationLifeAreas = MediatorLiveData<List<RelationLifeArea?>>()
fun getRelationLifeAreas() = relationLifeAreas
// other LiveData's with backing properties, to trigger UI events
init {
initializeRelation()
}
private fun initializeRelation(){
if(relationKey == null || relationKey == -1L) {
initializeNewRelation()
getAdorableAvatarFacialFeatures()
}
else {
retrieveAvatarUrl()
relation.addSource(
relationDatabase.getRelationWithId(relationKey),
relation::setValue)
relationLifeAreas.addSource(
relationLifeAreaDatabase.getAllRelationLifeAreasForRelation(relationKey),
relationLifeAreas::setValue)
}
}
private fun initializeNewRelation(){
uiScope.launch{
var relationId = insert(Relation(0L,"","",null,false))
initializeLifeAreasForRelation(relationId)
relation.addSource(
relationDatabase.getRelationWithId(
relationId!!),
relation::setValue)
relationLifeAreas.addSource(
relationLifeAreaDatabase.getAllRelationLifeAreasForRelation(
relationId!!),
relationLifeAreas::setValue)
}
}
private fun initializeLifeAreasForRelation(relationId: Long?){
if(relationId != null){
enumValues<LifeArea>().forEach {
uiScope.launch{
var relationLifeArea = RelationLifeArea(0L,relationId,it,"")
insert(relationLifeArea)
}
}
}
}
private fun retrieveAvatarUrl(){
uiScope.launch{
var rel = get(relationKey!!)
var avatarUrl = rel?.avatarUrl
if (avatarUrl.isNullOrEmpty()){
getAdorableAvatarFacialFeatures()
_enableSaveButton.value = true
}
else
_adorableAvatarString.value = rel?.avatarUrl
}
}
private fun getAdorableAvatarFacialFeatures(){
uiScope.launch{
var getFeaturesDeferred = AdorableAvatarApi.retrofitService.getFacialFeatures()
try{
var result = getFeaturesDeferred.await()
_adorableAvatarString.value = "https://api.adorable.io/avatars/face/" +
result.features.eyes.shuffled().take(1)[0] + "/" +
result.features.nose.shuffled().take(1)[0] + "/" +
result.features.mouth.shuffled().take(1)[0] + "/" +
result.features.COLOR_PALETTE.shuffled().take(1)[0]
relation.value?.avatarUrl = _adorableAvatarString.value
} catch(t:Throwable){
// ToDo: what if this fails?? -> Try again later!!
_adorableAvatarString.value = "Failure: " + t.message
}
}
}
fun onEditRelation(
relationNameText: String,
relationSynopsisText: String,
lifeAreaNowText: String,
// etc.
){
_enableSaveButton.value = !compareRelationAttributes(
relationNameText,
relationSynopsisText,
lifeAreaNow.Text,
// etc
)
}
private fun compareRelationAttributes(
relationNameText: String,
relationSynopsisText: String,
lifeAreaNowText: String,
// etc.
): Boolean {
// checks if any of the attributes of the Relation object were changed
// i.e. at least 1 of the editText fields has a text content that does
// does not equal the corresponding attribute of the Relation object
}
fun onSave(
name: String,
synopsis: String,
nowLA: String,
// etc.
){
if(!name.isNullOrEmpty()) {
uiScope.launch {
// update the DB
}
// TODO: this one should go away, need some sort of up button instead
_navigateToRelationsList.value = true
}
else
_showNameEmptySnackbar.value = true
}
// database suspend funs omitted
// ui event handling functions
override fun onCleared(){ /* cancel the viewModelJob */ }
}
类关系详细视图模型(
private val relationKey:长?,
val relationDatabase:RelationDao,
val RelationLifeArea数据库:RelationLifeAreaDao,
应用程序:应用程序
):AndroidViewModel(应用程序){
私有变量viewModelJob=Job()
private val uiScope=CoroutineScope(Dispatchers.Main+viewModelJob)
private val relation=MediatorLiveData()
fun getRelation()=关系
private val relationLifeAreas=MediatorLiveData()
乐趣getRelationLifeAreas()=relationLifeAreas
//具有支持属性的其他LiveData,用于触发UI事件
初始化{
初始化()
}
私人娱乐初始化(){
如果(relationKey==null | | relationKey==1L){
初始化
getAdorableAvatarFacialFeatures()
}
否则{
RetrieveAvataUrl()
relationship.addSource(
relationDatabase.getRelationWithId(relationKey),
关系::设置值)
relationLifeAreas.addSource(
RelationLifeArea数据库。GetAllRelationLifeAreas关联(relationKey),
relationLifeAreas::setValue)
}
}
私人娱乐初始值化(){
发射{
var relationId=insert(关系(0L,“,”,null,false))
初始化关系(relationId)
relationship.addSource(
relationDatabase.getRelationWithId(
relationId!!),
关系::设置值)
relationLifeAreas.addSource(
relationLifeAreaDatabase.getAllRelationLifeAreasForRelation(
relationId!!),
relationLifeAreas::setValue)
}
}
private fun初始值EliFeareasForRelation(关系ID:Long?){
if(relationId!=null){
enumValues().forEach{
发射{
var relationLifeArea=relationLifeArea(0L,relationId,it,“”)
插入(relationLifeArea)
}
}
}
}
私人娱乐检索{
发射{
var rel=get(relationKey!!)
变量avatarUrl=rel?.avatarUrl
if(avatarUrl.isNullOrEmpty()){
getAdorableAvatarFacialFeatures()
_enableSaveButton.value=true
}
其他的
_可爱的化身字符串.value=rel?.avatarUrl
}
}
私人娱乐getAdorableAvatarFacialFeatures(){
发射{
var getFeaturesFerred=AdorableAvatarApi.RefughtService.getFacialFeatures()
试一试{
var result=getFeaturesDeferred.await()
_adorableAvatarString.value=”https://api.adorable.io/avatars/face/" +
result.features.eyes.shuffled().take(1)[0]+“/”+
result.features.nose.shuffled().take(1)[0]+“/”+
result.features.mouth.shuffled().take(1)[0]+“/”+
result.features.COLOR_palete.shuffled().take(1)[0]
relation.value?.avatarUrl=\u adorableAvatarString.value
}捕获(t:可丢弃){
//ToDo:如果失败怎么办??->请稍后再试!!
_adorableAvatarString.value=“失败:”+t.message
}
}
}
有趣的关系(
relationNameText:String,
relationSynopsisText:String,
lifeAreaNowText:String,
//等等。
){
_enableSaveButton.value=!compareRelationAttributes(
关系名称文本,
relationSynopsisText,
lifeAreaNow.Text,
//等
)
}
私人乐趣比较属性(
relationNameText:String,
relationSynopsisText:String,
lifeAreaNowText:String,
//等等。
):布尔值{
//检查关系对象的任何属性是否已更改
//也就是说,至少有一个editText字段的文本内容
//不等于关系对象的相应属性
}
快乐在储蓄(
名称:String,
剧情简介:弦,
诺拉:弦,
//等等。
){
如果(!name.isNullOrEmpty()){
发射{
//更新数据库
class RelationDetailViewModel (
private val relationKey: Long?,
val relationDatabase: RelationDao,
val relationLifeAreaDatabase: RelationLifeAreaDao,
application: Application
): AndroidViewModel(application) {
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private val relation = MediatorLiveData<Relation?>()
fun getRelation() = relation
private val relationLifeAreas = MediatorLiveData<List<RelationLifeArea?>>()
fun getRelationLifeAreas() = relationLifeAreas
// other LiveData's with backing properties, to trigger UI events
init {
initializeRelation()
}
private fun initializeRelation(){
if(relationKey == null || relationKey == -1L) {
initializeNewRelation()
getAdorableAvatarFacialFeatures()
}
else {
retrieveAvatarUrl()
relation.addSource(
relationDatabase.getRelationWithId(relationKey),
relation::setValue)
relationLifeAreas.addSource(
relationLifeAreaDatabase.getAllRelationLifeAreasForRelation(relationKey),
relationLifeAreas::setValue)
}
}
private fun initializeNewRelation(){
uiScope.launch{
var relationId = insert(Relation(0L,"","",null,false))
initializeLifeAreasForRelation(relationId)
relation.addSource(
relationDatabase.getRelationWithId(
relationId!!),
relation::setValue)
relationLifeAreas.addSource(
relationLifeAreaDatabase.getAllRelationLifeAreasForRelation(
relationId!!),
relationLifeAreas::setValue)
}
}
private fun initializeLifeAreasForRelation(relationId: Long?){
if(relationId != null){
enumValues<LifeArea>().forEach {
uiScope.launch{
var relationLifeArea = RelationLifeArea(0L,relationId,it,"")
insert(relationLifeArea)
}
}
}
}
private fun retrieveAvatarUrl(){
uiScope.launch{
var rel = get(relationKey!!)
var avatarUrl = rel?.avatarUrl
if (avatarUrl.isNullOrEmpty()){
getAdorableAvatarFacialFeatures()
_enableSaveButton.value = true
}
else
_adorableAvatarString.value = rel?.avatarUrl
}
}
private fun getAdorableAvatarFacialFeatures(){
uiScope.launch{
var getFeaturesDeferred = AdorableAvatarApi.retrofitService.getFacialFeatures()
try{
var result = getFeaturesDeferred.await()
_adorableAvatarString.value = "https://api.adorable.io/avatars/face/" +
result.features.eyes.shuffled().take(1)[0] + "/" +
result.features.nose.shuffled().take(1)[0] + "/" +
result.features.mouth.shuffled().take(1)[0] + "/" +
result.features.COLOR_PALETTE.shuffled().take(1)[0]
relation.value?.avatarUrl = _adorableAvatarString.value
} catch(t:Throwable){
// ToDo: what if this fails?? -> Try again later!!
_adorableAvatarString.value = "Failure: " + t.message
}
}
}
fun onEditRelation(
relationNameText: String,
relationSynopsisText: String,
lifeAreaNowText: String,
// etc.
){
_enableSaveButton.value = !compareRelationAttributes(
relationNameText,
relationSynopsisText,
lifeAreaNow.Text,
// etc
)
}
private fun compareRelationAttributes(
relationNameText: String,
relationSynopsisText: String,
lifeAreaNowText: String,
// etc.
): Boolean {
// checks if any of the attributes of the Relation object were changed
// i.e. at least 1 of the editText fields has a text content that does
// does not equal the corresponding attribute of the Relation object
}
fun onSave(
name: String,
synopsis: String,
nowLA: String,
// etc.
){
if(!name.isNullOrEmpty()) {
uiScope.launch {
// update the DB
}
// TODO: this one should go away, need some sort of up button instead
_navigateToRelationsList.value = true
}
else
_showNameEmptySnackbar.value = true
}
// database suspend funs omitted
// ui event handling functions
override fun onCleared(){ /* cancel the viewModelJob */ }
}
@BindingAdapter("relationName")
fun TextView.setRelationName(item: Relation?){
item?.let{
text = item.name
}
}
@BindingAdapter("relationSynopsis")
fun TextView.setRelationSynopsis(item: Relation?){
item?.let{
text = item.synopsis
}
}
@BindingAdapter("relationLifeAreaNow")
fun TextView.setLifeAreaNowText(item: List<RelationLifeArea?>?){
item?.let{
item.forEach{
if(it?.lifeArea == LifeArea.EPHEMERA){
text = it.content
}
}
}
}
<!-- etc. -->