読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

Android における EditText のクールなバリデーション実装

Android

Android アプリによっては結構たくさんのEditTextが出て来て、それらを一つずつ検証しないといけない場面ってのはよくある。これら一つ一つに、以下のことを書いている方は多いのではないだろうか。

if (editText.getText().toString().isEmpty()) {
    // 入力必須エラー
}
if (editText.getText().toString().length < 8) {
    // 最小文字数エラー
}

それぞれにこのような記述を書くだけで、一瞬で残念なソースコードができあがる。かつての自分もそうやって書いていて後になってこれはひどいなーと思うようになった。今回はこれを改良しよう。

ValidateManager の使用

まず使い方はこんな感じにするように作った。

        ValidateManager validateManager = new ValidateManager(context);
        Validators validators = new Validators();
        validators.add(new PresenseValidatable());
        validators.add(new MinLengthValidatable(4));
        validators.add(new MaxLengthValidatable(320));
        validateManager.add(editText, validators);

        validators = new Validators();
        validators.add(new MaxLengthValidatable(24));
        validateManager.add(editText2, validators);

         validateManager.validate()

最後のvalidate()を呼ぶと、エラーメッセージがある場合はその文字列リストが返る。これでバリデーションの使い回しが可能だ。
今回はEditText のみのバリデーションだけど、バリデーションのほとんどはEditTextだと思うので、それで良いと思う。今後他のViewにも必要になったら考え直す。

ValidateManager の実装

ここまで設計できれば後は実装するのみ。

public class ValidateManager {
    private Map<EditText, Validators> validateMap;
    private Context context;

    public ValidateManager(Context context) {
        this.context = context;
        validateMap = new HashMap<EditText, Validators>();
    }

    public void add(EditText editText, Validators validators) {
        validateMap.put(editText, validators);
    }

    public List<String> validate() {
        List<String> errorMessages = new ArrayList<String>();

        LogUtil.v(validateMap.size());
        for (Map.Entry<EditText, Validators> keyValue : validateMap.entrySet()) {
            Validators validators = keyValue.getValue();
            EditText editText = keyValue.getKey();

            for (Validatable validatable : validators.as_list()) {
                ValidateDto validateResult = validatable.isValid(editText);
                if (validateResult.isValid()) {
                    continue;
                }
                // validate error
                StringBuffer errStr = new StringBuffer();
                errStr.append(editText.getContentDescription().toString())
                        .append(context.getString(validateResult.getMessage()));
                if (validatable.getTailMessage() != null) {
                    errStr.append(validatable.getTailMessage());
                }
                LogUtil.v(errStr.toString());
                errorMessages.add(errStr.toString());
            }
        }

        return errorMessages;

    }
}
public class Validators {
    private List<Validatable> validateLists;

    public Validators() {
        validateLists = new ArrayList<Validatable>();
    }

    public void add(Validatable validatable) {
        validateLists.add(validatable);
    }

    public List<Validatable> as_list() {
        return validateLists;
    }
}
public interface Validatable {
    ValidateDto isValid(EditText editText);
    String getTailMessage();
}
public class ValidateDto {
    private boolean ret;
    private int message;

    public ValidateDto(boolean ret, int message) {
        this.ret = ret;
        this.message = message;
    }

    public int getMessage() {
        return message;
    }

    public boolean isValid() {
        return ret;
    }
}
public class MinLengthValidatable implements Validatable {
    // default length
    private int minText;

    public MinLengthValidatable(int minText) {
        this.minText = minText;
    }

    @Override
    public ValidateDto isValid(EditText editText) {
        boolean isValid = true;
        if (editText.getText().length() < minText) {
            isValid = false;
        }

        ValidateDto ret = new ValidateDto(isValid, R.string.err_length_min);
        return ret;
    }

    @Override
    public String getTailMessage() {
        return String.valueOf(minText);
    }

}

Validatable を実装したクラスをどんどん作っていけば色んなバリエーションのEditTextの検証ができる!これは超便利。
こういう Interface の使い方はめっちゃ便利だよな〜。 Java使いならInterfaceは使いこなせるようになった方がいいですよ。