web-dev-qa-db-fra.com

Puis-je lier un message d'erreur à un TextInputLayout?

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>
24
Theyouthis

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.

44
Marcin Orlowski

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:

  1. Rendre le xml aussi lisible que possible et indépendant
  2. Rendre la validation côté activité et la validation côté xml indépendamment

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();

    });
  }
}
4
Long Ranger