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も持ちます。


使用前

  1. public class EditActivity extends Activity implements OnClickListener {  
  2.     public EditText mFirstName;  
  3.     public EditText mLastName;  
  4.     public TextView mLabelLastName;  
  5.     public TextView mLabelFirstName;  
  6.     public Spinner mSex;  
  7.     public FrameLayout mFrameSex;  
  8.     public TextView mLabelSex;  
  9.     public TextView mLabelBirthday;  
  10.     public DatePicker mBirthday;  
  11.   
  12.  @Override  
  13.  public void onCreate(Bundle savedInstanceState) {  
  14.   super.onCreate(savedInstanceState);  
  15.   setContentView(R.layout.activity_main);  
  16.   
  17.          mFirstName = findViewById(R.id.first_name);  
  18.          mLastName = findViewById(R.id.last_name);  
  19.          mLabelLastName = findViewById(R.id.label_last_name);  
  20.          mLabelFirstName = findViewById(R.id.label_first_name);  
  21.          mSex = findViewById(R.id.sex);  
  22.          mFrameSex = findViewById(R.id.frame_sex);  
  23.          mLabelSex = findViewById(R.id.label_sex);  
  24.          mLabelBirthday = findViewById(R.id.label_birthday);  
  25.          mBirthday = findViewById(R.id.birthday);  
  26.   
  27.     }  
  28.   
  29. }  

使用後
  1. public class EditActivity extends Activity implements OnClickListener {  
  2.  ViewHolder mViewHolder;  
  3.   
  4.  @Override  
  5.  public void onCreate(Bundle savedInstanceState) {  
  6.   super.onCreate(savedInstanceState);  
  7.   setContentView(R.layout.activity_main);  
  8.   
  9.   mViewHolder = new ViewHolder(getWindow().getDecorView());  
  10.   
  11.     }  
  12.   
  13. }  
  1. public class ViewHolder {  
  2.     public EditText mFirstName;  
  3.     public EditText mLastName;  
  4.     public TextView mLabelLastName;  
  5.     public TextView mLabelFirstName;  
  6.     public Spinner mSex;  
  7.     public FrameLayout mFrameSex;  
  8.     public TextView mLabelSex;  
  9.     public TextView mLabelBirthday;  
  10.     public DatePicker mBirthday;  
  11.   
  12.     public ViewHolder(View v) {  
  13.          mFirstName = findViewById(v, R.id.first_name);  
  14.          mLastName = findViewById(v, R.id.last_name);  
  15.          mLabelLastName = findViewById(v, R.id.label_last_name);  
  16.          mLabelFirstName = findViewById(v, R.id.label_first_name);  
  17.          mSex = findViewById(v, R.id.sex);  
  18.          mFrameSex = findViewById(v, R.id.frame_sex);  
  19.          mLabelSex = findViewById(v, R.id.label_sex);  
  20.          mLabelBirthday = findViewById(v, R.id.label_birthday);  
  21.          mBirthday = findViewById(v, R.id.birthday);  
  22.     }  
  23.   
  24.     @SuppressWarnings("unchecked")  
  25.     private <t> T findViewById(View v, int id) {  
  26.         return (T) v.findViewById(id);  
  27.     }  
  28. }  
  29. </t>  


これで、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の組み合わせ
  1. // Validators  
  2. Validators mValidators = new Validators();  
  3.   
  4. EditText mTextPersonName;  
  5. EditText mTextPassword;  
  6. Button mButton;  
  7.   
  8. protected void onCreate(Bundle savedInstanceState) {  
  9.     super.onCreate(savedInstanceState);  
  10.     setContentView(R.layout.activity_main);  
  11.   
  12.     mTextPersonName = (EditText) findViewById(R.id.textPersonName);  
  13.     mTextPassword = (EditText) findViewById(R.id.textPassword);  
  14.     mButton = findViewByIdAndCast(v, R.id.button);  
  15.   
  16.     // Define validators for views  
  17.     mValidators.put(mTextPersonName, new Validator[] {  
  18.                 new RequiredValidator("Be sure to input. "),  
  19.                 new MaxLengthValidator(30"Being allowed is to 30 characters. "),  
  20.     });  
  21.     mValidators.put(mTextPassword, new Validator[] {  
  22.                 new RequiredValidator("Be sure to input. "),  
  23.                 new RangeValidator(816"Being allowed is from 8 to 16 characters. "),  
  24.     });  
  25.   
  26.     mButton.setOnClickListener(new OnClickListener() {  
  27.             @Override  
  28.             public void onClick(View v) {  
  29.                 // Execute Validators  
  30.                 mValidators.clearError();  
  31.                 if (mValidators.isValid()) {  
  32.                     Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show();  
  33.                 }  
  34.             }  
  35.         });      
  36. }  


サンプル2 バリデータ付きEditView
  1. <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:validator="http://schemas.android.com/apk/res-auto/net.granoeste.validator" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">  
  2.   
  3.     <net.granoeste.validator.validatableedittext android:id="@+id/textPersonName" android:layout_width="match_parent" android:layout_height="wrap_content" validator:message="PersonName is required " validator:required="true">  
  4.   
  5.     <net.granoeste.validator.validatableedittext android:id="@+id/textPassword" android:layout_width="match_parent" android:layout_height="wrap_content" validator:label="@string/textPassword" validator:minlength="8" validator:required="true">  
  6.   
  7. </net.granoeste.validator.validatableedittext></net.granoeste.validator.validatableedittext></linearlayout>  
  1. // Validators  
  2. Validators mValidators = new Validators();  
  3.   
  4. net.granoeste.validator.ValidatableEditText mTextPersonName;  
  5. net.granoeste.validator.ValidatableEditText mTextPassword;  
  6. Button mButton;  
  7.   
  8. protected void onCreate(Bundle savedInstanceState) {  
  9.     super.onCreate(savedInstanceState);  
  10.     setContentView(R.layout.activity_main);  
  11.   
  12.     mTextPersonName = (net.granoeste.validator.ValidatableEditText) findViewById(R.id.textPersonName);  
  13.     mTextPassword = (net.granoeste.validator.ValidatableEditText) findViewById(R.id.textPassword);  
  14.     mButton = findViewByIdAndCast(v, R.id.button);  
  15.   
  16.     // add ValidatableEditText to Validators  
  17.     mValidators.put(mTextPersonName);  
  18.     mValidators.put(mTextEmailAddress);  
  19.   
  20.     mButton.setOnClickListener(new OnClickListener() {  
  21.             @Override  
  22.             public void onClick(View v) {  
  23.                 // Execute Validators  
  24.                 mValidators.clearError();  
  25.                 if (mValidators.isValid()) {  
  26.                     Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show();  
  27.                 }  
  28.             }  
  29.         });      
  30. }   


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

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

以上になります。

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



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

0 件のコメント:

コメントを投稿