我的问题是我的数据绑定值仅从我的 MediatorLiveData 获取最新更新passwortStatus
并跳过所有中间值。这会导致奇怪的视图问题,因为并非所有视图都正确更新。
这是一个例子:
当我在编辑文本中键入内容时,我passwordCalculator
会执行并查看是否满足某些特定条件。例如,当我写“1Ab”(数字、BigLetter、SmallLetter)时,passwordStatus livedata
值输出:PasswordStatus.HasDigit、PasswordStatus.HasLowerCase、PasswordStatus.HasUpperCase。
现在的问题是:passwordStatus
在我的片段中观察这个给了我所有上述值(PasswordStatus.HasDigit、PasswordStatus.HasLowerCase、PasswordStatus.HasUpperCase),我只在我的数据绑定变量中得到 PasswortStatus.HasUpperCase。
片段观察
viewModel.passwordCalculator.passwordStatus.observe(viewLifecycleOwner) {
Timber.d("STATE CHANGED TO FRAGMENT $it")
}
/// OUTPUT
"STATE CHANGED TO FRAGMENT PasswordStatus.HasDigit"
"STATE CHANGED TO FRAGMENT PasswordStatus.HasLowerCase"
"STATE CHANGED TO FRAGMENT PasswordStatus.HasUpperCase"
布局内的数据绑定观察
<include
android:id="@+id/has_minimum_letters_indicator"
layout="@layout/registration_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:applied="@{PasswortStateConverter.INSTANCE.convertHasMinimunCharakterToBoolean(viewModel.passwordCalculator.passwordStatus)}"
app:layout_constraintStart_toStartOf="@+id/registration_title"
app:layout_constraintTop_toBottomOf="@+id/registration_title"
app:tText="@{@string/registration_password_policy_minimum_amount}" />
数据绑定状态调试
object PasswortStateConverter {
fun convertHasMinimunCharakterToBoolean(state: PasswordStatus): Boolean {
Timber.d("STATE CHANGED TO $state")
return state is PasswordStatus.HasMinCharackter
}
"STATE CHANGED TO PasswortStatus.NONE" (Default Value instead of PasswortStatus.HasDigit)
"STATE CHANGED TO PasswortStatus.NONE" (Default Value instead of PasswortStatus.HasLowerCase)
"STATE CHANGED TO FRAGMENT PasswordStatus.HasUpperCase" (Getting latest value)
密码计算器
class PasswordCalculator @Inject constructor() : TextWatcher {
private companion object {
private val lowerCasePattern = Pattern.compile("[a-z]")
private val upperCasePattern = Pattern.compile("[A-Z]")
private val digitPattern = Pattern.compile("[0-9]")
private val specialPattern = Pattern.compile("[!@#$%^&*()_=+{}/.<>|\\[\\]~-]")
private const val NOT_ENOUGH_LETTERS = 4
private const val MIN_LETTERS = 8
private const val BEST_AMOUNT_LETTERS = 12
}
private val hasUpperCase = MutableLiveData(false)
private val hasLowerCase = MutableLiveData(false)
private val hasDigit = MutableLiveData(false)
private val hasSpecialCharackter = MutableLiveData(false)
private val hasMoreThanCharacters = MutableLiveData(false)
private val _passwordStatus: MediatorLiveData<PasswordStatus> = MediatorLiveData()
val passwordStatus: LiveData<PasswordStatus> get() = _passwordStatus
private val _passwordStrength: MediatorLiveData<PasswordStrength> = MediatorLiveData()
val passwortStrength: LiveData<PasswordStrength> get() = _passwordStrength
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable?) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (s != null) {
hasUpperCase.value = s.hasUpperCase()
hasLowerCase.value = s.hasLowerCase()
hasDigit.value = s.hasDigit()
hasSpecialCharackter.value = s.hasSpecialCharackter()
hasMoreThanCharacters.value = s.length >= MIN_LETTERS
calculateStrength(s)
}
}
fun isPasswordValid(): Boolean {
return hasUpperCase.value == true &&
hasLowerCase.value == true &&
hasDigit.value == true &&
hasSpecialCharackter.value == true &&
hasMoreThanCharacters.value == true
}
private fun CharSequence.hasLowerCase(): Boolean = lowerCasePattern.matcher(this).find()
private fun CharSequence.hasUpperCase(): Boolean = upperCasePattern.matcher(this).find()
private fun CharSequence.hasDigit(): Boolean = digitPattern.matcher(this).find()
private fun CharSequence.hasSpecialCharackter(): Boolean = specialPattern.matcher(this).find()
private fun postStatus(
bool: Boolean,
positiveStatus: PasswordStatus,
negativeStatus: PasswordStatus,
) {
when (bool) {
true -> _passwordStatus.value = positiveStatus
false -> _passwordStatus.value = negativeStatus
}
}
private fun calculateStrength(charSequence: CharSequence) {
when {
charSequence.length in NOT_ENOUGH_LETTERS until MIN_LETTERS ->
_passwordStrength.value = PasswordStrength.BAD
charSequence.length < MIN_LETTERS ->
_passwordStrength.value = PasswordStrength.NONE
charSequence.length >= BEST_AMOUNT_LETTERS && charSequence.hasSpecialCharackter() ->
_passwordStrength.value = PasswordStrength.HIGH
charSequence.length >= MIN_LETTERS ->
_passwordStrength.value = PasswordStrength.GOOD
}
}
init {
_passwordStatus.addSource(hasUpperCase) { postStatus(it, PasswordStatus.HasUpperCase, PasswordStatus.HasNoUpperCase) }
_passwordStatus.addSource(hasLowerCase) { postStatus(it, PasswordStatus.HasLowerCase, PasswordStatus.HasNoLowerCase) }
_passwordStatus.addSource(hasDigit) { postStatus(it, PasswordStatus.HasDigit, PasswordStatus.HasNoDigit) }
_passwordStatus.addSource(hasSpecialCharackter) { postStatus(it, PasswordStatus.HasSpecialCharackter, PasswordStatus.HasNoSpecialCharakter) }
_passwordStatus.addSource(hasMoreThanCharacters) { postStatus(it, PasswordStatus.HasMinCharackter, PasswordStatus.HasNotMinCharackter) }
}
}
所以我的最后一个问题是:如何强制数据绑定获取所有中间值(例如 PasswordStatus.HasDigit、PasswordStatus.HasLowerCase、PasswordStatus.HasUpperCase),而不仅仅是最新值(PasswordStatus.HasUpperCase)?