我可以将错误消息绑定到TextInputLayout吗?

我想直接将错误消息绑定到android.support.design.widget.TextInputLayout 。 我找不到通过布局设置错误的方法。 这甚至有可能吗?

这是我想象的工作方式:

 <?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> 

Solutions Collecting From Web of "我可以将错误消息绑定到TextInputLayout吗?"

没有对应于setError()方法的XML属性,所以不能直接在XML中设置错误消息(这有点奇怪,因为errorEnabled在那里)。 但是,这可以通过创buildBinding Adapter来为您解决。 就是这样

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

请参阅官方绑定文档 ,“属性设置器”部分,特别是“自定义设置器”

编辑

可能是愚蠢的问题,但我应该把这个呢? 我是否需要扩展TextInputLayout并把它放在那里?

这实际上不是一个愚蠢的问题,只是因为你不能通过阅读官方文档得到完整的答案。 幸运的是,它非常简单:您不需要扩展任何东西 – 只需将该方法放在项目中的任何位置即可 。 您可以创build单独的类(即BindingTools.java ),或者只是将此方法添加到项目中的任何现有类。 只要你用@BindingAdapter注解这个方法,确保它是public static它不pipe它在哪个类中。

我已经做了一个绑定就像我的答案如何使用DataBinding Framwork MVVM在EditText上设置错误 。 但是这一次,它使用TextInputLayout作为示例,就像之前那样。

这个想法的目的是:

  1. 使XML尽可能可读和独立
  2. 独立进行活动端validation和xml端validation

当然,你可以让你自己进行validation,并使用xml中的<variable>标记进行设置

首先实现静态绑定方法和相关的Stringvalidation规则。

捆绑

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

其次,将默认validation和错误消息放在TextInputLayout中,并使其容易知道validation,绑定在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> 

第三,当点击button触发时,您可以使用TextInputLayout中的预定义ID(例如,imageUrlValidation)对活动进行最终validation

 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_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://sofzh.miximages.com/android/DvpvklR.jpg"); 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(); }); } }