【Android / Java】 フラグメントでの画面遷移と戻る処理

# はじめに
Android Studio で Javaを用いたモバイルアプリの開発を学んでいます。
layoutファイルについてはあまり力を入れて記述していません（ざっくりです）。画面切り替えの動作等にフォーカスしているため、stringファイルは使用しませんでした。

### 学んだ内容
- ``FragmentManager`` ``FragmentTransaction`` を用いた表示フラグメントの切り替え（画面遷移）
- ``addToBackStack`` ``popBackStack`` を用いたアクションバー戻るボタンクリックで戻る動作

# 学習のために作成したサンプルアプリの概要
1つのアクティビティにメインとなるフラグメントを用意し、そこからボタンを押すことによって2つのフラグメントに行き来ができるだけの簡単なアプリ。
アクティビティ：１つ
フラグメント：３つ（メイン１つ、サブ２つ）

この①、②ボタンでサブフラグメント１、サブフラグメント２にそれぞれ遷移する


メイン画面（MainFragment）|
----------------------|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/8008f895-cb47-1d21-9120-7b80bcc0c2cb.png" width="300">|

サブ１画面（SubFragment1）|
----------------------|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/f0e56d5a-bbde-49d2-dfd6-52b54f1834a8.png" width="300">|

サブ２画面（SubFragment2）|
----------------------|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/1803de0d-0416-063b-6dea-1a9b1f2f9111.png" width="300">|



# ディレクトリ構成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/373c92cf-3afd-1451-5f9c-097c43c43600.png" width="300">


# 各javaファイルの処理内容
* MainActivity.java
  - MainFragmentの表示
  - SubFragmentのアクションバーに戻るボタン「←」を表示するメソッドを定義

* MainFragment.java
  - Fragmentの表示を切り替えるメソッドを定義
  - 設置したボタンのクリックリスナとFragmentを切り替えるメソッドの呼び出し
* SubFragment(1, 2).java
  - アクションバーに戻るボタンの表示
  - 戻るボタンをクリックしたときに戻る処理を記述


# ファイルのコードと解説
それぞれ、ファイルのコードを記載

### layoutファイル

```activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activityMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</FrameLayout>

```


```fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fragmentMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="25dp"
        android:text="メインフラグメントだよ"/>

    <Button
        android:id="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="250dp"
        android:layout_marginLeft="70dp"
        android:textSize="25dp"
        android:text="①"/>

    <Button
        android:id="@+id/bt2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="250dp"
        android:layout_marginLeft="250dp"
        android:textSize="25dp"
        android:text="②"/>

</FrameLayout>

```

```fragment_sub1.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/SubFragment1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SubFragment1">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="25dp"
        android:text="サブフラグメント1だよ"/>

</FrameLayout>

```

```fragment_sub2.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/SubFragment2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SubFragment2">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="25dp"
        android:text="サブフラグメント２だよ" />

</FrameLayout>

```

### javaファイル

※package, import 文は記載していません。

#### MainActivity.java

```MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // メソッドを呼び出し、デフォルトでMainFragmentを表示
        addFragment(new MainFragment());
    }

    // Fragmentを表示させるメソッドを定義（表示したいFragmentを引数として渡す）
    private void addFragment(Fragment fragment) {
        // フラグメントマネージャーの取得
        FragmentManager manager = getSupportFragmentManager();
        // フラグメントトランザクションの開始
        FragmentTransaction transaction = manager.beginTransaction();
        // MainFragmentを追加
        transaction.add(R.id.activityMain, fragment);
        // フラグメントトランザクションのコミット。コミットすることでFragmentの状態が反映される
        transaction.commit();
    }

    // 戻るボタン「←」をアクションバー（上部バー）にセットするメソッドを定義
    public void setupBackButton(boolean enableBackButton) {
        // アクションバーを取得
        ActionBar actionBar = getSupportActionBar();
        // アクションバーに戻るボタン「←」をセット（引数が true: 表示、false: 非表示）
        actionBar.setDisplayHomeAsUpEnabled(enableBackButton);
    }
}

```

画面表示に指定したフラグメントを表示したいので ``transaction.add`` をする

引数はこのような値をいれる

``transaction.add(追加先のレイアウト画面部品のR値, 追加する（表示したい）Fragmentオブジェクト);``


#### MainFragment.java


```MainFragment.java

public class MainFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // フラグメントで表示する画面をlayoutファイルからインフレートする
        View view = inflater.inflate(R.layout.fragment_main, container, false);

        // 所属している親アクティビティを取得
        MainActivity activity = (MainActivity) getActivity();
        // アクションバーにタイトルをセット
        activity.setTitle("メインフラグメント");
        // 戻るボタンは非表示にする（MainFragmentでは戻るボタン不要）
        // ここをfalseにしておかないとサブフラグメントから戻ってきた際に戻るボタンが表示されたままになってしまう
        activity.setupBackButton(false);

        // ボタン要素を取得
        Button bt1 = view.findViewById(R.id.bt1);
        Button bt2 = view.findViewById(R.id.bt2);

        // ①ボタンをクリックした時の処理
        bt1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // SubFragment1に遷移させる
                replaceFragment(new SubFragment1());
            }
        });

        // ②ボタンをクリックした時の処理
        bt2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // SubFragment2に遷移させる
                replaceFragment(new SubFragment2());
            }
        });

        return view;
    }

    // 表示させるFragmentを切り替えるメソッドを定義（表示したいFragmentを引数として渡す）
    private void replaceFragment(Fragment fragment) {
        // フラグメントマネージャーの取得
        FragmentManager manager = getFragmentManager(); // アクティビティではgetSupportFragmentManager()?
        // フラグメントトランザクションの開始
        FragmentTransaction transaction = manager.beginTransaction();
        // レイアウトをfragmentに置き換え（追加）
        transaction.replace(R.id.activityMain, fragment);
        // 置き換えのトランザクションをバックスタックに保存する
        transaction.addToBackStack(null);
        // フラグメントトランザクションをコミット
        transaction.commit();
    }
}

```

すでにメインフラグメントを表示しており、ボタンクリックで表示を切り替えたいので ``transaction.replace`` を使用
引数は ``add`` の処理と同じイメージ
``transaction.replace(表示先のレイアウト画面部品のR値, 置き換える（表示したい）Fragmentオブジェクト);``

メインフラグメントから遷移したサブフラグメントではアクションバーの戻るボタン「←」でメインフラグメントに戻れるようにしたいため
``transaction.addToBackStack(null);`` 
を記述することにより、遷移前に表示していたフラグメントを保存している。
この記述により遷移先のフラグメントで
``getFragmentManager().popBackStack();``
を呼び出すことで、戻るボタン「←」をクリックしたときに戻る処理ができるようになる

#### SubFragment1.java / SubFragment2.java


```SubFragment1.java

public class SubFragment1 extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // フラグメントで表示する画面をlayoutファイルからインフレートする
        View view = inflater.inflate(R.layout.fragment_sub1, container, false);

        // 所属親アクティビティを取得
        MainActivity activity = (MainActivity) getActivity();
        // アクションバーにタイトルをセット
        activity.setTitle("サブフラグメント1");
        // 戻るボタンを表示する
        activity.setupBackButton(true);

        // この記述でフラグメントでアクションバーメニューが使えるようになる
        setHasOptionsMenu(true);

        return view;
    }

    // アクションバーボタンを押した時の処理
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            // android.R.id.homeで戻るボタン「←」を押した時の動作を検知
            case android.R.id.home:
                // 遷移前に表示していたFragmentに戻る処理を実行
                getFragmentManager().popBackStack();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

```

```SubFragment2.java

public class SubFragment2 extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // フラグメントで表示する画面をlayoutファイルからインフレートする
        View view = inflater.inflate(R.layout.fragment_sub2, container, false);

        // 所属親アクティビティを取得
        MainActivity activity = (MainActivity) getActivity();
        // アクションバーにタイトルをセット
        activity.setTitle("サブフラグメント２");
        // 戻るボタンを表示する
        activity.setupBackButton(true);

        // この記述でフラグメントでアクションバーメニューが使えるようになる
        setHasOptionsMenu(true);

        // View viewのが良い？
        return view;
    }

    // アクションバーのボタンを押した時の処理
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            // 戻るボタン「←」を押した時android.R.id.homeに値が入る
            case android.R.id.home:
                // 遷移前に表示していたFragmentに戻る処理を実行
                getFragmentManager().popBackStack();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

```

``getFragmentManager().popBackStack();``この記述で一つ前のフラグメントに戻る。



# 最後に
今回は内容に含みませんでしたが、今後フラグメント間のデータの受け渡しについても書けたら良いと思っています。

Java / Android を学び始めて１ヶ月弱程度の初心者です。ご指摘等ありましたらコメントください。

# 参考にさせていただいた資料
- https://developer.android.com/training/basics/fragments/fragment-ui?hl=ja
- https://qiita.com/tagfa/items/a1e2b7302ad36aa99dab
- https://akira-watson.com/android/fragment-code.html


非常にわかりやすく助かりました！ありがとうございました！