J'utilise Android Framework de liaison de données, je suppose un EditText pour le formulaire de connexion avec le nom d'utilisateur comme ci-dessous
<EditText
Android:id="@+id/etext_uname"
style="@style/login_edittext"
Android:hint="@string/hint_username"
Android:inputType="textEmailAddress" />
J'ai également défini LoginViewModel mais j'ai besoin d'aide pour définir l'erreur dans edittext lorsque l'utilisateur saisit une mauvaise adresse e-mail dans certains cas, disons à l'intérieur
public void afterTextChanged(@NonNull final Editable editable)
Parce que pour autant que je sache dans l'approche traditionnelle Android, nous pouvons le faire par programmation via la méthode et.setError () mais je ne veux pas créer d'objet edittext via Activity ou Fragment.
Si vous voulez faire quelque chose comme la fonction EditText.setError()
avec la liaison de données, voici deux méthodes.
Méthode 1
Utilisé la vue EditText finale générée à partir de la liaison de données ( https://developer.Android.com/topic/libraries/data-binding/index.html#views_with_ids )
Vous pouvez appeler le EditText directement sans le créer manuellement car il est généré automatiquement après avoir défini l'ID de la vue (également vrai pour la disposition incluse) .
MainActivityBinding.etext_uname.setError("Wrong email format");
Ou
MainActivityBinding.etext_uname.addTextChangedListener(new MyOwnTextWatcher());
Méthode 2
Si vous souhaitez utiliser la méthode de liaison avec xml comme George l'a mentionné ( https://medium.com/google-developers/Android-data-binding-custom-setters-55a25a7aea47#.su88ujqrn )
Vous devez d'abord définir votre propre méthode de reliure. Suggérez de créer une autre classe pour toute la méthode de liaison.
La méthode doit être statique, avec l'annotation @BindingAdapter et le nom de la méthode de liaison correspondante (l'espace de noms et le nom de la méthode peuvent être personnalisés)
1. Définissez le TextWatcher personnalisé
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
*/
}
Si vous souhaitez configurer votre propre TextWatcher avec une action personnalisée, comme Toast montré, Dialogue montré. Vous devez utiliser la méthode "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) {
}
});
En 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. Configurez votre propre règle de validation et message d'erreur
Si vous souhaitez utiliser la fonction setError et ne laisser que le errorMsg et la logique de validation à personnaliser. Vous pouvez définir le xml comme suit.
En 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}"
/>
Code d'activité
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;
}
});
N'hésitez pas à modifier mon code pour que la liaison soit plus générique pour le développeur.
Fondamentalement, vous avez besoin d'un moyen d'implémenter des champs dépendants. L'erreur dépend de la valeur du texte. Vous souhaitez que la valeur d'erreur soit mise à jour lorsque le texte change.
J'ai trouvé deux façons d'y parvenir:
<EditView
Android:text="@={viewModel.email}"
Android:error="@={viewModel.emailRule.check(email)} />
La liaison de données garantit que la fonction check
est invoquée chaque fois que email
est modifié.
J'ai écrit un utilitaire pour convertir entre ObservableField
et Observable
. Voir FieldUtils.Java
En utilisant cela, vous pouvez implémenter dans votre code 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 efface l'erreur lorsque l'utilisateur tape. La liaison de données s'attend à ce que la valeur de l'attribut soit conservée après l'appel de setter. Ainsi, il n'invoque plus le setter si la valeur ne change pas. Par conséquent, dès que vous tapez, si la valeur d'erreur calculée est la même, la liaison de données n'appellera pas setter et, par conséquent, l'erreur disparaîtra. Ce type rend l'attribut error
incompatible avec la liaison de données.
Je préfère utiliser TextInputLayout fourni par la bibliothèque de conception. Il a un champ d'erreur persistant et semble également meilleur.
Je veux juste partager ma modification de la réponse de Long Ranger pour 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);
}
}
la vueModèle ...
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);
}
}
});
}
...
et le 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"
/>
Vous pouvez également ajouter une validation sur le texte de modification comme celui-ci.
Fichier de mise en page
<?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
}
})
}
}
Règle de validation
enum class ValidationRule{
EMPTY, EMAIL, PASSWORD
}
N'oubliez pas de mettre l'observateur en fragment ou en activité comme celle-ci
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) {
}
}