2012/06/22

HVGA,タブレット両対応の郵便番号アプリをViewPagerとFragmentで作りかえてみ た

以前作成した郵便番号アプリはSlidingDrawerを使って同一レイアウトXMLに全てのViewを記述しつつ、 入力部分の描画を切り替えるようにしていました。 タブレットではSlidingDrawerを使わずにViewを配置する事で、画面サイズの違いに対応していました。

今回はViewPagerを使う事で、SlidingDrawerのツメ部分の描画が省略されるなど、若干シンプルになりました。 タブレットではViewPagerを使わずにレイアウトXMLを記述しています。 まったくの同一コードという分けにはいかないので、MainActivity側でViewPagerインスタンスが入手できない事を検出して、Fragmentへのアクセス方法を切り分ける事で、同一コードで対応しました。

今回はViewPagerならではの部分についてまとめていきます。

ViewPagerを使用した画面イメージ Tablet用画面イメージ

対応するバージョン、使用したAPI等々

今回のアプリケーションはAndroid 1.6以降、Android 4.0までをターゲットにしています。 検証のために次のような機器を使用しています。

  • Acer Liquid MT (2.3 800x480)
  • Iconia Tab A500 (3.2 1280x800)
  • Sony Xperia Mini Pro (4.0 320x480)

内部ではFragmentを使うために、android.support.v4パッケージを使用しています。 参考までにActivityクラスのimport文は、次のようになっています。

Activityクラスのimport文抜粋

import net.yadiary.android.actionbarcompat.ActionBarActivity;
...
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.CursorAdapter;
...

ViewPagerがある場合とない場合の切り替え方法

レイアウトXMLにViewPagerの記述があれば、findViewById(R.id.pager)のようなメソッドでインスタンスが取得できるはずです。

課題はFragmentのインスタンスにどのようにアクセスするのかという事です。 通常はFragmentManagerインスタンスを経由して、Fragmentにアクセスしますが、 ViewPagerが設定するFragment Tag名は外部からは(一応)分かりません。

Activity(実際にはFragmentActivityをベースにしたActionBarActivityの子クラス)中のコードは次のようになっています。

onCreateメソッドの抜粋

	protected void onCreate(Bundle state) {
		super.onCreate(state);
		setContentView(R.layout.main);
		viewPager = (ViewPager) findViewById(R.id.pager);
		if (viewPager != null) {
			myAdapter = new MyAdapter(getSupportFragmentManager());
			viewPager.setAdapter(myAdapter);
		}

viewPagerの中に配置するFragmentはMyAdapterクラスのgetItem(int position)メソッドの中でインスタンスを生成しています。 ここら辺はオフィシャルのFragmentPagerAdapterリファレンスを参照してください。

FragmentにアクセスするためのサポートメソッドをActivity中に追加しています。 ViewPagerを使う場合は、アダプターのinstantiateItem(viewPager, position)を使用しています。

FragmentはレイアウトXMLで記述したのでR.id経由で指定していますが、 ViewPagerのインスタンスがnullの場合に、必要な場面は想像できませんが、動的にFragmentを定義する事もできます。

Activityクラスに追加したFragment取得用サポートメソッド

	public Fragment getFragment(int position) {
		Fragment ret = null;
		if (viewPager != null) {
			ret = (Fragment) myAdapter.instantiateItem(viewPager, position);
		} else {
			FragmentManager fm = getSupportFragmentManager();
			if (position == PAGER_PAGE_INPUT) {
				ret = fm.findFragmentById(R.id.fragmentInput);
			} else {
				ret = fm.findFragmentById(R.id.fragmentListView);
			}
		}
		return ret;
	}
レイアウトXML中でのViewPager, Fragmentの記述方法

android.support.v4パッケージを使っている事で、レイアウトXMLの具体的な書き方は、Android 3.0以降に対応したものとは少し変わっています。

ここら辺の書き方で困る場合もありそうなので、該当個所の抜粋だけ載せておきます。

android.support.v4のViewPager, FragmentレイアウトXML記述例

...
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="0.9" >
    </android.support.v4.view.ViewPager>

...

        <fragment
            android:id="@+id/fragmentInput"
            android:name="net.yadiary.android.jpostal.InputFragment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </fragment>

...

要素名をパッケージで指定したり、大文字が小文字だったり、知っていれば何でもないんですけどね。

まとめ

ViewPagerを使う事で画面はシンプルになりましたが、ActionBarを使っているので20〜30ピクセルほどは以前よりも画面を占有するようになりました。

とはいえ、Fragmentに分けた事で内部構造は分割統治が可能になりシンプルになりました。

以前のコードはサポートクラスに処理を切り出したりはしていましたが、ステータス管理の面からは巨大なActivityクラスでした。 android.support.v4パッケージとFragmentを使う事で、互換性を維持しつつ、よりレイアウトXMLを中心としたアプリケーション開発ができるでしょう。

正直なところFragmentを始める前は、解説書を読んでもどういう風に処理を分けたらいいのか、イメージを持つ事が難しかったです。

まずは新規で簡単なアプリを作って慣れてから、古いアプリのActivityをFragment対応にする場合には、レイアウトXMLに対応する新しいFragmentクラスを作って、Activityの処理をFragmentクラスに移すようにするべきでしょう。

いろいろなデバイスに対応するのは面倒ですが、参考になれば幸いです。

0 件のコメント: