2012年12月23日日曜日

InputFilterとValidator

ValidatorじゃなくてAndroidには InputFilterがあるじゃないか?って話がでたので...

フィルタとは (filter): - IT用語辞典バイナリ
フィルターは、受け取ったデータに対して何らかの処理や加工を行った上で出力すること

Androidでは以下のInputFilterが提供されています。
InputFilter | Android Developers

public interface

InputFilter (view source)

android.text.InputFilter
Known Indirect Subclasses
DateKeyListenerFor entering dates in a text field. 
DateTimeKeyListenerFor entering dates and times in the same text field. 
DialerKeyListenerFor dialing-only text entry
As for all implementations of KeyListener, this class is only concerned with hardware keyboards. 
DigitsKeyListenerFor digits-only text entry
As for all implementations of KeyListener, this class is only concerned with hardware keyboards. 
InputFilter.AllCapsThis filter will capitalize all the lower case letters that are added through edits. 
InputFilter.LengthFilterThis filter will constrain edits not to make the length of the text greater than the specified length. 
LoginFilterAbstract class for filtering login-related text (user names and passwords)  
LoginFilter.PasswordFilterGMailThis filter is compatible with GMail passwords which restricts characters to the Latin-1 (ISO8859-1) char set. 
LoginFilter.UsernameFilterGMailThis filter rejects characters in the user name that are not compatible with GMail account creation. 
LoginFilter.UsernameFilterGenericThis filter rejects characters in the user name that are not compatible with Google login. 
NumberKeyListenerFor numeric text entry
As for all implementations of KeyListener, this class is only concerned with hardware keyboards. 
TimeKeyListenerFor entering times in a text field. 


これら以外のフィルターを行いたい場合はカスタムフィルタを作成する必要があります。
作成の方法はこちらが参考になると思います。
EditTextにカスタムInputFilterを適用して入力値の制限を行う | MoaiApps Labo 
Y.A.M の 雑記帳: Android Filterを使ってみた

フィルタは、特定の文字列以外は認めない機能です。
入力された文字列を検査する機能では有りません。
それがバリデータになります。

バリデータとは 「バリデーター」 (validator): - IT用語辞典バイナリ
入力されたデータが仕様にそって適切に記述されているかを判断し、不適切な箇所があった場合にはエラーとして通知する。


特定の文字以外を入力させなくしているから、バリデータは不要でしょうか?

そんなことは無いと思います。
  • ユーザーID/パスワードなどの必須項目は入力されているか? 
  • パスワードはある桁以上であるか?特定の文字列を含んでいるか?
  • パスワードの確認入力などの二つの項目が同じであるか?
  • 電話番号や郵便番号などの"-"区切りのフォーマットが正しいか?
など、入力確定後に検査をしなければならないと思います。
※フォーマット付き項目については、項目を分けたらとか、フォーマット無しで入力させたらいいじゃないかとかありますけどね。鶴様が既存PCのフォームがフォーマット付きだから踏襲し...(ry

検査のタイミングですが、
  • フォーカスが外れたタイミング
  • ボタン押下などアクションのタイミング
となると思います。
※必須項目の検査は、全ての入力項目を確定させたアクションでしか行えないと思います。

AndroidのEditViewの場合、

  1. android:inputType 入力制御 (IMEのパネルを制御)
  2. InputFilter 入力制御 入力されたタイミングでフィルタ
  3. Formatter 文字列入力後にフォーマット
  4. Validator 最終的な文字列の検査

の順に処理をすればいいんじゃないかと思います。

バリデータなんて古いぜこっちの方がクールだぜ。ってのあったら教えてほしいな〜。

でわでわ

2012年12月20日木曜日

ViewHolderとValidatorForAndroid

Android Advent Calendar 2012 #androidadvent2012
12/20(裏) 担当の @katsummy です。表担当は@stachibanaさんとなります。

よろしくお願いします〜。

初心者向けなニッチな内容です。
シニアの方にはこんなの自分で実装してるよ〜な内容になるかもです。

■ViewHolder

XMLでレイアウトを作成した後、ActivityまたはFragmentの中で扱う為に、inflateしてViewをfindViewByIdして、メンバー変数に格納しますよね?

結構面倒くさくないですか?
  • レイアウトファイルからidを抜き出し
  • メンバー変数にViewのクラスを記述して、mから始める名前で定義
  • mXxxXxx = (View) findViewById(R.id.xxxx); を記述

Viewの項目が増えるとコピペの作業と文字列の置き換えが大変。
  android:id="@+id/first_name"

EditText mFirstName;
mFirstName = (EditText) findViewById(R.id.first_name);

また、Activity/Fragmentにメンバー変数が多くなり見難くなると思います。

そこで、まずは、これらViewの項目の定義を外だしにしてしまいましょう。

1つのレイアウトに対してViewHolderクラスを作成します。
ViewHolderってのはListAdapterのテクニックでも使用しているあれです。

ViewHolderには、基本的にプロパティ値としてViewも持ちます。


使用前

public class EditActivity extends Activity implements OnClickListener { public EditText mFirstName; public EditText mLastName; public TextView mLabelLastName; public TextView mLabelFirstName; public Spinner mSex; public FrameLayout mFrameSex; public TextView mLabelSex; public TextView mLabelBirthday; public DatePicker mBirthday; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mFirstName = findViewById(R.id.first_name); mLastName = findViewById(R.id.last_name); mLabelLastName = findViewById(R.id.label_last_name); mLabelFirstName = findViewById(R.id.label_first_name); mSex = findViewById(R.id.sex); mFrameSex = findViewById(R.id.frame_sex); mLabelSex = findViewById(R.id.label_sex); mLabelBirthday = findViewById(R.id.label_birthday); mBirthday = findViewById(R.id.birthday); } }
使用後
public class EditActivity extends Activity implements OnClickListener { ViewHolder mViewHolder; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mViewHolder = new ViewHolder(getWindow().getDecorView()); } } public class ViewHolder { public EditText mFirstName; public EditText mLastName; public TextView mLabelLastName; public TextView mLabelFirstName; public Spinner mSex; public FrameLayout mFrameSex; public TextView mLabelSex; public TextView mLabelBirthday; public DatePicker mBirthday; public ViewHolder(View v) { mFirstName = findViewById(v, R.id.first_name); mLastName = findViewById(v, R.id.last_name); mLabelLastName = findViewById(v, R.id.label_last_name); mLabelFirstName = findViewById(v, R.id.label_first_name); mSex = findViewById(v, R.id.sex); mFrameSex = findViewById(v, R.id.frame_sex); mLabelSex = findViewById(v, R.id.label_sex); mLabelBirthday = findViewById(v, R.id.label_birthday); mBirthday = findViewById(v, R.id.birthday); } @SuppressWarnings("unchecked") private T findViewById(View v, int id) { return (T) v.findViewById(id); } }


これで、Activity/Fragmentから大量のメンバー変数が消えて、コードを追いやすくなるのではないでしょうか?

でも、ViewHolderにいろいろ書くのは変ってないし、やっぱり面倒くさいよね?

なので、レイアウトからViewHolderを作成するスクリプトを作成しました。


レイアウトからViewHolderクラスを作成

なお、指定するレイアウトは予めフォーマーッター(Ctrl+Shift+F)でフォーマットしておく必要があります。要は、Viewタグの後にIDアトリビュートが定義されてる必要があります。


ValidatorForAndroid


社員管理・顧客管理などの編集画面で、一度に多くの項目を入力を行わせるアプリを作成することもあります。

※PCアプリ/Webアプリの機能をそのままAndroid側でも実現させようとする要件ですね。

入力項目で使用するEditViewには、InputTypeでIMEを制御はありますがValidatorは備わってません。エラーメッセージを表示する機能はあるけど、バリデーションは自分でやってねって感じでしょうか。

StrutsみたいなValidator欲しいなっと思ったので作ってみました。

granoeste/ValidatorForAndroid · GitHub 


StrutsみたいなValidator欲しいなっと思ったので作ってみました。
apache commons validatorを参考にしています。

サンプル1 EditViewとValidatorの組み合わせ
// Validators Validators mValidators = new Validators(); EditText mTextPersonName; EditText mTextPassword; Button mButton; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextPersonName = (EditText) findViewById(R.id.textPersonName); mTextPassword = (EditText) findViewById(R.id.textPassword); mButton = findViewByIdAndCast(v, R.id.button); // Define validators for views mValidators.put(mTextPersonName, new Validator[] { new RequiredValidator("Be sure to input. "), new MaxLengthValidator(30, "Being allowed is to 30 characters. "), }); mValidators.put(mTextPassword, new Validator[] { new RequiredValidator("Be sure to input. "), new RangeValidator(8, 16, "Being allowed is from 8 to 16 characters. "), }); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Execute Validators mValidators.clearError(); if (mValidators.isValid()) { Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show(); } } }); }

サンプル2 バリデータ付きEditView
// Validators Validators mValidators = new Validators(); net.granoeste.validator.ValidatableEditText mTextPersonName; net.granoeste.validator.ValidatableEditText mTextPassword; Button mButton; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextPersonName = (net.granoeste.validator.ValidatableEditText) findViewById(R.id.textPersonName); mTextPassword = (net.granoeste.validator.ValidatableEditText) findViewById(R.id.textPassword); mButton = findViewByIdAndCast(v, R.id.button); // add ValidatableEditText to Validators mValidators.put(mTextPersonName); mValidators.put(mTextEmailAddress); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Execute Validators mValidators.clearError(); if (mValidators.isValid()) { Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show(); } } }); } 

こんな感じで使用します。

詳しくは、GitHubに上げてるのでソース見てね。
解んなかったら聞いて下さい。
※バグがあったらIssue書いてくれても、ForkしてPull Requesteしてくれてもいいんだよ。

以上になります。

明日は、@out_of_kayaさん と @tarotaro4さん が担当されます。



でわでわ。
メリークリスマス&よいおとしを。

2012年12月4日火曜日

Google Maps Android API v2 がリリースされたようですね。


Google Maps Android API v2 がリリースされたようですね。


とりあえず、サンプル動かしてみる。
Developer Guide 通りにやるだけだけどw

1. Sample Codeをインポート
  1. まずは、Google Play Services のライブラリをインポート
    1. Select File > Import > Android > Existing Android Code Into Workspace
    2. /extras/google/google_play_services/libproject/google-play-services_lib
  2. 続いて、サンプルアプリケーションのインポート
    1. Select File > Import > Android > Existing Android Code Into Workspace
    2. /extras/google/google_play_services/samples/maps
  3. ライブラリーの追加
    1. Context Menu > Android Tools > Add Support Library...
2. API Keyの取得
  1. Google APIs Console にアクセス
  2. Services 選択
  3. Google Maps Android API v2 をONにする。
    • MapsのAPIがいくつかあるけど、Androidってついてるやつです。
  4. API Access 選択
  5. [Create new Android key...] 選択
  6. テキストボックスに、証明書のSHA1 fingerprintとパッケージをセミコロンで区切って入力
    • 証明書のSHA1 fingerprintは以下のコマンドで確認出来る。 
    • keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
  7. 登録したら、API keyを確認

3. AndroidManifest.xmlにAPI Keyを記述

4. ビルドして実機で確認


Google Play 開発者サービスがインストールされてないと、以下のようにインストールするように言われます。
※しかし、開発者サービスって表示どうにかなりませんかね。理解してないユーザのGoogle Playのコメントが酷い。。。

サンプルソースチラ見

  • MapはFragmentで提供されてます!
  • ようやく、FragmentにMAPが!
  • しかも、SupportMapFragment!
  • 2.xでも動くようですな
  • Galaxy S (2.3.?)でも動いてました。
  • AndroidManifestのには、OpenGL ES 2.0. の定義があるので、2.2(Lv8)以上でないとだめなようです。

くわしくは、Google Maps Android API v2 — Google Developers を見る必要がありますね。


以上