Androidのラジオボタンは、HTMLのラジオボタンのようなvalueを持っていません。
「どのRadioButtonが選択されたか」と「選択されたRadioButtonのテキスト」くらいしかそのラジオボタンを特定する情報がありません。
そのため、ラジオボタンの値をDBへ登録しようなどと考えているとリソースIDとコード値の割り当てをハードコーディングすることになってしまいます。
例えば以下のような感じです。
RadioGroup radioGroup = (RadioGroup) activity.findViewById(R.id.radio_group_foo);
int checkedId = radioGroup.getCheckedRadioButtonId();
int value;
switch (checkedId) {
case R.id.radio_button_foo_1:
value = 1;
break;
case R.id.radio_button_foo_2:
value = 2;
break;
}
このマッピングを色んなところに散らかすのは嫌です。
AndroidのUIに関するプログラミングである以上、リソース(R.java)を扱うのは避けられそうにないので、外部の設定ファイルにマッピングを書くのは難しそうです。
そこで、アノテーションを使ってラジオボタンの値を設定するフィールドに情報をまとめることにしました。
ソースコードにマッピングを書くのは変わりませんが、情報がまとまるのでコードは見やすくなるはずです。
イメージは、SAStrutsのような、アノテーションをpublicフィールドに定義する感じです。
以下、必要なアノテーションです。
Radio.java
@Target(value = { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Radio {
int groupId();
RadioValue[] values();
}
RadioValue.java
@Target(value = { ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RadioValue {
int id();
String value();
}
ラジオボタンの以下のような定義があったとします。
<RadioGroup
android:id="@+id/type"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/type_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="タイプA" />
<RadioButton
android:id="@+id/type_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="タイプB" />
</RadioGroup>
このラジオボタンの値を格納するフィールドをアノテーションを使って定義する例です。
@Radio(groupId = R.id.type,
values = { @RadioValue(id = R.id.type_a, value = "A"),
@RadioValue(id = R.id.type_b, value = "B") })
public String type;
アノテーションを使ってフィールドに値を取得するロジックです。(断片)
Field field = target.getClass().getField("type");
:
Radio radio = (Radio) field.getAnnotation(Radio.class);
if (radio != null && type.equals(String.class)) {
int groupId = radio.groupId();
RadioGroup radioGroup = (RadioGroup) activity.findViewById(groupId);
int checkedId = radioGroup.getCheckedRadioButtonId();
RadioValue[] values = radio.values();
for (int i = 0; i < values.length; i++) {
if (values[i].id() == checkedId) {
field.set(form, values[i].value());
break;
}
}
}
これで、選択されたラジオボタンに応じて「A」や「B」などの値がtypeというフィールドに取得できます。
フィールド名をハードコーディングしているので、このままだとフィールド数分最後のロジックを書くことになってしまいますが、フィールドの取得部分(getField)も「クラス内の全てのpublicフィールドが対象」
などとしてしまえばだいぶ汎用的になるはずです。
注意:
上記のアノテーションRadioValueを定義せず、
Radio内にリソースIDの配列(int[] ids())と値の配列(String[] values())
を定義する方法も考えられますが、以下のAndroidのバグがあるためAndroid2.1以前では動作しません。
Issue 11124: array of primitive types in annotation
プリミティブ型配列のアノテーションパラメータの実装が漏れていたようです。