2011年5月19日木曜日

Android Fragment を使う

Fragment のサブクラス

DialogFragment
ListFragment
PreferenceFragment
WebViewFragment

■ その他のヘルパークラス

FragmentManager
FragmentTransaction


Activity のレイアウトにフラグメントのレイアウトを挿入する


■ Activity のレイアウトファイルに <fragment> タグで入れる

<?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">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>


各 <fragment> には一意の識別子が必要
識別子をつける方法は次の3つ
 ・ android:id で一意のIDをつける
 ・ android:tag で一意の文字列をつける
 ・ 両方指定しない場合、システムは container view の ID を使う

---
findFragmentById(int id) : 戻り値は見つかった fragment もしくは null
findFragmentByTag(String tag) : 戻り値は見つかった fragment もしくは null
---


■ すでに存在している Activity のレイアウトの ViewGroup にコードで入れる

FragmentTransaction を使う

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();


FragmentTransaction に変更を加えたあとは、commit() を呼ばなければ反映されない。


■ UI なしの Fragment を追加する

Activity で add(Fragment fragment, String tag) を呼び出す。
第1引数が追加するフラグメントのインスタンス、第2引数が追加するフラグメントの文字列識別子("tag")

---
FragmentTransaction add (int containerViewId, Fragment fragment)
 tag に null を指定して add(int, Fragment, String) を呼ぶ

FragmentTransaction add (Fragment fragment, String tag)
 containerViewId に 0 を指定して add(int, Fragment, String) を呼ぶ

FragmentTransaction add (int containerViewId, Fragment fragment, String tag)
 containerViewId : このフラグメントが配置されるコンテナの ID, コンテナに配置されない場合は 0
 fragment : 追加されるフラグメント, 事前に Activity に追加されていてはならない
 tag : フラグメントのタグ名, FragmentManager.findFragmentByTag(String) で使われる

 戻り値 : 同じ FragmentTransaction インスタンス
---


Fragment の管理


・FragmentManager で行う
・FragmetnManager は getFragmentManager() で取得する

FragmentManager fragmentManager = getFragmentManager();

・FragmentManager でできること
  ・findFragmentById() もしくは findFragmentByTag() で Activity に存在する Fragment を取得
  ・popBackStack() でバックスタックの Fragment を pop off (ユーザーが BACK ボタンを押したときと同じ動作)
  ・addOnBackStackChangedListener() を使って、バックスタックの変更用リスナーを登録
  ・FragmentTransaction を開く


Fragment Transaction を行う


・各トランザクションは一度に行いたい変更の組み合わせ
・実行したいすべてのトランザクションを add(), remove(), replace() などを使ってセットし、それから commit() を呼び出してトランザクションを適用させる
・例えば、以前の状態をバックスタックに保存して、別のフラグメントに置き換えるにはこんな感じになる

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

addToBackStack() を呼ぶことで置換トランザクションがバックスタックに保存され、ユーザーはバックボタンを押すことでトランザクションをリバースして前のフラグメントに戻すことができる。

複数のトランザクション(add() や remove())の変更を追加して、addToBackStack() を読んだ場合、commit() が呼ばれる前のすべての変更が1つのトランザクションとしてバックスタックに保存され、BACK キーを押した場合、すべての変更が一緒にリバースされる。

FragmentTransaction に加える変更の順番は、次を除いて重要ではない:
 ・最後に commit() を呼ばなければならない
 ・同じコンテナに複数のフラグメントを追加した場合、追加した順番によって View 階層に現れる順番が決まる

フラグメントを削除するトランザクションを実装する際に、addToBackStack() を呼ばないと、フラグメントは commit 時に破棄され、バック操作で戻すことはできない。一方、addToBackStack() を呼べば、フラグメントの削除時にフラグメントはストップし、バック操作で再開できる。

commit() する前に setTransition() を呼ぶことで、トランザクションのアニメーションを設定できる

・commit() を呼び出し直後にトランザクションが実行されるわけではない
・UI スレッドで実行が可能になったらすぐに実行できるようにスケジュールされる
・しかし、必要であれば、executePendingTransaction() を UI スレッドから呼ぶことで commit() でサブミットされたトランザクションを直ちに実行できる
・他のスレッドがトランザクションに依存しているのでない限り、この方法は必要ない


Activity とやりとりする


Fragment から自身を含む Activity のインスタンスを取得するには getActivity() メソッドを使う

View listView = getActivity().fintViewById(R.id.list);


Activity から自身が含む Fragment のインスタンスを取得するには、findFragmentById() や findFragmentByTag() を使う

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);



■ Activity へのイベントコールバックを作る

Activity と Fragment でイベントを共有するいい方法は、
 ・Fragment にコールバックインタフェースを定義
 ・Activity がそのインタフェースを実装
という方法

例えば、リストの FragmentA とAで選択された記事の詳細を表示する FragmentB が Activity に含まれているとする

FragmentA --- 選択されたリストアイテムを通知 --> Activity
|
FragmentB <-- 選択されたリストに応じた記事を表示 ----


FragmentA --- onListItemClick() で onArticleSelected() 呼び出し --> Activity
|
FragmentB <-- onArticleSelected()内で処理 -----------------------------



public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}


フラグメントが Activity に追加される時点で呼ばれる onAttach() コールバックメソッド内で、Activity をキャストしてインタフェースが実装されているかチェックする

public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}


FragmentA が ListFragment を継承したものだとすると、リストアイテムがクリックされたときに呼ばれる onListItemClick() ないで、インタフェースで定義したメソッドを呼ぶことでイベントを Activity に通知できる。

public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
}
...
}



■ Action Bar にアイテムを追加する

・Fragment は onCreateOptionsMenu() を実装することで、Option Menu (従って、Action Bar)のメニューアイテムに寄与できる
・このメソッドが呼び出しを受けるためには、onCreate() 内で setHasOptionMenu() を呼ばなければならない
・Fragment から Option Menu に追加するアイテムは、既存のメニューアイテムに追加される
・メニューアイテムが選択された場合、Fragment は onOptionsItemSelected() へのコールバックを受け取る
・registerForContextMenu() を呼ぶことで、コンテキストメニューを提供するためにフラグメントのレイアウトに View を登録できる
・ユーザーがコンテキストメニューを開くと、Fragment は onCreateContextMenu() への呼び出しを受け取る
・ユーザーがコンテキストメニューのアイテムを選択すると、Fragment は onContextItemSelected() への呼び出しを受け取る

・Fragment は自身が追加したメニューアイテムがユーザーによって選択されたときに、 on-item-selected callback を受け取るが、一番最初は Activity が対応するコールバックを受け取る
・Activity の on-item-selected callback が選択されたアイテムを処理しない場合、そのイベントは Fragment のコールバックに渡される


■ Fragment のライフサイクル

onAttach()
・onCreate()
onCreateView()
onActivityCreated()
・onStart()
・onResume()
・onPause()
・onStop()
onDestroyView()
・onDestroy()
onDetach()




from http://developer.android.com/guide/topics/fundamentals/fragments.html


参考


http://developer.android.com/guide/topics/fundamentals/fragments.html
http://developer.android.com/guide/topics/ui/menus.html
http://developer.android.com/guide/topics/ui/actionbar.html

 

1 件のコメント: