Fragmentを動的に変化させる


PreferenceFragmentを使って2Paneな設定画面を作成する」「Fragmentを使ってMenuを動的に作成する」にて、Android3.0で追加された、Fragmentについて紹介してきました。

今回も、Fragmentの機能を紹介していきます。
FragmentはActivityなどと同じ様に、ライフサイクルを持ちます。
それらを確認した上で、Button操作による、Fragmentの追加/編集を行っていきましょう。

Fragmentのライフサイクル

Fragmentにはライフサイクルがあります。
それは以下の通り。基本的な流れはActivityと同様ですが、UIの生成等に関わるMethodが追加されています。

〜コアライフサイクルMethod〜
1. onAttach(Activity)
→Activityに関連付けされた際に一度だけ呼ばれる。
2. onCreate(Bundle)
→Fragmentの初期化処理を行う。
3. onCreateView(LayoutInflater, ViewGroup, Bundle)
→Fragmentに関連付けるViewを作成し、returnする。
4. onActivityCreated(Bundle)
→親となるActivityの「onCreate」の終了を知らせる。
5. onStart()
→Activityの「onStart」に基づき開始される。
6. onResume()
→Activityの「onResume」に基づき開始される。

〜終了/復帰処理にあたるMethod〜
1. onPause()
→Activityが「onPause」になったり、Fragmentが変更更新され、
操作を受け付けなくなった場合に呼び出される。
2. onStop()
→フォアグラウンドで無くなった場合に呼び出される。
3. onDestroyView()
→Fragmentの内部のViewリソースの整理を行う。
4. onDestroy()
→Fragmentが殺される最後に呼び出される。
5. onDetach()
→Activityの関連付けから外された時に呼び出される。

以上のMethodを踏まえ、全てOverrideし、Logを仕込む。

    public static class UpFragment extends Fragment implements OnClickListener {
    	public static String TAG = "FragmentLifeCycle";

    	@Override
    	public void onAttach(Activity act){
    		super.onAttach(act);
    		Log.d(TAG,"Fragment-onAttach");
    	}

    	@Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    		Log.d(TAG,"Fragment-onCreate");
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
    		Log.d(TAG,"Fragment-onCreateView");
        	View v = inflater.inflate(R.layout.component_button,container,false);
        	return v;
        }

        @Override
        public void onActivityCreated(Bundle bundle){
        	super.onActivityCreated(bundle);
    		Log.d(TAG,"Fragment-onActivityCreated");
        }

        @Override
        public void onStart(){
        	super.onStart();
    		Log.d(TAG,"Fragment-onStart");
        }

        @Override
        public void onResume(){
        	super.onResume();
    		Log.d(TAG,"Fragment-onResume");
        }

        @Override
        public void onPause(){
        	super.onPause();
    		Log.d(TAG,"Fragment-onPause");
        }

        @Override
        public void onStop(){
        	super.onStop();
    		Log.d(TAG,"Fragment-onStop");
        }

        @Override
        public void onDestroyView(){
        	super.onDestroyView();
    		Log.d(TAG,"Fragment-onDestroyView");
        }

        @Override
        public void onDestroy(){
        	super.onDestroy();
    		Log.d(TAG,"Fragment-onDestroy");
        }

        @Override
        public void onDetach(){
        	super.onDetach();
    		Log.d(TAG,"Fragment-onDetach");
        }
}

Fragmentの追加/編集

まず、Fragmentを使用する場合は、 Fragment class を継承したクラスを用意します。
今回用意したのは、上記 Log 出力を入れ込んだ物(小変更 UpFragment)と、TextViewを表示する、
DownFragmentを作成しました。

Fragment内部にTextViewを持つため、それを「onCreateView」において呼び出しています。
「findViewById」を常日頃「onCreate」で呼び出している様なイメージでしょうか。

呼び出すViewの内容は、続けて引用する component_text.xml で定義しています。
LayoutInflater#inflateは、「リストビューをカスタマイズする」でも使用しています。

Fragment内部に持つ、コンポーネントにアクセスする場合のポイントは以下になります。
1.LayoutInflater#inflateにて、Layoutファイルを指定
2.指定したLayoutファイルから、findViewByIdでコンポーネントを指定
3.それぞれのコンポーネントに対する処理(TextView#setText()等)

    public static class DownFragment extends Fragment {

    	@Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
        	View v = inflater.inflate(R.layout.component_text,container,false);

        	return v;
        }
    }

    public static class UpFragment extends Fragment implements OnClickListener {
    	int cnt;
    	UpFragment(int counter){
    		cnt = counter;
    	}
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
        	View v = inflater.inflate(R.layout.component_button,container,false);
        	View bt = v.findViewById(R.id.button1);
        	((Button)bt).setOnClickListener(this);
        	((Button)bt).setText("Load the TextView-Fragment"+cnt);

        	return v;
        }
}

component_text.xml

<TextView android:text="This is a Textview.." android:id="@+id/textView1"
	android:layout_width="wrap_content" android:layout_height="wrap_content"
	xmlns:android="http://schemas.android.com/apk/res/android"></TextView>

次に、Fragmentの登録部分を作成していきます。
基本的な流れは「Fragmentを使ってMenuを動的に作成する」で紹介した流れと同じです。

まずFragmentTransactionをFragmentManagerから取得します。
次に、Flagmentを登録(replace)します。
replaceは、既に「R.id.fragment01」のViewIdで追加指定(add等)しているFragment要素を破棄(remove)
することと同様の操作になります。
アニメーションの指定には、以下を指定します。
#和演算を行うことで、同時に指定できます。

  • TRANSIT_FRAGMENT_OPEN

→開く時のアニメーションを行うよう指定

  • TRANSIT_FRAGMENT_CLOSE

→閉じる時のアニメーションを行うよう指定

  • TRANSIT_NONE

→無し

    void addFragmentToStack() {
        // フラグメントのインスタンスを生成する。
        Fragment newFragment = new UpFragment(counter++);

        // ActivityにFragmentを登録する。
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        // Layout位置先の指定
        ft.replace(R.id.fragment01, newFragment);
        // Fragmentの変化時のアニメーションを指定
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        ft.addToBackStack(null);
        ft.commit();
    }

この処理を、Button押下時に呼び出す様に作成します。
#処理自体は割愛します。

最後に、実行結果のキャプチャを添付します。

実行時の画面は以下左図の通りです。
「Load the TextView-Fragment0」を押下すると、真ん中の図の様にTextViewが挿入されます。
0番目のFragmentから呼び出されたので、末尾に0が付加されています。
「Button」を押下すると、「Load the TextView-Fragment0」の末尾の数値がカウントアップします。
これは、Fragmentがスタック状に重なっている様子を示しています。(右図)

 

同様に、「Load the TextView-Fragment1」を押下すると、以下左図の様に、
TextViewの末尾の数値が増加します。これも同様にスタック状に重なっている事を示しています。
この先も繰り返しで、数値を変化させていけます。

本サンプルで面白いのは、BACKボタンを押下した時の動作です。
従来のACTIVITYであれば、BACKボタンを押下した場合、画面ごと切り替わってしまいますが、
本サンプルでは、Fragmentが一枚戻る形、すなわち、図が一つ戻る様に動作します。
Fragmentを用いることで、UIの可能性がまた一つ広がるのではないでしょうか。

#2011/03/19 追記

3 Comments