I'm trying to enable/disable a button when 4 EditTexts have more than X chars and email is valid (ignore the xml, still applying styles):
<Button
android:id="@+id/fragment_login_button"
android:fontFamily="@font/montserrat_regular"
android:layout_marginTop="20dp"
android:textColor="@android:color/white"
android:background="@drawable/button_primary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:enabled="@{viewModel.createAccountDetailsValid}"
android:layout_marginRight="20dp"
android:text="create account "
android:onClick="@{(theView) -> handler.onCreateClick(theView, viewModel)}"
app:layout_constraintTop_toBottomOf="@id/fragment_login_companyText"
/>
I got it working using MediatorLiveData with the 4 MutableLiveData that the button depends on, but I find that I'm going against the MVVM standards by doing this, but was the only way it works since MediatorLiveData only allows addSource if has at least 1 observer, have a look at the code:
on my View Model:
//USER DATA
val email: MutableLiveData<String> = MutableLiveData()
val name: MutableLiveData<String> = MutableLiveData()
val surname: MutableLiveData<String> = MutableLiveData()
val company: MutableLiveData<String> = MutableLiveData()
val createAccountDetailsValid: MediatorLiveData<Boolean> = MediatorLiveData()
fun populateMediator(owner: LifecycleOwner) {
createAccountDetailsValid.observe(owner, Observer { })
createAccountDetailsValid.addSource(email) {
createAccountDetailsValid.value = isCreateAccountDetailsValid()
}
createAccountDetailsValid.addSource(name) {
createAccountDetailsValid.value = isCreateAccountDetailsValid()
}
createAccountDetailsValid.addSource(surname) {
createAccountDetailsValid.value = isCreateAccountDetailsValid()
}
createAccountDetailsValid.addSource(company) {
createAccountDetailsValid.value = isCreateAccountDetailsValid()
}
}
private fun isCreateAccountDetailsValid() : Boolean {
if(email.value == null || name.value == null || surname.value == null || company.value == null) return false
return android.util.Patterns.EMAIL_ADDRESS.matcher(email?.value!!).matches() && name.value?.length!! >= 3 && surname.value?.length!! >= 3 && company.value?.length!! >= 3
}
on my fragment:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container,false)
viewModel = ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel::class.java)
binding.viewModel = viewModel
binding.handler = LoginHandler()
binding.setLifecycleOwner(this)
viewModel.setLifecycleOwner(this as LifecycleOwner)
return binding.root
}
Like I said it's working but ViewModel has a reference to View (LifecycleOwner), yes I could put the empty observer on the Fragment and don't need to pass the LifecycleOwner to the ViewModel but still doesn't feel right, maybe I'm being a bit perfectionist here but I bet there's another way of binding directly from the ViewModel instead of having to have to set isEnable observing the MediatorLiveData in the Fragment?
Thanks!
void addSource(@NonNull LiveData– Emanuel Amiguinho Feb 01 '19 at 15:49source, @NonNull f (existing != null) { return; } if (hasActiveObservers()) { e.plug(); } }` Basically, it doesn't have any observers doesn't plug as you can see, so I need to have one for some weird reason.