Je voudrais lier un message d'erreur directement à un Android.support.design.widget.TextInputLayout
. Je ne trouve pas de moyen de régler l'erreur via la mise en page. Est-ce seulement possible?
Voici comment je l'imaginais fonctionner:
<?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>
<import type="Android.view.View" />
<variable
name="error"
type="String" />
</data>
<Android.support.v7.widget.LinearLayoutCompat
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:orientation="vertical">
<Android.support.design.widget.TextInputLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:errorEnabled="true"
app:errorText="@{error}">
<EditText
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:hint="@string/username"
Android:inputType="textEmailAddress" />
</Android.support.design.widget.TextInputLayout>
</Android.support.v7.widget.LinearLayoutCompat>
</layout>
Au moment d'écrire cette réponse, il n'y a pas d'attribut XML correspondant à la méthode setError()
, vous ne pouvez donc pas définir de message d'erreur directement dans votre XML, ce qui est un peu étrange sachant que errorEnabled
est là. Mais cette omission peut être facilement corrigée en créant Binding Adapter qui comblerait le vide et fournirait l'implémentation manquante. Quelque chose comme ça:
@BindingAdapter("app:errorText")
public static void setErrorMessage(TextInputLayout view, String errorMessage) {
view.setError(errorMessage);
}
Voir documents de liaison officiels , section "Attributs Setters" en particulier "Custom Setters".
[~ # ~] modifier [~ # ~]
Question peut-être stupide, mais où dois-je mettre cela? Dois-je étendre
TextInputLayout
et le mettre là-dedans?
Ce n'est pas du tout une question stupide, simplement parce que vous ne pouvez pas obtenir une réponse complète en lisant la documentation officielle. Heureusement, c'est assez simple: vous n'avez pas besoin d'étendre quoi que ce soit - il suffit de mettre cette méthode n'importe où dans vos projets. Vous pouvez créer une classe distincte (c'est-à-dire DataBindingAdapters
) ou simplement ajouter cette méthode à n'importe quelle classe existante de votre projet - cela n'a pas vraiment d'importance. Tant que vous annotez cette méthode avec @BindingAdapter
, assurez-vous qu'il s'agit de public
et static
peu importe la classe dans laquelle il vit.
J'ai fait une liaison comme ma réponse sur Comment définir une erreur sur EditText en utilisant DataBinding Framwork MVVM . Mais cette fois, il a utilisé TextInputLayout comme exemple, comme le précédent.
Objectifs de cette idée:
Bien sûr, vous pouvez créer votre propre validation et la définir à l'aide du <variable>
tag en xml
Premièrement, implémente la méthode de liaison statique et les règles de validation de chaîne associées pour la préparation.
Contraignant
@BindingAdapter({"app:validation", "app:errorMsg"})
public static void setErrorEnable(TextInputLayout textInputLayout, StringRule stringRule,
final String errorMsg) {
}
StringRule
public static class Rule {
public static StringRule NOT_EMPTY_RULE = s -> TextUtils.isEmpty(s.toString());
public static StringRule EMAIL_RULE = s -> s.toString().length() > 18;
}
public interface StringRule {
boolean validate(Editable s);
}
Deuxièmement, placez la validation par défaut et le message d'erreur dans TextInputLayout et simplifiez la connaissance de la validation, en liant en xml
<Android.support.design.widget.TextInputLayout
Android:id="@+id/imageUrlValidation"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:validation="@{Rule.NOT_EMPTY_RULE}"
app:errorMsg='@{"Cannot be empty"}'
>
<Android.support.design.widget.TextInputEditText
Android:id="@+id/input_imageUrl"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:hint="Image Url"
Android:text="@={feedEntry.imageUrl}" />
</Android.support.design.widget.TextInputLayout>
Troisièmement, lorsque le déclencheur du bouton de clic se déclenche, vous pouvez utiliser l'ID prédéfini dans TextInputLayout (par exemple, imageUrlValidation) pour effectuer la validation finale de l'activité
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(view1 -> {
// TODO Do something
//to trigger auto error enable
FeedEntry inputFeedEntry = dialogFeedEntryBinding.getFeedEntry();
Boolean[] validations = new Boolean[]{
dialogFeedEntryBinding.imageUrlValidation.isErrorEnabled(),
dialogFeedEntryBinding.titleValidation.isErrorEnabled(),
dialogFeedEntryBinding.subTitleValidation.isErrorEnabled()
};
boolean isValid = true;
for (Boolean validation : validations) {
if (validation) {
isValid = false;
}
}
if (isValid) {
new AsyncTask<FeedEntry, Void, Void>() {
@Override
protected Void doInBackground(FeedEntry... feedEntries) {
viewModel.insert(feedEntries);
return null;
}
}.execute(inputFeedEntry);
dialogInterface.dismiss();
}
});
Le code complet est le suivant:
dialog_feedentry.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">
<data>
<import type="com.example.common.components.TextInputEditTextBindingUtil.Rule" />
<variable
name="feedEntry"
type="com.example.feedentry.repository.bean.FeedEntry" />
</data>
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:padding="10dp"
Android:orientation="vertical">
<Android.support.design.widget.TextInputLayout
Android:id="@+id/imageUrlValidation"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:validation="@{Rule.NOT_EMPTY_RULE}"
app:errorMsg='@{"Cannot be empty"}'
>
<Android.support.design.widget.TextInputEditText
Android:id="@+id/input_imageUrl"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:hint="Image Url"
Android:text="@={feedEntry.imageUrl}" />
</Android.support.design.widget.TextInputLayout>
<Android.support.design.widget.TextInputLayout
Android:id="@+id/titleValidation"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:validation="@{Rule.NOT_EMPTY_RULE}"
app:errorMsg='@{"Cannot be empty"}'
>
<Android.support.design.widget.TextInputEditText
Android:id="@+id/input_title"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:hint="Title"
Android:text="@={feedEntry.title}"
/>
</Android.support.design.widget.TextInputLayout>
<Android.support.design.widget.TextInputLayout
Android:id="@+id/subTitleValidation"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:validation="@{Rule.NOT_EMPTY_RULE}"
app:errorMsg='@{"Cannot be empty"}'
>
<Android.support.design.widget.TextInputEditText
Android:id="@+id/input_subtitle"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:hint="Subtitle"
Android:text="@={feedEntry.subTitle}"
/>
</Android.support.design.widget.TextInputLayout>
</LinearLayout>
</layout>
TextInputEditTextBindingUtil.Java
package com.example.common.components;
import Android.databinding.BindingAdapter;
import Android.support.design.widget.TextInputLayout;
import Android.text.Editable;
import Android.text.TextUtils;
import Android.text.TextWatcher;
/**
* Created by Charles Ng on 7/9/2017.
*/
public class TextInputEditTextBindingUtil {
@BindingAdapter({"app:validation", "app:errorMsg"})
public static void setErrorEnable(TextInputLayout textInputLayout, StringRule stringRule,
final String errorMsg) {
textInputLayout.getEditText().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) {
textInputLayout
.setErrorEnabled(stringRule.validate(textInputLayout.getEditText().getText()));
if (stringRule.validate(textInputLayout.getEditText().getText())) {
textInputLayout.setError(errorMsg);
} else {
textInputLayout.setError(null);
}
}
});
textInputLayout
.setErrorEnabled(stringRule.validate(textInputLayout.getEditText().getText()));
if (stringRule.validate(textInputLayout.getEditText().getText())) {
textInputLayout.setError(errorMsg);
} else {
textInputLayout.setError(null);
}
}
public static class Rule {
public static StringRule NOT_EMPTY_RULE = s -> TextUtils.isEmpty(s.toString());
public static StringRule EMAIL_RULE = s -> s.toString().length() > 18;
}
public interface StringRule {
boolean validate(Editable s);
}
}
FeedActivity.Java
public class FeedActivity extends AppCompatActivity {
private FeedEntryListViewModel viewModel;
@SuppressLint("StaticFieldLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feed);
viewModel = ViewModelProviders.of(this)
.get(FeedEntryListViewModel.class);
viewModel.init(this);
ViewPager viewPager = findViewById(R.id.viewpager);
setupViewPager(viewPager);
// Set Tabs inside Toolbar
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(view -> {
//insert sample data by button click
final DialogFeedentryBinding dialogFeedEntryBinding = DataBindingUtil
.inflate(LayoutInflater.from(this), R.layout.dialog_feedentry, null, false);
FeedEntry feedEntry = new FeedEntry("", "");
feedEntry.setImageUrl("http://i.imgur.com/DvpvklR.png");
dialogFeedEntryBinding.setFeedEntry(feedEntry);
final Dialog dialog = new AlertDialog.Builder(FeedActivity.this)
.setTitle("Create a new Feed Entry")
.setView(dialogFeedEntryBinding.getRoot())
.setPositiveButton("Submit", null)
.setNegativeButton("Cancel", (dialogInterface, i) -> dialogInterface.dismiss())
.create();
dialog.setOnShowListener(dialogInterface -> {
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(view1 -> {
// TODO Do something
//to trigger auto error enable
FeedEntry inputFeedEntry = dialogFeedEntryBinding.getFeedEntry();
Boolean[] validations = new Boolean[]{
dialogFeedEntryBinding.imageUrlValidation.isErrorEnabled(),
dialogFeedEntryBinding.titleValidation.isErrorEnabled(),
dialogFeedEntryBinding.subTitleValidation.isErrorEnabled()
};
boolean isValid = true;
for (Boolean validation : validations) {
if (validation) {
isValid = false;
}
}
if (isValid) {
new AsyncTask<FeedEntry, Void, Void>() {
@Override
protected Void doInBackground(FeedEntry... feedEntries) {
viewModel.insert(feedEntries);
return null;
}
}.execute(inputFeedEntry);
dialogInterface.dismiss();
}
});
});
dialog.show();
});
}
}