java - DataBindig 向布局返回错误的 onFocusChangeListener
问题描述
我有一个登录表单验证机制。电子邮件和密码有两个 TextInputEditText,它们都附加到各自的 onFocusChangeListeners,DataBinding 用于设置它们,但 DataBinding 返回错误的侦听器。
这是表格:
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="loginViewModel"
type="com.example.increaseyouriq.ui.forms.LoginFormViewModel" />
</data>
<LinearLayout
android:id="@+id/ll_login"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/parent.contentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="26dp">
<com.google.android.material.card.MaterialCardView
style="@style/cardOutline"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
style="@style/viewParent.headerText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_title" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/textInputStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/enter_email"
error="@{loginViewModel.loginForm.emailError}"
app:errorEnabled="@{loginViewModel.loginForm.isEmailError()}"
app:endIconMode="clear_text">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_login_email"
style="@style/textInputEditext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
onFocus="@{loginViewModel.onFocusEmailChangeListener}"
android:text="@={loginViewModel.loginFields.email}"
android:inputType="textNoSuggestions|textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/textInputStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/enter_password"
error="@{loginViewModel.loginForm.passwordError}"
app:errorEnabled="@{loginViewModel.loginForm.isPasswordError()}"
app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText
style="@style/textInputEditext"
android:id="@+id/et_login_password"
onFocus="@{loginViewModel.onFocusPasswordChangeListener}"
android:text="@={loginViewModel.loginFields.password}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</layout>
这是我的LoginFormViewModel.java:
public class LoginFormViewModel extends ViewModel {
private static final String LOG_TAG = "LoginFormViewModel";
private View.OnFocusChangeListener onFocusEmail;
private View.OnFocusChangeListener onFocusPassword;
public void init() {
loginForm = new LoginForm();
onFocusPassword = (v, hasFocus) -> {
TextInputEditText et = (TextInputEditText) v;
MyUtilsApp.showLog(LOG_TAG,
String.format("%s password listener attached to :%d", String.valueOf(et.getText()), et.getId()));
if (et.getText().length() > 0 && !hasFocus) {
loginForm.isPasswordValid(true);
}
};
onFocusEmail = (v, hasFocus) -> {
TextInputEditText et = (TextInputEditText) v;
MyUtilsApp.showLog(LOG_TAG, "email listener attached");
if (et.getText().length() > 0 && !hasFocus) {
loginForm.isEmailValid(true);
}
};
}
public View.OnFocusChangeListener getOnFocusEmailChangeListener() {
return onFocusEmail;
}
public View.OnFocusChangeListener getOnFocusPasswordChangeListener() {
return onFocusPassword;
}
@BindingAdapter("onFocus")
public static void bindFocusChange(TextInputEditText editText, View.OnFocusChangeListener onFocusChangeListener) {
if (editText.getOnFocusChangeListener() == null) {
editText.setOnFocusChangeListener(onFocusChangeListener);
}
}
}
这是我的LoginActivity.java:
public class LoginActivity extends AppCompatActivity {
private static final String LOG_TAG = "LoginActivity";
ActivityLoginBinding binding;
private LoginRegisterViewModel loginRegisterViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_login);
setupBindings(savedInstanceState);
}
private void setupBindings(Bundle savedInstanceState) {
LoginFormViewModel loginFormViewModel = new ViewModelProvider(this).get(LoginFormViewModel.class);
if(savedInstanceState==null)
loginFormViewModel.init();
binding.setLoginViewModel(loginFormViewModel);
binding.setLifecycleOwner(this);
loginFormViewModel.getLoginFields().observe(this, loginFields -> {
Toast.makeText(LoginActivity.this,
"Email " + loginFields.getEmail() + ", Password " + loginFields.getPassword(),
Toast.LENGTH_SHORT).show();
});
binding.inLayoutLogin.btnLoginLogin.setOnClickListener(view -> {
binding.executePendingBindings();
loginFormViewModel.getLoginForm().onClick();
});
}
}
很明显,我已将两个侦听器都设置为 onFocusEmail 类型,因为 onFocusPassword 在运行时被附加到两个 TextInputEditTexts。以下是日志:
com.example.increaseyouriq E/LoginFormViewModel: password listener attached to :2131296499
com.example.increaseyouriq E/LoginFormViewModel: ttf password listener attached to :2131296499
onFocusEmail 无处可见,它已在运行时附加。有关代码的更多详细信息,请参见此处
解决方案
我对你的代码做了一些实验,我相信我找到了根本原因。
问题:
您的两个听众都没有附加到您的电子邮件字段(TextInputEditText“et_login_email”)。
原因:
原因是您在这里有一个空检查;
@BindingAdapter("onFocus")
public static void bindFocusChange(TextInputEditText editText, View.OnFocusChangeListener onFocusChangeListener) {
if (editText.getOnFocusChangeListener() == null) {
editText.setOnFocusChangeListener(onFocusChangeListener);
}
}
并且您的电子邮件字段已经有一个由“app:endIconMode="clear_text""属性设置的 onFocusChangeListener。
<com.google.android.material.textfield.TextInputLayout
...
android:hint="@string/enter_email"
..."
app:endIconMode="clear_text">
您不能为电子邮件字段设置侦听器,因为它的侦听器从一开始就不为空,并且当前方法 (bindFocusChange()) 仅在它为空时设置它。
解决方案1:
设置 onFocusChangeListeners 时删除空检查。
解决方案2:
利用数据绑定库的自动方法选择,代码更少。
将自定义属性的名称“onFocus”更改为“onFocusChangeListener”。由于 TextInputEditText 字段对此名称具有匹配的设置方法 (setOnFocusChangeListener),因此库可以自动处理此问题,您可以完全删除“ bindFocusChange(...) ”方法:
推荐阅读
- c# - 如果有的话,我可以改用将所有内容都设为 readkey 吗?我只需要在输入“q”时退出;
- reactjs - 无法为 github.io 投资组合页面选择 src
- mongodb - 如何使用复杂函数对 mongo 进行投影和排序
- javascript - 如何使用react js在另一个页面显示html表单提交的信息?
- sql - ORDER BY 项目必须出现在选择列表中
- java - Liquibase 根据表名前缀在 db 上生成更改日志
- amazon-web-services - 带有日志的 cloudwatch 指标过滤器警报
- python - 如何在 Python 的 discord.Colour 中使用 RGB 代码?
- javascript - npm 请求失败,原因:证书链中的自签名证书
- python - 单击中的所有按钮
- 使用 Selenium Python 循环标记