Android Developer BlogのPreparing for Handsetsのまとめ
Android Developer Blogに新しい記事がのってました。いつもは適当ながら最初から最後まで翻訳するんですが、今日は時間がないのでざっとピンポイントにしてみました。おかしなところは下記の参照元リンクより確認してください。
今回は「次バージョンであるIce Cream Sandwichがきたとき、タブレット専用アプリでも対応が必要だよ。いきなりリリースされてもビビらんように、ちゃんとタブレットだけで動作するようにし、また事前にスマートフォンに対応しやすいようにしときましょうね。」という話です。
タブレット専用アプリが、タブレットとスマートフォンと両対応させるためのポイント
Honeycombがタブレット専用なので、アプリもタブレット専用に作ってるものが多い。けど、もうすぐIce Cream Sandwichもでるし、前方互換性も考えるとそのときの準備をしておいた方がいい。そのためには、以下の対応が必要。
- small screenな端末にインストールさせない
- 同じapkファイルでsmall screenもサポートする
まずアプリをタブレット専用に設定する
おそらく、supports-screensにandroid:xlargeScreens="true"を指定しているだけの場合も多いが、以下のようにちゃんと定義する。2つの方法がある。
- xlarge 以外のsmall、normal、largeを明示的にfalseにする
- 画面のwidthの最小サイズを600dpに定義する
<manifest ... > <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="false" android:xlargeScreens="true" android:requiresSmallestWidthDp="600" /> <application ... > ... </application> </manifest>1.はSDK version 3.1以前、2.は3.2以降で使用できる。
android:requiresSmallestWidthDpという属性は3.2以上しか使用できないため、3.1以下ではコンパイルエラーとなる。この属性を使うならminSdkVersionを指定する必要がある。
この例では600dpに設定してる。これは大体画面サイズが7インチ以上の場合で、9インチ以上にしたい場合は720dpを指定する。詳細はAndroid Developers Blog: New Tools For Managing Screen Sizesを参照のこと。
Honeycombアプリをスマートフォンでも動くようにする
multiple APK*1より、アプリのマルチデバイス対応の方を推奨している。以下に2つのガイダンスを示す。
- スマートフォン向けにシングルペイン/タブレット向けにマルチペインなど、異なった組み合わせで再利用できるようにFragmentまわりを設計する
- 画面サイズに沿うように Action Barのデザインを控えめなものとする
シングルペインとマルチペインの両対応レイアウトの作り方
2つのアプローチがあって
どちらにするかはアプリの作りに応じて選ぶべき。
前者は、動的にfragmentをaddしたりremoveしたりするので、fragmentの変更に応じてActionBarを更新しないといけない。挙動を管理しきれるのなら、特にレイアウトファイルを分けるよりもいい場合もある。
後者は、要はFragmentの使い分けを定義した各端末向けのレイアウトファイルを使って、Fragmentの実装コードのモジュール化、ActionBarの簡素化し、stack処理をシステムにまかすことができる。将来的にいい方法だけど、複数のFragmentが分割されたActivityを縦断してアクセスされるので、状況に応じて選択した方がいい。
後者についてはさらに以下のようなレイアウトをもつアプリの場合を例に考えてみる。Activity AがメインとなるActivityで、スマートフォンにはFragment Aのみ、タブレットにはFragment A とFragment Bの両方のレイアウトをもつとする。
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.example.android.TitlesFragment" android:id="@+id/list_frag" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>タブレットの場合、
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/frags"> <fragment class="com.example.android.TitlesFragment" android:id="@+id/list_frag" android:layout_width="@dimen/titles_size" android:layout_height="match_parent"/> <fragment class="com.example.android.DetailsFragment" android:id="@+id/details_frag" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>実装としては、レイアウトに Fragment Bがあるかどうかで処理を分け、ある場合はActivity AからActivity Bへの更新通知を実装し、ない場合は、Activity AからActivity Bを起動するよう実装する。このときポイントは2つ。
- 片方のFragmentからもう片方のFragmentを操作しない
- コンテンツ関係のコードはFragmentの中ではなく、Activityの中に実装する
前者については、コールバックインタフェースを各Fragmentクラスの中に定義すると、ホストとなるActivityへイベントを通知できる。Activityがコールバックを受け取る時に、現在のFragmentの設定に基づいて適切に処理される。
今回の例の場合、以下のような実装となる。
/** Fragment A でアイテム選択時のコールバック */ public void onItemSelected(int position) { // DisplayFragment (Fragment B) がレイアウトに存在するかどうかのフラグ DisplayFragment fragB = (DisplayFragment) getFragmentManager().findFragmentById(R.id.display_frag); if (fragB == null) { // レイアウトにない場合、DisplayActivity (Activity B) を起動する。 Intent intent = new Intent(this, DisplayActivity.class); intent.putExtra("position", position); startActivity(intent); } else { // レイアウトにある場合、更新する。 fragB.updateContent(position); } }このあたりの完全なデモコードとして、Honeycomb Gallery サンプル (ZIP ファイル)があるのでみておくこと。
スマートフォンでも対応するようなAction Barを作っておく
コツは3つ。
- Action Itemを設定するとき「always」の使用は避ける。「isRoom」を使う。
- できるならアイコンも用意しておき、showAsAction="ifRoom|withText"と定義する。
- カスタムナビゲーションモードの使用は避ける。ビルドインされたタブ&ドロップダウンのものを使う。もし使用したい場合はICSのリリース後、テストをする必要がある。
上記に従っておけば、スマートフォン画面では以下の図のように対応される。スマートフォンでは2つのAction ItemがAction Barに配置され、残りはMenuに配置される。
その他の考慮点
- ListViewを使用してる場合、画面サイズが変わることで表示する情報量に影響があるので、あらかじめ考慮したレイアウトを準備しておく必要がある
- リソースファイル(特に integers、dimensions、booleansなどの値)をスマートフォンにあったものを用意しておく