タブレット時代のレイアウト FragmentLayout
Androidは解像度の異なる端末がいろいろ出てきて悩みどころ。
タブレット型に関してはFragments API使えばiPadっぽいレイアウトが組めるらしいけど現状3.0のみだし。
iPadとiPhoneのようにマーケットが分かれていれば片方のみ対応とかできるけどAndroidはそうもいかないし...
とか、いろいろ困ってる人たちに朗報が!
Google、Android 3.0の「Fragments API」で非互換性問題を解消へ
http://japan.internet.com/allnet/20110309/8.html
Android Developers Blog「Fragments For ALL」
http://android-developers.blogspot.com/2011/03/fragments-for-all.html
なんとFragments APIが1.6までの後方互換が効くようになった。
ってことでやってみよう。
まず、「Android SDK and AVD Manager」の「Available Packages」に
Android Compatibility package
が増えているのでインストール。
そうするとSDKのディレクトリにandroid-compatibilityフォルダができていて、
その下にv4/android-support-v4.jarがあるのでライブラリに追加する。
これがFragmentsの後方互換ライブラリ。
ではアプリ作成。
作るのはリストのアイテムを選択すると詳細画面が表示される、というシンプルなもの。
レイアウトファイルの作成
まず、縦向きと横向き用のレイアウトを用意
縦:layout/fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment class="com.sample.MainActivity$TitlesFragment" android:id="@+id/titles" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout>
横:layout-land/fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment class="com.sample.MainActivity$TitlesFragment" android:id="@+id/titles" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> <FrameLayout android:id="@+id/details" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"/> </LinearLayout>
詳細画面のレイアウト:layout/details.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/detail_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp"/> </FrameLayout>
※リスト画面のレイアウトはプログラム内で作ってしまうのでいらない
アクティビティの作成
これがなかなか面倒くさい。ApiDemosにあるソースを拝借して必要な部分のみ記述した。
package com.sample; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends FragmentActivity { private static final String[] ITEMS = { "A", "B", "C" }; private static final String[] DETAILS = { "ラテン文字(アルファベット)の1番目の文字。小文字は a 。ギリシャ文字のΑ(アルファ)に由来し、キリル文字のАに相当する。", "ラテン文字(アルファベット)の2番目の文字。ギリシャ文字のΒ(ベータ)に由来する。小文字は b 。キリル文字のБ、Вと同系である。", "C は、ラテン文字(アルファベット)の3番目の文字。小文字は c 。" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_layout); } /** * ポートレイト(縦向き)のアクティビティ */ public static class DetailsActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { // ランドスケープ(横向き)モードの場合、このアクティビティはいらない finish(); return; } if (savedInstanceState == null) { // フラグメント(右ペイン)を作成 DetailsFragment details = new DetailsFragment(); details.setArguments(getIntent().getExtras()); getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); } } } /** * 左のペイン */ public static class TitlesFragment extends ListFragment { boolean mDualPane; int mCurCheckPosition = 0; int mShownCheckPosition = -1; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // リストの作成 setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_checked, ITEMS)); // デュアルモードかどうかを確認 View detailsFrame = getActivity().findViewById(R.id.details); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // 前回の選択位置を取得 mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); mShownCheckPosition = savedInstanceState.getInt("shownChoice", -1); } if (mDualPane) { getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); showDetails(mCurCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); outState.putInt("shownChoice", mShownCheckPosition); } @Override public void onListItemClick(ListView l, View v, int position, long id) { showDetails(position); } void showDetails(int index) { mCurCheckPosition = index; if (mDualPane) { // デュアルモードの場合は選択されているアイテムをハイライト getListView().setItemChecked(index, true); if (mShownCheckPosition != mCurCheckPosition) { // 別のアイテムが選択されたら対応する画面を作成 DetailsFragment df = DetailsFragment.newInstance(index); // トランザクションを開始して既存のフラグメントと入れ替える FragmentTransaction ft = getFragmentManager() .beginTransaction(); ft.replace(R.id.details, df); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); mShownCheckPosition = index; } } else { // デュアルモードではない場合、詳細画面を起動 Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra("index", index); startActivity(intent); } } } /** * 右のペイン */ public static class DetailsFragment extends Fragment { public static DetailsFragment newInstance(int index) { DetailsFragment f = new DetailsFragment(); // リストに対応するインデックスを仕込む Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // ポートレイトでリスト画面の場合は詳細画面を作らない return null; } View view = inflater.inflate(R.layout.details, null); TextView textView = (TextView) view.findViewById(R.id.detail_text); textView.setText(DETAILS[getArguments().getInt("index", 0)]); return view; } } }