Help us understand the problem. What is going on with this article?

Fragmentを使ってみる

More than 5 years have passed since last update.

以前アプリを組んでた時はActivityだけを使ってたけど、どうやら今はFragmentを使って画面制御をおこなうのが常識になっているみたいですね。
調べたことをメモ。

Fragmentとは?

cf.今さら聞けないActivityとFragmentの使い分け

Fragment以前の問題点

  1. Activity という割と大きなオブジェクトを、タブのホストとタブのコンテンツの数だけメモリに乗せないといけない
  2. いくつかの専用のコンポーネントとガッツリ連携しなければ実現できないため、柔軟性に乏しい

なるほど。

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の定義は以下のように。

fragment_main.xml
<?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とかは割愛)。

HelloFragment.java
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を以下のようにしました。

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を編集。

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は以下のように。

MainActivity.java
    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入門 フラグメント

wakaba260
Webエンジニア - Vim, Ruby, ansible, docker
aiming
オンラインゲームの企画・プロデュース・開発・運営を行う会社。Web+Game+リアルタイム通信技術に力を入れています!
http://aiming-inc.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away