Is there a possibility to modify Lombok's #Getter for one type of field? There is a MutableLiveData that is child of LiveData. I want Lombok to create getters for MutableLiveData fields that return LiveData not MutableLiveData. I hope you understand what I mean.
To picture what I'm talking about I am adding some code:
public class ViewModelAccount extends ViewModel {
private MutableLiveData<String> selectedLanguagesReadable = new MutableLiveData<>();
public LiveData<String> getSelectedLanguagesReadable() {
return selectedLanguagesReadable;
}
}
It is about MVVM pattern in Android and removing boilerplate code in ViewModels. Thanks.
This answer might help you,
#Getter(AccessLevel.NONE) private boolean hasObject;
public boolean hasObject() {
return hasObject;
}
i found it here Edit lombok getter method name for boolean member having prefix "has"
Related
I used the code showen below to not expose the MutableLiveData in the main activity
class CalculatorViewModel : ViewModel(){
private val operation = MutableLiveData<String>()
val stringOperation :LiveData<String>
get() = operation
}
but I figured out a way to access the value of MutableLiveData via the LiveData getter and even change it and this is my code to do it:
(viewModel.stringOperation as MutableLiveData).value = ""
MutableLiveData is supposed to be used when you need to modify LiveData outside of your viewmodel. If you don't want it to get exposed from ViewModel, you must use LiveData.
If you look at LiveData class, you will notice that LiveData is an Abstract class, which means you have to extend it before you can use it. For cases such as yours, you would extend LiveData class. In that child class, you would for example call api and and update the value using private methods. So basically your LiveData class will be responsible for loading and updating the data.
Check out this link for an example:
https://developer.android.com/topic/libraries/architecture/livedata#extend_livedata
This is how to properly use LiveData class as per documentation:
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
#Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
public StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
#Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
#Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
To sum up, Use MutableLiveData when you want to modify data outside of LiveData/ViewModel. For all other cases, provide your own implementation of LiveData.
I'm trying to use two-way databinding on a EditText, that works fine if I expose the field as MutableLiveData as it is usually seen on examples I found online.
However there are good reasons not to expose MutableLiveData and those reasons aren't magically invalid because I decided to use the databinding library.
EDIT: The main motivation here is MyViewModel should remain in control of setting data (this is the reason why it is not recommended to expose MutableLiveData directly), in the setter I can perform whatever checks or transformations necessary and then just call setValue on the LiveData.
I usually expose a LiveData getter and a separate setter from my ViewModel, I tried to get this working with two-way data binding by using the InverseMethod() annotation, but that won't really work because databinding is looking for a InverseMethod to getValue() of the LiveData itself.
Here is a simple example:
public class MyViewModel extends ViewModel {
private MutableLiveData<String> mEmail = new MutableLiveData<>();
// #InverseMethod("setEmail") ### THIS DOESN'T WORK
public LiveData<String> getEmail() {
return mEmail;
}
// ### I WANT DATA-BINDING TO USE THIS METHOD
public void setEmail(String email) {
if (mEmail.getValue() != email) {
mEmail.setValue(email);
}
}
}
and this how a want to bind it
<EditText
android:id="#+id/input_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#={viewmodel.email}"/>
the only workaround so far that works is using one-way data-binding to set the text on the EditText and then attach a TextWatcher and call my ViewModel.setter from there.
EDIT:
second workaround is to extend MutableLiveData and then do the checks and transformations in an overridden setValue ... that's a lot of boilerplate to write.
We have been recommended to switch from ObservableField to LiveData for data binding because it is lifecycle-aware. We have also been recommended not expose MutableLiveData because the view model should control assignment.
This works perfectly for one-way data binding and in those cases I would only expose LiveData.
We want to use two-way data binding, which by definition moves assignment from the view model to the UI, so I think in this case exposing MutableLiveData is correct. I say this as we are doing it on purpose because we want our UI to be able to assign values so that we have cleaner views.
I've forgotten about this issue for a while, but as a workaround I've extended MutableLiveData slightly and use this instead every time I need control over the setter.
import androidx.core.util.Consumer;
import androidx.lifecycle.MutableLiveData;
public class DelegatedMutableLiveData<T> extends MutableLiveData<T> {
private Delegate<T> mDelegate;
public interface Delegate<T> {
void onSet(T value, Consumer<T> setter);
}
public DelegatedMutableLiveData(Delegate<T> delegate) {
mDelegate = delegate;
}
public DelegatedMutableLiveData(T value, Delegate<T> delegate) {
super(value);
mDelegate = delegate;
}
#Override
public void setValue(T value) {
if (mDelegate != null) {
mDelegate.onSet(value, super::setValue);
} else {
super.setValue(value);
}
}
}
now use DelegatedMutableLiveData as follows:
private final DelegatedMutableLiveData<Integer> mMyLiveData = new DelegatedMutableLiveData<>((value, setter) -> {
// do your checks and change value if necessary
setter.accept(value); // or don't accept if you don't want to change the current value
});
I had exactly the same question, that´s how i found yours.
I know it´s not exactly what you are looking for but an option would be to observe mEmail from your ViewModel, and implement your setEmail() code in it (after the value itself has been set of course... i don´t know how to control setting the value which is what you are looking for i guess)
val observer = Observer<String> { setEmail(it)}
fun setEmail(value:String){ //Your code }
init{
mEmail.observeForever(observer)
}
//Don´t forget to remove the observer
I've followed https://github.com/googlecodelabs/android-build-an-app-architecture-components.
I want to be able to fetch weather data by city name.
I've created the required method in the DAO class.
I've changed my code in the Repository class to:
public LiveData<List<ListWeatherEntry>> getCurrentWeatherForecasts(String cityName) {
initializeData();
Date today = SunshineDateUtils.getNormalizedUtcDateForToday();
return mWeatherDao.getCurrentWeatherForecasts(today,cityName);
}
But in my ViewModel class when I'm trying to use this function in the Transformation.switchMap, Im getting compile time error that the method getCurrentWeatherForecasts(String ) cannot be applied to getCurrentWeatherForecasts().
Here's my code in ViewModel class:
private final SunshineRepository mRepository;
public LiveData<List<ListWeatherEntry>> mForecast;
private final MutableLiveData<String> cityName = new MutableLiveData();
public MainActivityViewModel(SunshineRepository repository) {
this.mRepository = repository;
mForecast = Transformations.switchMap(this.cityName,(city)->
mRepository.getCurrentWeatherForecasts(city));
}
I've read Android's Transformations.switchMap docs, but I couldn't figure out what am I doing wrong.
Can anyone explain me what's wrong with my code.
I'm trying to implement a simple App using Architecture Components.
I can get the info from RestApi services using Retrofit2.
I can show the info in the respective Recyclerview and when I rotate the phone everything works as it should.
Now I want to filter by a new kind of object (by string)
Can someone guide me a little with the ViewModel, I don't know what is the best practice to do that...
I'm using MVVM...
This is my ViewModel:
public class ListItemViewModel extends ViewModel {
private MediatorLiveData<ItemList> mList;
private MeliRepository meliRepository;
/* Empty Contructor.
* To have a ViewModel class with non-empty constructor,
* I have to create a Factory class which would create instance of you ViewModel and
* that Factory class has to implement ViewModelProvider.Factory interface.
*/
public ListItemViewModel(){
meliRepository = new MeliRepository();
}
public LiveData<ItemList> getItemList(String query){
if(mList == null){
mList = new MediatorLiveData<>();
LoadItems(query);
}
}
private void LoadItems(String query){
String queryToSearch = TextUtils.isEmpty(query) ? "IPOD" : query;
mList.addSource(
meliRepository.getItemsByQuery(queryToSearch),
list -> mList.setValue(list)
);
}
}
UPDATE
I resolved this using transformation a package from lifecycle library...
enter link description here
public class ListItemViewModel extends ViewModel {
private final MutableLiveData<String> mQuery = new MutableLiveData<>();
private MeliRepository meliRepository;
private LiveData<ItemList> mList = Transformations.switchMap(mQuery, text -> {
return meliRepository.getItemsByQuery(text);
});
public ListItemViewModel(MeliRepository repository){
meliRepository = repository;
}
public LiveData<ItemList> getItemList(String query){
return mList;
}
}
#John this is my solution. I'm using lifecycle library and the solution was easier than I thought. Thx!
I'm more familiar with doing this in Kotlin but you should be able to translate this to Java easily enough (or perhaps now is a good time to start using Kotlin :) )....adapting similar pattern I have here I believe you'd do something like:
val query: MutableLiveData<String> = MutableLiveData()
val mList = MediatorLiveData<List<ItemList>>().apply {
this.addSource(query) {
this.value = meliRepository.getItemsByQuery(query)
}
}
fun setQuery(q: String) {
query.value = q
}
I'm using this pattern in following https://github.com/joreilly/galway-bus-android/blob/master/app/src/main/java/com/surrus/galwaybus/ui/viewmodel/BusStopsViewModel.kt
I have developed app base on android data binding library: https://developer.android.com/topic/libraries/data-binding/index.html
class SignInViewModel extends BaseObservable {
#Bindable
public String getLogin() {
return login;
}
#Bindable
public String getPassword() {
return password;
}
}
and now I want to use ViewModelProviders from new library:
https://developer.android.com/topic/libraries/architecture/guide.html
SignInViewModel signInViewModel = ViewModelProviders.of(this).get(SignInViewModel.class);
How it combine? any idea? or should be combined these two libraries?
Edit
I change to:
class SignInViewModel extends ViewModel {
public ObservableField<String> login = new ObservableField<>("");
public ObservableField<String> password = new ObservableField<>("");
}
and now compiles, but question is: is it right way?
It's a known incompatibility. You can't extend BaseObservable and AndroidViewModel at the same time, so you can't use #Bindable making two-way data binding impossible*.
This will be fixed after arch components 1.0 final (on the data binding side).
*Edit: You can make your own ObservableViewModel: https://gist.github.com/JoseAlcerreca/4b66f9953d50b483d80e6b9ad7172685
Maybe this didn't exist when when the question was asked, but there is another option explained in this article: https://medium.com/google-developers/android-data-binding-observability-9de4ff3fe038
Basically instead of extending from BaseObservable you can implement android.databinding.Observable.
It's slightly more work as you need to also do the following:
Create this variable in your model class
private PropertyChangeRegistry registry = new PropertyChangeRegistry();
Implement the overriden methods like this
#Override
public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
registry.add(callback);
}
#Override
public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
registry.remove(callback);
}
Replace all the "BR" calls with these:
registry.notifyChange(this, BR.bar);
Everything else works the same as extending from BaseObservable. So I think maybe this is the solution that Jose might have been alluding to which probably wasn't available back then. It seems to work.
Update: As Eugene Brusov has mentioned, you can now use LiveData with data binding. This is what I'm doing now and it's much easier with less boilerplate. See https://developer.android.com/topic/libraries/data-binding/architecture.
It's possible with Android Studio 3.1 Canary 6 (https://androidstudio.googleblog.com/2017/12/android-studio-31-canary-6-is-now.html):
You can now use a LiveData object as an observable field in data binding expressions. The ViewDataBinding class now includes a new setLifecycle method that you need to use to use to observe LiveData objects.
You can find more details and sample in this Medium post.
This can be also solved using a wrapper:
class SignInViewModelWrapper extends ViewModel {
public final SignInViewModel model = new SignInViewModel();
}
class SignInViewModel extends BaseObservable {
#Bindable
public String getLogin() {
return login;
}
#Bindable
public String getPassword() {
return password;
}
}
You can then get the view model like this:
SignInViewModel signInViewModel = ViewModelProviders.of(this).get(SignInViewModelWrapper.class).model;