初心者向けなニッチな内容です。
シニアの方にはこんなの自分で実装してるよ〜な内容になるかもです。
■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 {
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 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> T findViewById(View v, int id) {
- return (T) v.findViewById(id);
- }
- }
- </t>
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 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);
-
-
- 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) {
-
- mValidators.clearError();
- if (mValidators.isValid()) {
- Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show();
- }
- }
- });
- }
// 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
- <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">
-
- <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">
-
- <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">
-
- </net.granoeste.validator.validatableedittext></net.granoeste.validator.validatableedittext></linearlayout>
-
- 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);
-
-
- mValidators.put(mTextPersonName);
- mValidators.put(mTextEmailAddress);
-
- mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
-
- mValidators.clearError();
- if (mValidators.isValid()) {
- Toast.makeText(getApplicationContext(), "It has some errors. ", Toast.LENGTH_SHORT).show();
- }
- }
- });
- }
// 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さん が担当されます。
でわでわ。
メリークリスマス&よいおとしを。