以前アプリを組んでた時はActivityだけを使ってたけど、どうやら今はFragmentを使って画面制御をおこなうのが常識になっているみたいですね。
調べたことをメモ。
#Fragmentとは?
cf.今さら聞けないActivityとFragmentの使い分け
##Fragment以前の問題点
- Activity という割と大きなオブジェクトを、タブのホストとタブのコンテンツの数だけメモリに乗せないといけない
- いくつかの専用のコンポーネントとガッツリ連携しなければ実現できないため、柔軟性に乏しい
なるほど。
##Fragmentのメリット
UI を持ち、その UI の振る舞いを管理する、Activity の中に組み込むことが出来るコンポーネントが Fragment です。つまり、これによって、タグだけでは実現できなかった、コントローラのロジックを使いまわす事ができるようになったのです。
また、ActionBar にかぎらず、ViewPager のような View コンポーネントからも Fragment が取り扱えます。以前のタブと異なり、実装に用いるインタフェースも単純化されたため、タブほどの実装コストも掛けずに同等の機能を実装することが出来るようになりました。
includeでxmlの定義を使いまわすことは出来たけど、それだとコントローラのロジックを使いまわせない。
そこで、UIとロジックをカプセル化してコンポーネントにしたのがFragment、ってことですかね。
確かに、以前組んだ時も(かなり単純なアプリだったのに)Activityの肥大化は今思うと気になるレベルだった気がします。
ActivityはMVCでいうControllerの役割だけど、Controllerは極力小さくってのはWebフレームワークを学んだ今ならよくわかりますね。
ということは、FragmentはViewとControllerの仲立ちをするってことだから……ViewModelってことになるんだろうか?(もっとデザパタ勉強しよう……)
#Fragmentを使ってみる
今回生成するFragmentの定義は以下のように。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/push_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/push_button" />
<TextView
android:id="@+id/view_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
ここまで来て、あれ、じゃあactivityにfragmentを取り込むのはどうするんだ……ってことに気づきました。
#静的にFragmentをActivityで利用する
##Fragmentクラスを継承したクラスの作成
cf.フラグメントの一番簡単な利用方法
まずはここを参考に、Fragmentクラスを継承してHelloFragmentクラスを作成。
ちなみに【Android】Fragmentを使ってみるによると、
Fragmentの実装
Fragmentを実装するには、最低限、以下のメソッドを実装する必要があります。
|Activityの状態|Fragmentで呼ばれるコールバック|
|:------------:|:-------------------------|
|onCreate()|システムが、Fragmentを作成したときに呼び出される。コンポーネントの初期化処理などをここで行う。|
|onCreateView()|FragmentのUIが描画されるタイミングでよびだされる。FragmentのレイアウトのRootになっているViewをここでinflateする。|
|onPause()|Fragmentが停止するときによばれる。Fragmentで変更されたステータスの保存はここで行う。|
他にも様々なライフサイクルメソッドが存在しますが、今回は割愛します。
って書いてますが、onCreateViewだけでもよさそうです(なんででしょう?)。
以下実装(package、importとかは割愛)。
public class HelloFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_hello, null);
}
}
これでOK。自動生成されるものとreturnの値が違うのに注意。
LayoutInflaterはxmlファイルからViewを生成するためのクラスで、inflateメソッドの第一引数にparseしたいxmlのファイル名を渡すことで、生成されたViewクラスのオブジェクトを返すようです。
第二引数の値に関してはこちらのサイトを参照してnullにしておいたけど、何が適切なのかは結局よくわかってないです。。
第三引数は公式のAPIを見る限り、第二引数がnullなら無くて良いみたいですね。
##Activityのxmlの編集
次に、main_activity.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
android:name="com.example.helloandroid.HelloFragment"
android:id="@+id/fragment1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</LinearLayout>
fragmentのandroid:nameは上で作成したFragmentクラスのクラス名です。
二個ほどwarningが出るけど、パフォーマンスのためにうんたらってやつなので今回はスルーします。
あとはリソースファイルとしてstring.xmlを編集。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">HelloAndroid</string>
<string name="push_button">Push!</string>
<string name="action_settings">Settings</string>
<string name="hello_android">Hello, Android!</string>
</resources>
##MainActivityの編集
MainActivity.javaを編集します。
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();
}
なんてものが出てきていますが、FragmentManagerは動的にFragmentを切り替えるためのクラスのようなので今回はここをすぱっと削除(main_activity.xmlを編集したのでR.id.containerがないってエラーも出ますし)。
イベントハンドラとしてリスナーをボタンに設定したため、最終的にonCreateは以下のように。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// pushbuttonがクリックされると、viewtextに文字列を表示する
findViewById(R.id.push_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TextView viewtext = (TextView) findViewById(R.id.view_text);
viewtext.setText(getText(R.string.hello_android));
}
});
}
##動作確認
Genymotionで動作確認。
正常に動くようです。
最初はFragmentの存在を知らず、fragment_main.xmlだけ編集して「えっ……なんでや!前動いたのに!」ってなってました。
Androidは進化早いですね……。
次Android触るときは動的なFragmentの生成に挑戦してみたいと思います(Rubyの勉強との兼ね合いもあるんでいつかはわかりませんw)。
その他参考など(自分用メモ)
cf.Android初心者におすすめする「Fragmentによるシンプルで効率的なUI実装」
cf.ViewGroupクラス
cf.Android入門 フラグメント