ページ

2011/05/21

Android Preferenceの内容が他の項目に変わる

|
カスタマイズしたPreferenceを作りました。
しばらく画面内に1つしかそのPreferenceを使っていなかったのですが、
数を増やしたところおかしなことになりました。

増やしたもののうち、Dependencyを設定しているカスタムPreferenceがあるのですが
依存先のチェックボックスを操作したところ
そのカスタムPreferenceと他のカスタムPreferenceが入れ替わる現象が起こりました。
さらに、カスタムPreferenceをタップしてダイアログを開き、
OKで閉じても他の項目と表示が入れ替わってしまいます。
しかし、他の項目と思ってタップすると、開くダイアログは入れ替わり前の内容です。

Preferenceの一覧表示だけがおかしい?と見当をつけて調べたところ、
原因はonBindView()にありました。

2011/05/05

Android CheckStyle設定

|
AndroidプロジェクトでのCheckStyle設定についてです。

Android Open Source Projectのコードスタイルでは、フィールド名はmから開始する、
などのルールがあります。
Code Style Guidelines for Contributors

これはコントリビューター向けのものではありますが、
自分のアプリの書くコードもなるべくこれに沿っていた方が見やすいかなと思っています。

# 多くはCode Completeにあるような推奨されるor避けるべき書き方の話なので
# Android特有のルールというのは実は少ないかもしれません。

そこでCheckStyleでチェックしようとしたのですが、ちょっと苦労したので載せておきます。
Android用のCheckStyle設定を公開してくださっている方がいます。
Checkstyle for Android

でもこれ、一部間違いがあって動きませんでした。

Android 見つけにくいResources$NotFoundExceptionの原因

|
TextViewに文字列や数値を表示する画面を作りました。
正しくレイアウトのXMLを記述して、プロジェクトも再ビルドして、R.javaにIDは存在しているはずなのに
Resources$NotFoundExceptionが出てしまいました…。

しばらく例外メッセージの中の「見つからない」と言われているIDを見ていて、気づきました。

TextViewにはsetText(CharSequence text)だけでなくsetText(int resid)があるのです。

そのため、以下のようなコードを書くとコンパイルエラーは出ません。
int price = 1000;
((TextView) view.findViewById(R.id.price)).setText(price);

これを動かすと、リソースID「1000」のR.stringを探そうとするので上記の例外が発生します。

例えば以下のようにres/values/string.xmlが書いてあったとします。
<resources>
    <string name="app_name">Test</string>
</resources>

この場合は以下のようにすれば「Test」がTextViewに表示されます。
((TextView) view.findViewById(R.id.price)).setText(R.string.app_name);

最初の例外が出る例は、以下のように文字列に変換すれば動きます。
int price = 1000;
((TextView) view.findViewById(R.id.price)).setText(Integer.toString(price));

数値はCharSequenceではないので当たり前の動作なのですが、
コンパイルエラーが出ないので注意した方が良さそうです。
本当は、R.javaの定数がintなどのプリミティブ型でなく列挙型等になっていると、
間違ってint型を指定してもコンパイル時に検出できて良いのですけどね。

2011/05/04

Android (ソースコード解説) SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

|
SlidingDrawerを使って、「Gmailアプリでチェックボックスにチェックを入れたときに表示されるボタン群」のようなものをきれいに実現する方法です。

ソースコードのポイントを解説します。

※完成イメージと、それに至るまでの問題は以下で説明しました。
Android SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

※ソースコードは以下に掲載しました。
Android (ソースコード掲載) SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

Android (ソースコード掲載) SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

|
SlidingDrawerを使って、「Gmailアプリでチェックボックスにチェックを入れたときに表示されるボタン群」のようなものをきれいに実現する方法です。

完成イメージと、それに至るまでの問題は以下で説明しました。
Android SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

ソースコードをとりあえず載せます。
また長いので、解説は改めて投稿します…。

まずはレイアウトです。

Android SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

|
他の部品を隠さずにSlidingDrawerを使い、コンテンツの大きさに合わせてSlidingDrawerをレイアウトする方法についてです。この説明もタイトルも、一言でうまく伝えられないので、まずは完成イメージをまず載せます。
ソースコードは改めて投稿します。

完成イメージ

1. 画面の下方にスクロールする部品があります。(TextView)
2. ドロワーを開くと、スクロールするビューのスクロールバーがずれています。つまり、ドロワーで隠れてしまった部分を下方にスクロールすることで表示できます。
3. 実際、最下部までスクロールできます。(「終わり」という部分です。)
4. ドロワーを閉じると、スクロールビューが元のサイズに戻ります。

2011/05/03

Android アノテーションでRadioButtonの値をコード値として取得

|
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フィールドに定義する感じです。

以下、必要なアノテーションです。

Java リフレクションで取得するフィールドの順番

|
リフレクションでフィールドを取得すると、フィールドの順番が何に基づいているのか分からない結果となりました。

Java API仕様を見ると、確かに順序は決まっていないという記述がありました。
Class#getFields()

この Class オブジェクトが表すクラスまたはインタフェースのすべてのアクセス可能な public フィールドをリフレクトする、Field オブジェクトを保持している配列を返します。返された配列内の要素は、ソートされていたり、特定の順序になっていたりすることはありません。


Javaの標準的な機能ではフィールドの定義順でフィールド情報を取得することはできなさそうです。

以下のように、フィールド名やアノテーションを使えば順序を規定することはできそうです。
Java reflection: Is the order of class fields and methods standardized? - stackoverflow

これを使って、AndroidのParcelableの実装を汎用化しようと思います。
# Parcelable#writeToParcel(Parcel, int)やコンストラクタにフィールドを列挙するのを毎回書かずに済むようにします。

Android フレームワークとか

|
Androidプログラミングを始めてまだ数カ月ですが、
入門書やWebの情報を参考に作成していると、
同じようなコードを何度も書くようでうんざりしてきます…。

Androidのフレームワーク、ライブラリなどはないのでしょうか?

個人的に、ライブラリ化できそうだと思うのは以下のようなものです。
まだ勉強中なのでもっとあるのかもしれませんが。

  • 入力チェック
  • DBアクセス

Android SDK自体、フレームワークなのですが
一般的なWebアプリケーション等と同様、
多数の入力画面やBean的なものを書こうとすると
どうしても上記のような部分が面倒に感じます。

既存のライブラリを含めてしまうとかなりアプリ容量が大きくなるし、
動作も保証できないのが不安なのかもしれませんが。

UIに関してはライブラリがあるようですが、
見つからなければ、今進めているアプリの開発の中で
簡易なものを作ってみようかなと思います。

Android TabHostで下側にタブを表示するときにビューが隠れないようにする方法

|
# これまであまりに一般的すぎる内容ばかり書いていたので、ある程度調べてもわからなかったこと、つまづいたことを中心に書いていきたいと思います。

AndroidでTabHostによるタブを作成するとき、下側にタブを作成するにはRelativeLayoutを使う必要があるようです。
しかしこのとき、メインのビューを下一杯まで広げると、タブでビューの一部が隠れてしまいました。

RelativeLayoutを使う場合の注意ですが、相対的な配置なので、
他の要素とどのような位置関係にあるのかを指定しなくてならないようです。

上側に表示するFrameLayoutにこれを指定することで解決しました。
android:layout_above="@android:id/tabs"

FrameLayoutはTabWidgetの上にある、という指定です。

以下、全体のレイアウトです。タブのコンテンツは含まれていません。

<?xml version="1.0" encoding="utf-8"?>
<TabHost
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@android:id/tabhost"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 <RelativeLayout
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical">
  <FrameLayout
   android:layout_alignParentTop="true"
   android:layout_above="@android:id/tabs"
   android:id="@android:id/tabcontent"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent" />
  <TabWidget
   android:id="@android:id/tabs"
   android:layout_alignParentBottom="true"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content" />
 </RelativeLayout>
</TabHost>