我正在使用Android数据绑定框架我假设一个EditText用于登录表单,用户名如下
<EditText
android:id="@+id/etext_uname"
style="@style/login_edittext"
android:hint="@string/hint_username"
android:inputType="textEmailAddress" />
我也定义了LoginViewModel但我需要帮助如何在用户输入错误的电子邮件地址时在edittext中设置错误在某些情况下让我们说内部
public void afterTextChanged(@NonNull final Editable editable)
因为据我所知,在传统Android方法中,我们可以通过et.setError()方法以编程方式执行此操作,但我不想通过Activity或Fragment创建edittext对象。
答案 0 :(得分:6)
如果你想用数据绑定做EditText.setError()
函数,这里有两种方法。
方法1
使用从数据绑定(https://developer.android.com/topic/libraries/data-binding/index.html#views_with_ids)
生成的最终EditText视图您可以直接调用EditText而无需手动创建,因为在为视图设置ID后会自动生成它(对于包含的布局也是如此)。
MainActivityBinding.etext_uname.setError("Wrong email format");
或者
MainActivityBinding.etext_uname.addTextChangedListener(new MyOwnTextWatcher());
方法2
如果你想像乔治亚提到的那样使用xml的绑定方法(https://medium.com/google-developers/android-data-binding-custom-setters-55a25a7aea47#.su88ujqrn)
首先,您必须设置自己的绑定方法。建议为所有绑定方法创建另一个类。
方法必须是静态的,使用@BindingAdapter注释和相应的绑定方法名称(可以自定义命名空间和方法名称)
<强> 1。设置自定义TextWatcher
public class MyOwnBindingUtil {
public interface StringRule {
public boolean validate(Editable s);
}
@BindingAdapter("android:watcher")
public static void bindTextWatcher(EditText pEditText, TextWatcher pTextWatcher) {
pEditText.addTextChangedListener(pTextWatcher);
}
@BindingAdapter(value = {"email:rule", "email:errorMsg"}, requireAll = true)
public static void bindTextChange(final EditText pEditText, final StringRule pStringRule, final String msg) {
pEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (!pStringRule.validate(s)) {
pEditText.setError(msg);
}
}
});
}
/*
Your other custom binding method
*/
}
如果要使用自定义操作设置自己的TextWatcher,如显示的Toast,则显示Dialog。你应该使用“android:watcher”方法
mBinding.setWatcher(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
在xml中,
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:email="http://schemas.android.com/tools"
>
<data>
<variable
name="watcher"
type="android.text.TextWatcher"/>
<variable
name="emailRule"
type="example.com.testerapplication.MyOwnBindingUtil.StringRule"/>
<variable
name="errorMsg"
type="java.lang.String"/>
</data>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Input Email"
android:watcher="@{watcher}
/>
<强> 2。设置您自己的验证规则和错误消息
如果你想使用setError函数,只留下errorMsg和验证逻辑进行自定义。您可以像下面那样设置xml。
在xml中,
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:email="http://schemas.android.com/tools"
>
<data>
<variable
name="watcher"
type="android.text.TextWatcher"/>
<variable
name="emailRule"
type="example.com.testerapplication.MyOwnBindingUtil.StringRule"/>
<variable
name="errorMsg"
type="java.lang.String"/>
</data>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Input Email"
email:rule="@{emailRule}"
email:errorMsg="@{errorMsg}"
/>
活动代码
mBinding.setErrorMsg("Wrong type");
mBinding.setEmailRule(new MyOwnBindingUtil.StringRule() {
@Override
public boolean validate(Editable s) {
// check if the length of string is larger than 18
return s.toString().length() > 18;
}
});
请随意编辑我的代码,以使开发人员使用绑定更通用。
答案 1 :(得分:2)
从根本上说,您需要一种方法来实现依赖字段。错误取决于文本的值。您希望在文本更改时更新错误值。
我找到了两种方法来实现这一目标:
<EditView
android:text="@={viewModel.email}"
android:error="@={viewModel.emailRule.check(email)} />
数据绑定可确保在check
更改时调用email
函数。
我编写了一个实用程序,可以在ObservableField
和Observable
之间进行转换。见FieldUtils.java
使用它,您可以在ViewModel / Model代码中实现。
public class ViewModel {
ObservableField<String> email = new ObservableField<>();
ObservableField<String> emailError = toField(toObservable(email).map(new Func1<String, String>() {
@Override
public String call(String email) {
return FormUtils.checkEmail(email) ? null : "Invalid Email";
}
}));
}
EditText会在用户输入时清除错误。 Data Binding期望在调用setter之后保留属性的值。因此,如果值没有改变,它不会再次调用setter。因此,只要键入,如果计算的错误值相同,数据绑定将不会调用setter,因此错误将消失。这种使error
属性与数据绑定不兼容。
我更喜欢使用设计库提供的TextInputLayout。它有一个持久的错误字段,看起来也更好。
答案 2 :(得分:2)
我只想分享我对Long Ranger的答案的修改,用于android arch viewModel:
public class StringValidationRules {
public static StringRule NOT_EMPTY = new StringRule() {
@Override
public boolean validate(Editable s) {
return TextUtils.isEmpty(s.toString());
}
};
public static StringRule EMAIL = new StringRule() {
@Override
public boolean validate(Editable s) {
return !android.util.Patterns.EMAIL_ADDRESS.matcher(s).matches();
}
};
public static StringRule PASSWORD = new StringRule() {
@Override
public boolean validate(Editable s) {
return s.length() < 8;
}
};
public interface StringRule {
boolean validate(Editable s);
}
}
viewModel ...
public class LoginViewModel extends ViewModel {
...
@BindingAdapter({"app:validation", "app:errorMsg"})
public static void setErrorEnable(EditText editText, StringValidationRules.StringRule stringRule, final String errorMsg) {
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (stringRule.validate(editText.getText())) {
editText.setError(errorMsg);
} else {
editText.setError(null);
}
}
});
}
...
和XML:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:bind="http://schemas.android.com/apk/res-auto"
>
<data>
<variable name="viewModel" type="com.fernandonovoa.sapmaterialstockoverview.login.LoginViewModel"/>
<import type="com.fernandonovoa.sapmaterialstockoverview.utils.StringValidationRules" />
</data>
...
<EditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Ingrese su email"
android:inputType="textEmailAddress"
android:drawableLeft="@drawable/ic_email"
android:drawableStart="@drawable/ic_email"
app:validation="@{StringValidationRules.EMAIL}"
app:errorMsg='@{"Email no válido"}'
style="@style/AppTheme.Widget.TextInputLayoutLogin"
/>
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Ingrese su contraseña"
android:inputType="textPassword"
android:drawableLeft="@drawable/ic_lock"
android:drawableStart="@drawable/ic_lock"
app:validation="@{StringValidationRules.PASSWORD}"
app:errorMsg='@{"Contraseña no válida"}'
style="@style/AppTheme.Widget.TextInputLayoutLogin"
/>
答案 3 :(得分:0)
您还可以在这样的编辑文本上添加验证。
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.app.ui.login.LoginViewModel" />
<import type="com.example.app.ui.ValidationRule" />
<variable
name="watcher"
type="android.text.TextWatcher" />
<import type="com.example.app.utils.ValidationUtils" />
</data>
<RelativeLayout
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".ui.login.LoginFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="username"
android:watcher="@{watcher}"
app:error="@{@string/validation_error_msg_email}"
app:rule="@{ValidationRule.EMPTY}">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={viewModel.usernameObs}" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="password"
android:watcher="@{watcher}"
app:error="@{@string/validation_error_msg_password}"
app:rule="@{ValidationRule.PASSWORD}">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:text="@={viewModel.passwordObs}" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:background="?colorAccent"
android:enabled="@{ValidationUtils.isValidEmail(viewModel.usernameObs) && ValidationUtils.isValidPassword(viewModel.passwordObs)}"
android:onClick="@{() -> viewModel.login()}"
android:text="Login"
android:textColor="?android:textColorPrimaryInverse" />
</LinearLayout>
</RelativeLayout>
</layout>
BindingUtils
object BindingUtils {
@BindingAdapter(value = ["error", "rule", "android:watcher"], requireAll = true)
@JvmStatic
fun watcher(textInputLayout: com.google.android.material.textfield.TextInputLayout, errorMsg: String, rule: ValidationRule, watcher: TextWatcher) {
textInputLayout.editText?.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
textInputLayout.error = null
if (rule == ValidationRule.EMPTY && !ValidationUtils.isValidEmail(p0.toString())) textInputLayout.error = errorMsg
if (rule == ValidationRule.PASSWORD && !ValidationUtils.isValidPassword(p0.toString())) textInputLayout.error = errorMsg
}
})
}
}
ValidationRule
enum class ValidationRule{
EMPTY, EMAIL, PASSWORD
}
不要忘记将观察者设置为这样的片段或活动
binding.watcher = object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
}