Can I associate an error message with TextInputLayout?

I want to associate the error message directly with android.support.design.widget.TextInputLayout . I can not find a way to fix the error through the layout. Is it possible?

This is how I thought it worked:

 <?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> 
+7
android android-layout android-databinding
source share
2 answers

There is no XML attribute corresponding to the setError() method, so you cannot set the error message directly in XML (this bit is an odd knowledge of errorEnabled ). But this can easily be solved by creating a Binding Adapter that would install this for you. Something like that.

 @BindingAdapter("app:errorText") public static void setErrorMessage(TextInputLayout view, String errorMessage) { view.setError(errorMessage); } 

See official binding documents , Attributes section, especially Custom Setters

EDIT

Perhaps a dumb question, but where should I put this? Do I need to extend TextInputLayout and put it there?

This is actually not a stupid question, simply because you cannot get the full answer by reading the official documentation. Fortunately, it’s pretty simple: you don’t have to extend anything - just put this method anywhere in your projects. You can create a separate class (i.e. BindingTools.java ) or simply add this method to any existing class in your project. While you are commenting on this method with @BindingAdapter , make sure that it is public and static does not matter in which class it lives.

+18
source share

I made the binding as my answer to How to set an error in EditText using DataBinding Framwork MVVM . But this time, he used TextInputLayout as a sample, like the previous one.

The goals of this idea are:

  • Make xml as convenient and independent as possible
  • Regardless of action-side validation and xml-side validation

Of course you can do your own validation and set it using the <variable> in xml

Firstly, it implements the static method of binding and the corresponding rules for checking the string for preparation.

Snap

  @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); } 

Secondly, put the default error message and error message in TextInputLayout and simplify checking it, binding in 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> 

Thirdly, when you click the button, you can use the predefined identifier in TextInputLayout (for example, imageUrlValidation) to perform a final activity check

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

The full code is as follows:

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(); }); } } 
0
source share

All Articles