web-dev-qa-db-ja.com

DataBinding Framework MVVMを使用してEditTextにエラーを設定する方法

私は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オブジェクトを作成したくありません。

14
Pranav

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

コードを編集して、開発者が使用できるようにバインディングをより汎用的にしてください。

6
Long Ranger

基本的に、依存フィールドを実装する方法が必要です。エラーはテキストの値に依存します。テキストが変更されたときにエラー値を更新する必要があります。

私はこれを達成する2つの方法を見つけました:

データバインディング式を使用して属性を設定する

<EditView
    Android:text="@={viewModel.email}"
    Android:error="@={viewModel.emailRule.check(email)} />

データバインディングにより、checkが変更されると必ずemail関数が呼び出されます。

RxJavaを使用して1つのフィールドから別のフィールドに変換する

ObservableFieldObservableの間で変換するユーティリティを作成しました。 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の問題

EditTextは、ユーザーが入力するとエラーをクリアします。データバインディングは、setterを呼び出した後も属性の値が保持されることを期待しています。したがって、値が変更されない場合は、セッターを再度呼び出すことはありません。したがって、入力するとすぐに、計算されたエラー値が同じ場合、データバインディングはセッターを呼び出さないため、エラーは消えます。この種のerror属性は、データバインディングと互換性がありません。

私は、デザインライブラリによって提供される TextInputLayout を使用することを好みます。永続的なエラーフィールドがあり、見栄えもよくなっています。

3
Manas Chaudhari

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) &amp;&amp; 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) {
        }
    }
0
Rajesh Khadka