フラグメント Fragment

01 – Fragment(フラグメント)を理解する

コース紹介

このレッスンは Android の基礎知識があることを前提としています。

基礎がないと、先に進むのは難しいでしょう。

Fragment は「フラグメント」や「断片」と訳されます。

実際には「フラグメント」とするのが正解です。

これは Activity に埋め込まれる UI の一部で、

大きな画面スペースをより合理的かつ効率的に活用できるようにしてくれます。

そのため、タブレットでの使用に非常に適しています。

Activity とよく似ており、レイアウトを持ち、独自のライフサイクルもあります。

ミニ版の Activity と考えると分かりやすいでしょう。

ニュースアプリを開発する場合を考えてみましょう。

スマホ画面であれば、このように設計するかもしれません:

リスト用の Activity を用意し、タップするとニュース詳細の Activity が開くようにする。

しかしタブレットの場合、そうすると画面が大きく空いてしまい、あまり良くありません。

より良い設計は、リストと詳細を1つの Activity 内の2つのフラグメントに分けることです。

タブレット用エミュレーターの作成

タブレットアプリをしっかりテストするために、タブレット用のエミュレーターを作成しましょう。

02 – フラグメントの最初の例

Activity に2つのフラグメントを追加し、スペースを均等に分割する。

FragmentFirst という新しいプロジェクトを作成します。

2つのレイアウトを追加します。1つは左側のフラグメント用、

newlist.xml

もう1つは右側のフラグメント用の newdetail.xml です。

左側のフラグメント

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/buttondialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="2018年 米中貿易戦争" />
</LinearLayout>Code language: HTML, XML (xml)

右側のフラグメント

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0ddb97"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="ニュース内容 ニュース内容" />
</LinearLayout>Code language: HTML, XML (xml)

フラグメントクラスの追加

NewListFragment を追加します。

android.support.v4.app.Fragment を継承します。

そして onCreateView メソッドをオーバーライドし、このフラグメントに newlist の xml を使わせます。

public class NewListFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.newlist,container,false);
        return view;
    }
}Code language: PHP (php)

同じようにニュース詳細フラグメントも追加します。

NewDetailFragment も継承します。

android.support.v4.app.Fragment

public class NewDetailFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.newdetail,container,false);
        return view;
    }
}Code language: PHP (php)

メインアクティビティのレイアウトでフラグメントを呼び出す

android:name=”com.foxdevelop.” 属性を使って、どのフラグメントを使用するか設定します。

メインレイアウトにフラグメントを追加する際、id は必須です。

<?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:id="@+id/newlist_fragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:name="com.foxdevelop.www.fragmentfirst.NewListFragment"
        />
    <fragment
        android:id="@+id/newdetail_fragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:name="com.foxdevelop.www.fragmentfirst.NewDetailFragment"
        />
</LinearLayout>Code language: HTML, XML (xml)

03 – フラグメントの動的追加

フラグメントの真の強みは、アプリの実行中に動的に Activity に追加できることです。

状況に応じて動的にフラグメントを追加することで、より多様な画面設計が可能になります。

例えばニュースアプリの場合、画像ニュース、テキストニュース、動画ニュースなどがあります。ニュースの種類に応じて異なるフラグメントレイアウトを使い分けられます。

new_picture レイアウトを追加してみましょう。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/birdsee"
        />
</LinearLayout>Code language: HTML, XML (xml)

見分けがつくように、画像を1枚表示するだけのものです。

画像用フラグメントクラスの追加

public class NewImageFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.new_picture,container,false);
        return view;
    }
}Code language: PHP (php)

メインレイアウトで FrameLayout を使う

ここではフレームレイアウトを使います。

<?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:id="@+id/newlist_fragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:name="com.foxdevelop.www.fragmentfirst.NewListFragment"
        />

    <FrameLayout
        android:id="@+id/framelayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        ></FrameLayout>
</LinearLayout>Code language: HTML, XML (xml)

左側に2つのボタンを追加

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/buttondialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="2018年 米中貿易戦争" />
    <Button
        android:id="@+id/buttonpicture"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="画像ニュース" />
</LinearLayout>Code language: HTML, XML (xml)

メインアクティビティでボタンにイベントを追加

ボタンにクリックイベントを設定し、押されたボタンに応じて異なるフラグメントを動的に読み込みます。

package com.foxdevelop.www.fragmentfirst;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 2つのボタンにイベントをバインド
        Button b1 = (Button)findViewById(R.id.buttonarticle);
        b1.setOnClickListener(this);
        Button b2 = (Button)findViewById(R.id.buttonpicture);
        b1.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        // 押されたボタンに応じて異なるフラグメントを設定
        switch (view.getId())
        {
            case R.id.buttonarticle:
                setFragement(new NewDetailFragment());
                break;
            case R.id.buttonpicture:
                setFragement(new NewImageFragment());
                break;
            default:
                break;
        }
    }

    // このメソッドでフレームレイアウトにフラグメントを設定
    public void setFragement(Fragment fragement) {
        FragmentManager fragementManager = getSupportFragmentManager();
        // トランザクションを開始
        android.support.v4.app.FragmentTransaction fragmentTransaction = fragementManager.beginTransaction();
        fragmentTransaction.replace(R.id.framelayout,fragement);// フレームレイアウトに渡されたフラグメントを設定
        fragmentTransaction.commit();
    }
}Code language: JavaScript (javascript)

04 – フラグメントでバックスタックを使う

バックスタックについては基礎講座で説明しました。

前の例で、バックキーを押すとアプリがメインアクティビティを終了してプログラム全体が終了してしまいます。これは望ましい動作ではありません。バックキーを押したら前のフラグメントに戻ってほしいのです。

フラグメントでバックスタックをサポートさせるのは実はとても簡単です。

トランザクションをバックスタックに追加するだけです。

// このメソッドでフレームレイアウトにフラグメントを設定
public void setFragement(Fragment fragement) {
    FragmentManager fragementManager = getSupportFragmentManager();
    // トランザクションを開始
    android.support.v4.app.FragmentTransaction fragmentTransaction = fragementManager.beginTransaction();
    fragmentTransaction.replace(R.id.framelayout,fragement);// フレームレイアウトに渡されたフラグメントを設定
    fragmentTransaction.addToBackStack(null);// トランザクションをバックスタックに追加
    fragmentTransaction.commit();
}Code language: JavaScript (javascript)

05 – フラグメントとアクティビティの通信

フラグメントはアクティビティに埋め込まれていますが、実際の関係はそれほど密ではありません。

フラグメントとアクティビティは、実際には独立したクラスとして存在しています。

直接的に通信する手段は用意されていません。

通信を容易にするため、

FragmentManager は findViewById に似たメソッドを提供しており、レイアウトからフラグメントのインスタンスを取得できます。

getFragmentManager android.support.v4.app.Fragment

キャストが必要です。これは本には書かれていませんでした。

android.support.v4.app.Fragment パッケージ(つまり v4 パッケージ)をインポートした場合、

import android.support.v4.app.Fragment;

FragmentManager を取得する際は以下を使う必要があります:

FragmentManager fm = getSupportFragmentManager();

対応するフラグメント側でも、正しいパッケージをインポートしてください!

// アクティビティの onCreate メソッド内でフラグメントのインスタンスを取得
NewListFragment newListFragment = (NewListFragment)getSupportFragmentManager().findFragmentById(R.id.newlist_fragment);
Toast.makeText(this, newListFragment.getId()+"xxxx",Toast.LENGTH_LONG).show();Code language: JavaScript (javascript)

フラグメントからアクティビティを取得

// フラグメントからアクティビティを取得
MainActivity mainActivity = (MainActivity) getActivity();
Toast.makeText(getContext(), mainActivity.getTitle(),Toast.LENGTH_LONG).show();Code language: JavaScript (javascript)

getActivity() メソッドを呼び出せます。

06 – フラグメントのライフサイクル

フラグメントのライフサイクルは Activity とほぼ同じです。

フラグメントにも4つの状態があります。

実行中、一時停止、停止、破棄。

1. 実行中状態:フラグメントが表示されており、所属するアクティビティも実行中の場合、そのフラグメントもアクティブです。

2. 一時停止状態

画面全体を覆わない別のアクティビティがバックスタックの最上位に追加されると、それが管理する表示中のフラグメントは一時停止状態になります。

3. 停止状態

アクティビティが停止状態になると、関連するフラグメントも停止状態になります。あるいは、FragmentTransaction の remove や replace メソッドでフラグメントをアクティビティから削除し、トランザクションのコミット前に addToBackStack を呼び出した場合も停止状態になります。

つまり、停止状態のフラグメントはユーザーには見えず、いつシステムに回収されてもおかしくありません。

4. 破棄状態

フラグメントは常にアクティビティに依存しています。そのため、アクティビティが破棄されると、関連するフラグメントも破棄状態になります。また、FragmentTransaction の remove や replace メソッドでフラグメントを削除し、コミット前に addToBackStack を呼び出さなかった場合も破棄状態になります。

Activity と同様に、Fragment も一連のコールバックメソッドを提供し、ライフサイクルの各段階をカバーします。Activity にあるものはほとんど Fragment にもあります。

同時に、Fragment にはいくつか追加のコールバックメソッドも用意されています。

フラグメント Fragment

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です