私はAndroidデータバインディングフレームワークを使用しています
<EditText
Android:id="@+id/etext_uname"
style="@style/login_edittext"
Android:hint="@string/hint_username"
Android:inputType="textEmailAddress" />
LoginViewModelも定義しましたが、ユーザーが間違ったメールアドレスを入力したときにedittextでErrorを設定する方法を教えてください
public void afterTextChanged(@NonNull final Editable editable)
伝統的なAndroidアプローチで知る限り、et.setError()メソッドを介してプログラムでこれを行うことができますが、ActivityまたはFragmentを介してedittextオブジェクトを作成したくありません。
EditText.setError()
関数のようなものをデータバインディングで実行したい場合は、2つの方法があります。
方法1
データバインディングから生成された最後のEditTextビューを使用しました( https://developer.Android.com/topic/libraries/data-binding/index.html#views_with_ids )
EditTextは、ビューのIDを設定した後に自動的に生成されるため、手動で作成せずに直接呼び出すことができます(含まれるレイアウトにも当てはまります)。
MainActivityBinding.etext_uname.setError("Wrong email format");
または
MainActivityBinding.etext_uname.addTextChangedListener(new MyOwnTextWatcher());
方法2
Georgeが述べたようにxmlでバインディングメソッドを使用する場合( https://medium.com/google-developers/Android-data-binding-custom-setters-55a25a7aea47#.su88ujqrn )
まず、独自のバインド方法を設定する必要があります。すべてのバインディングメソッドに別のクラスを作成することをお勧めします。
メソッドは静的である必要があり、@ BindingAdapterアノテーションと対応するバインディングメソッド名(名前空間とメソッド名はカスタマイズできます)
1。カスタムTextWatcherを設定します
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
*/
}
Toastを表示する、Dialogを表示するなど、カスタムアクションで独自のTextWatcherをセットアップする場合。 「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) {
}
});
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。独自の検証ルールとエラーメッセージを設定します
SetError関数を使用し、errorMsgと検証ロジックのみをカスタマイズする場合。次のようにxmlを設定できます。
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}"
/>
活動コード
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;
}
});
コードを編集して、開発者が使用できるようにバインディングをより汎用的にしてください。
基本的に、依存フィールドを実装する方法が必要です。エラーはテキストの値に依存します。テキストが変更されたときにエラー値を更新する必要があります。
私はこれを達成する2つの方法を見つけました:
<EditView
Android:text="@={viewModel.email}"
Android:error="@={viewModel.emailRule.check(email)} />
データバインディングにより、check
が変更されると必ずemail
関数が呼び出されます。
ObservableField
とObservable
の間で変換するユーティリティを作成しました。 FieldUtils.Java を参照してください
これを使用して、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は、ユーザーが入力するとエラーをクリアします。データバインディングは、setterを呼び出した後も属性の値が保持されることを期待しています。したがって、値が変更されない場合は、セッターを再度呼び出すことはありません。したがって、入力するとすぐに、計算されたエラー値が同じ場合、データバインディングはセッターを呼び出さないため、エラーは消えます。この種のerror
属性は、データバインディングと互換性がありません。
私は、デザインライブラリによって提供される TextInputLayout を使用することを好みます。永続的なエラーフィールドがあり、見栄えもよくなっています。
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);
}
}
ビューモデル...
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);
}
}
});
}
...
そして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"
/>
このように編集テキストに検証を追加することもできます。
レイアウトファイル
<?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
}
})
}
}
ValidationRule
enum class ValidationRule{
EMPTY, EMAIL, PASSWORD
}
このようにウォッチャーをフラグメントまたはアクティビティに設定することを忘れないでください
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) {
}
}