I want to change UI as LiveData
changes when used with DataBinding. But as the LiveData
is changing it is not reflecting changes in UI.
MainViewModel.java
public class MainViewModel extends AndroidViewModel {
/**
* Boolean variable to see whether user is ClockedIn or ClockedOut
*/
public MutableLiveData<Boolean> isClockedIn;
public MainViewModel(Application application) {
super(application);
isClockedIn = new MutableLiveData<>();
//...
calculateClockedInOrNot(PreferencesManager.getString(mContext,PreferencesManager.CLOCK_STATUS_KEY));
}
/**
* An utility function to calculate whether user is clocked in or clocked out,
* this function is also called within this ViewModel after an api hit
*
* if string == "CLOCKED_OUT" or string == "mobileEventType.CLOCK_OUT" => user is clocked out
* else user is clocked in
*/
private void calculateClockedInOrNot(String string) {
if (string.equals("CLOCKED_OUT") || string.equals("mobileEventType.CLOCK_OUT")) {
//user is clocked out
isClockedIn.setValue(false);
} else {
//user is clocked in
isClockedIn.setValue(true);
}
}
//getter function
public MutableLiveData<Boolean> isClockedIn() {
return isClockedIn;
}
//other function left for brevity
}
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Obtain the ViewModel component.
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
// Inflate view and obtain an instance of the binding class.
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// Assign the component to a property in the binding class.
binding.setData(mainViewModel);
}
activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.app.bidunity.viewmodel.MainViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="@dimen/img_hight_location"
android:adjustViewBounds="true"
android:src="@{data.clockedIn ? @drawable/ic_location_on : @drawable/ic_location_off}"/>
<!--Other view left for brevity-->
</LinearLayout>
</layout>
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.app.bidunity"
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}
dependencies {
def arch_version = "2.0.0-rc01"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
//material library
implementation 'com.google.android.material:material:1.0.0'
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$arch_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}
I am using Android studio 3.3 Canary 11 with gradle 4.10.1 and onclick
with databinding is working fine within viewmodel, but even after changing isClockedIn
the ImageView
is not changing it's drawable. I have tried everything, I even debugged to see if isClockedIn
is changing or not in middle of the run, and yes it's value is changing
You need to make your isClockedIn
variable Observable Field,
So replace it from
public boolean isClockedIn = false;
to
public ObservableBoolean isClockedIn = new ObservableBoolean();
Note : also make sure that xml has the same variable name as in your ViewModel
Refer here
Edit:
If you don't want to make ObservableField, then it'll take some other changes.
Inspite of ObservableField, you'll need to make your observable extend BaseObservable
and in your livedata you'll required to call notifyPropertyChanged(BR.isClockedIn);
after boolean value change.
If you want to use LiveData as observer, first is to provide your binding a life cycle
// Specify the current activity as the lifecycle owner (in your activity/fragment).
binding.setLifecycleOwner(this);
In your ViewModel, take LiveData object to observe change and apply Transformation on it:
MutableLiveData<Boolean> dataToBeObserve = new MutableLiveData<>();
LiveData booleanDataChange = Transformations.map(dataToBeObserve, dataToBeObserve-> dataToBeObserve.value);
Now, use this booleanDataChange
to xml and change your MutableLiveData value to observe data change event.
Note: You can use same LiveData for change events, just need to split defination to constructor call.