对象字段更改的LiveData更新

时间:2017-12-29 09:34:23

标签: android observable android-livedata android-viewmodel

我在LiveData中使用Android MVVM架构。我有一个这样的对象

public class User {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

我的视图模型看起来像这样

public class InfoViewModel extends AndroidViewModel {
    MutableLiveData<User> user = new MutableLiveData<>();

    public InfoViewModel(@NonNull Application application) {
        super(application);
        User user = new User();
        user.setFirstName("Alireza");
        user.setLastName("Ahmadi");

        this.user.setValue(user);
    }

    public LiveData<User> getUser(){
        return user;
    }

    public void change(){
        user.getValue().setFirstName(user.getValue().getFirstName() + " A ");
    }
}

如何确保用户对象中的某些变更观察者收到通知?顺便说一下,将这些数据保存在单独的对象中并且不在我的ViewModel中使用像字符串这样的主要值这一点很重要。

6 个答案:

答案 0 :(得分:26)

我认为没有像Android推荐的最佳做法。我建议你使用使用清洁剂和清洁剂的方法。更少的样板代码。

如果您使用Android数据绑定以及LiveData,您可以采用以下方法:

您的POJO对象看起来像这样

public class User extends BaseObservable {
    private String firstName;
    private String lastName;

    @Bindable
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    @Bindable
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}

因此,您将拥有一个在其属性发生变化时通知的类。因此,您可以在MutableLiveData中使用此属性更改回调来通知其观察者。您可以为此

创建自定义MutableLiveData
public class CustomMutableLiveData<T extends BaseObservable>
        extends MutableLiveData<T> {


    @Override
    public void setValue(T value) {
        super.setValue(value);

        //listen to property changes
        value.addOnPropertyChangedCallback(callback);
    }

    Observable.OnPropertyChangedCallback callback = new Observable.OnPropertyChangedCallback() {
        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {

            //Trigger LiveData observer on change of any property in object
            setValue(getValue());

        }
    };


}

然后您需要做的就是在View模型中使用此CustomMutableLiveData而不是MutableLiveData

public class InfoViewModel extends AndroidViewModel {

    CustomMutableLiveData<User> user = new CustomMutableLiveData<>();
-----
-----

通过这样做你可以通知视图和&amp; LiveData观察者对现有代码几乎没有任何改变。希望它有所帮助

答案 1 :(得分:8)

如果您使用的是Kotlin和LiveData,我可以为您提供2种方法-带有和不带有扩展功能:

没有扩展功能

import React, { useState, useEffect } from "react";
import {
  StyleSheet,
  View,
  Text,
  ActivityIndicator,
  TouchableOpacity,
  TextInput
} from "react-native";
import * as firebase from "firebase";

const AccountScreen = () => {
  const db = firebase.firestore();
  let user = firebase.auth().currentUser;

  const [user_data, set_user_data] = useState(null);

  const SignOut = () => {
    set_user_data(null);
    firebase
      .auth()
      .signOut()
      .then(() => {
        console.log("successfully logged out");
      })
      .catch(error => {
        // An error happened.
        console.log("error logging out" + error);
      });
  };

  if (user) {
    useEffect(() => {
      db.doc(`users/${user.uid}`)
        .get()
        .then(doc => {
          set_user_data(doc.data());
        })
        .catch(error => {
          console.log("Error getting document:", error);
        });
    }, []);
    if (!user_data) {
      console.log("uh oh! there's a user but they don't have any data");
      return (
        <View style={styles.container}>
          <ActivityIndicator
            animating={true}
            style={styles.indicator}
            size="large"
          />
        </View>
      );
    } else if (user_data) {
      console.log("nice! there's a user and they have data");
      return (
        <View style={styles.container}>
          <TouchableOpacity
            style={styles.submitButton}
            onPress={() => SignOut()}
          >
            <Text style={styles.submitButtonText}>Sign Out</Text>
          </TouchableOpacity>
        </View>
      );
    }
  } else {
    console.log("no user at all!!");
    return (
      <View>
        <Text>There's an error here</Text>
      </View>
    );
  }
};

export default AccountScreen;

相同,但带有扩展名

liveData.value = liveData.value?.also { it ->
    // Modify your object here. Data will be auto-updated
    it.name = "Ed Khalturin"
    it.happyNumber = 42
}

答案 2 :(得分:7)

使用MVVM和LiveData时,您可以将对象重新绑定到布局,这样它将触发UI上的所有更改。

在ViewModel中,“用户”为MutableLiveData<User>

ViewModel

class SampleViewModel : ViewModel() {
    val user = MutableLiveData<User>()

    fun onChange() {
        user.value.firstname = "New name"
        user.value = user.value // force postValue to notify Observers
        // can also use user.postValue()
    }
}

活动/片段文件:

viewModel = ViewModelProviders
            .of(this)
            .get(SampleViewModel::class.java)

// when viewModel.user changes, this observer get notified and re-bind
// the user model with the layout.
viewModel.user.observe(this, Observer {
    binding.user = viewModel.user //<- re-binding
})

您的布局文件不应更改:

<data>
    <variable
        name="user"
        type="com.project.model.User" />
</data>

...

<TextView
        android:id="@+id/firstname"
        android:text="@{user.firstname}"
        />

答案 3 :(得分:1)

来自reddit - @cedrickc's answer

为 MutableLiveData 添加一个扩展函数:

fun <T> MutableLiveData<T>.modifyValue(transform: T.() -> T) {
   this.value = this.value?.run(transform)
}

答案 4 :(得分:0)

如果您的观察者收到通知,您应该使用setValue user.getValue().setFirstName(user.getValue().getFirstName() + " A ");您的观察者不会收到通知!

查看模型

public MutableLiveData<User> getUser() {
    return user;
}

活动/片段

mModel = ViewModelProviders.of(this).get(InfoViewModel.class);
mModel.getUser().observe(this, s -> {
    // User has been modified
});

活动/片段中的某个地方

这将触发观察者:

mModel.getUser().setValue(user);

如果您只想更新对象中的一个字段而不是更新整个对象,则应该有倍数MutableLiveData<String>

// View Model
private MutableLiveData<String>         firstName;
private MutableLiveData<String>         lastName;

//Somewhere in your code
mModel.getFirstName().setValue(user.getValue().getFirstName() + " A ");
mModel.getFirstName().observe(this, s -> {
    // Firstname has been modified
});

答案 5 :(得分:0)

我如何确定何时在用户对象中归档某些字段来更改观察者 得到通知?顺便说一句,对我来说,将这些数据保存在 单独的对象,并且在 ViewModel。

您可以使用androidx.lifecyle.Transformation类监视各个字段。

val user = MutableLiveData<User>();
//to monitor for User.Name
val firstName: LiveData<String>  = Transformations.map {it.firstName}
val lastName: LiveData<String>  = Transformations.map {it.lastName}

您可以正常更新用户,并监听名字/姓氏以监视这些字段中的更改。