※本記事は2021年7月2日に更新しました。
「フラグメントを違うフラグメントに切り替えるのってどうやるの?」
今回はこの疑問に答えていこうと思います。
まずはこちらの動画をご覧ください。
ボタンを押すことで、画面上半分のフラグメントが切り替わります。
今回はフラグメントを別のフラグメントへ切り替える方法について解説します。
前提として、本記事はAndroid Studio 4.2.1を対象に記載します。
また、筆者のAndroid Studioは日本語化しているため、日本語にて解説します。
本記事の使用言語はJavaとなります。
今回はProgate のJavaのレッスンがすべて理解できている、
という前提の上進めていきます。
では早速参りましょう!
※本記事の内容は前回記事と重複する部分が多いです。
こちらの記事から読むことを強くお勧めします。
フラグメントを切り替えてみよう
フラグメントの切り替え方は以下の通りです。
- フラグメント用のレイアウトファイル、javaファイルを用意する
- 画面用のレイアウトファイルにフラグメントを埋め込む場所を用意する
- アクティビティ.javaファイルで、フラグメントを追加する
- ボタンを押してフラグメントが切り替わるように定義する
では、一つ一つ解説していきます。
フラグメント用のレイアウトファイル、javaファイルを用意する
フラグメント用のレイアウトファイル、javaファイルを用意します。
用意の仕方は以下の通りです。
- 左上のファイル⇒新規⇒フラグメント⇒フラグメント(空白)を選択
- フラグメント名、フラグメントレイアウト名を入力する
今回はフラグメント名を『Fragment1』、レイアウト名を『fragment_1』とします。
また、切り替え先のフラグメントを用意する必要があります。
上記手順をもう一度行ってください。
切り替え先のフラグメント名を『Fragment2』、レイアウト名を『fragment_2』とします。
画面用のレイアウトファイルにフラグメントを埋め込む場所を用意する
続いて、画面用のレイアウトファイルへのフラグメントを埋め込む場所の用意です。
以下のコードをレイアウトファイルに埋め込むことで、フラグメントが埋め込まれます。
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:layout_width="0dp"
android:layout_height="0dp"/>
※Constraintlayoutの場合はButton等と同様、位置の制約が必要となります。
必要に応じて付け加えてください。
(後述のサンプルコードには記載されています。)
前回との違いは、android:name属性がないことです。
今回フラグメントをjavaファイルから追加するため、
ここでは埋め込むフラグメントを宣言していません。
埋め込む場所だけを定義する、というイメージです。
アクティビティ.javaファイルで、フラグメントを追加する
次に、アクティビティ.javaファイルで、フラグメントを追加します。
プログラムでフラグメントを管理する方法についての解説です。
手順は以下の通りです。
- FragmentManagerを用意する(getSupportFragmentManager)
- Transactionを開始する(beginTransaction)
- フラグメントの状態変化を最適化する(setReorderingAllowed)
- フラグメントの追加を行う(add)
- Transactionを実行する(commit)
FragmentManagerは、フラグメントを管理するオブジェクトです。
このオブジェクトがTransactionを使うことで、フラグメントの追加を行います。
Transactionはいくつかの命令をまとめて処理するときに使われるものです。
命令のうちどれかに不都合があると、処理自体が停止されます。
イメージ図は以下の通りです。
いくつかの処理をまとめて、commitで処理を実行するという手順です。
今回は処理が少ないため、効果は薄いですが、
データベースなどを扱う際に効果を発揮する考え方となります。
手順3の最適化処理はtrueに設定することで実行されます。
なくてもプログラムに支障ないのですが、公式から推奨されているため記載しています。
手順4のaddの第1引数は用意した埋め込む場所(fragmentContainerView)のID、
第2引数は埋め込むフラグメントのjavaファイルです。
第3引数はActivityからデータを受け取る際の引数です。
今回は不要のため、nullとします。
ここで、実際に記述するコードを見てみましょう。
MainActivityのonCreateに以下の文を記載します。
//MainActivityが強制終了された時は、savedInstanceStateにFragmentの情報が入ってonCreateが呼び出される
//最初の1回だけフラグメントを作成するため、nullで条件分岐
if (savedInstanceState == null) {
//FragmentManagerのインスタンスを取得し、FragmentTransactionを開始
getSupportFragmentManager().beginTransaction()
//トランザクションに関与するフラグメントの状態変更を最適化
.setReorderingAllowed(true)
//フラグメントを追加するよう指示。第3引数はデータを受け渡す場合に使用
.add(R.id.fragmentContainerView, Fragment1.class, null)
//上記のトランザクションに設定した内容を実行
.commit();
}
全体をif節で囲っています。
これはActivityが作られた最初の一回だけ動作するようにしたいためです。
Activityがメモリ圧迫などで強制終了されるとsavedInstanceStateに
Fragmentの情報が残ります。
再度起動した際にsavedInstanceStateを読み込むため、
Fragmentの作成処理をしようとしても既にある、という状態になってしまいます。
これを防ぐため、savedInstanceStateがnullの時だけ実行するようにしています。
以上が、フラグメントの作成処理となります。
ボタンを押してフラグメントが切り替わるように定義する
最後に、ボタンを押してフラグメントが切り替わるように定義します。
といっても作業内容は上の追加の時とほとんど同じです。
異なるのは『add』が『replace』になる点です。
replaceは今あるフラグメントを終了させ、新たなフラグメントを追加する処理です。
これにより、新しいフラグメントに切り替えることができます。
サンプルコード
今回のサンプルコードを記載します。
今回のアプリはGitHubに公開しています。
下記URLの『SampleFragmentChange』です。
是非参考にしてください。
今回のアプリでは複数のボタンの処理を行います。
複数のボタンの処理のやり方については、次の記事をご一読ください。
文字列リソースファイル string.xml
<resources>
<string name="app_name">SampleFragmentChange</string>
<string name="bt_fragment1">フラグメント1を表示</string>
<string name="bt_fragment2">フラグメント2を表示</string>
<string name="fragment1">フラグメント1</string>
<string name="fragment2">フラグメント2</string>
</resources>
フラグメント1レイアウトファイル fragment_1.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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#4169e1"
tools:context=".Fragment1">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fragment1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
フラグメント1javaファイル Fragment1.java
package com.zerokaraapp.samplefragmentchange;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// フラグメントのレイアウトをインフレート
return inflater.inflate(R.layout.fragment_1, container, false);
}
}
フラグメント2レイアウトファイル fragment_2.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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff4500"
tools:context=".Fragment2">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fragment2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
フラグメント2javaファイル Fragment2.java
package com.zerokaraapp.samplefragmentchange;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// フラグメントのレイアウトをインフレート
return inflater.inflate(R.layout.fragment_2, container, false);
}
}
画面レイアウトファイル activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_fragment1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_fragment2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button1" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/button1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
画面アクティビティファイル MainActivity.java
package com.zerokaraapp.samplefragmentchange;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//MainActivityが強制終了された時は、savedInstanceStateにFragmentの情報が入ってonCreateが呼び出される
//最初の1回だけフラグメントを作成するため、nullで条件分岐
if (savedInstanceState == null) {
//FragmentManagerのインスタンスを取得し、FragmentTransactionを開始
getSupportFragmentManager().beginTransaction()
//トランザクションに関与するフラグメントの状態変更を最適化
.setReorderingAllowed(true)
//フラグメントを追加するよう指示。第3引数はデータを受け渡す場合に使用
.add(R.id.fragmentContainerView, Fragment1.class, null)
//上記のトランザクションに設定した内容を実行
.commit();
}
//レイアウトからボタンオブジェクトをそれぞれ取得
Button button1 = findViewById(R.id.button1);
Button button2 = findViewById(R.id.button2);
//ボタンオブジェクトににリスナをそれぞれ設定
button1.setOnClickListener(new ButtonClickListener());
button2.setOnClickListener(new ButtonClickListener());
}
//ボタンを押したときの動作を定義するリスナクラス
private class ButtonClickListener implements View.OnClickListener{
//ボタンを押したときの動作を定義
@Override
public void onClick(View view){
//ボタンのIDによって条件分岐
switch(view.getId()){
//ボタン1が押された場合
case R.id.button1:
//FragmentManagerのインスタンスを取得し、FragmentTransactionを開始
getSupportFragmentManager().beginTransaction()
//トランザクションに関与するフラグメントの状態変更を最適化
.setReorderingAllowed(true)
//フラグメント1に入れ替えするよう指示。第3引数はデータを受け渡す場合に使用
.replace(R.id.fragmentContainerView, Fragment1.class, null)
//上記のトランザクションに設定した内容を実行
.commit();
break;
//ボタン2が押された場合
case R.id.button2:
//FragmentManagerのインスタンスを取得し、FragmentTransactionを開始
getSupportFragmentManager().beginTransaction()
//トランザクションに関与するフラグメントの状態変更を最適化
.setReorderingAllowed(true)
//フラグメント2に入れ替えするよう指示。第3引数はデータを受け渡す場合に使用
.replace(R.id.fragmentContainerView, Fragment2.class, null)
//上記のトランザクションに設定した内容を実行
.commit();
break;
default:
break;
}
}
}
}
※javaファイルについて、1行目packageの部分は人によって異なります。
コピペする場合は2行目以降をコピペしてください。
ここまで出来たら一度アプリを実行してみましょう。
一番上の動画のアプリのように動作すれば成功です。
まとめ
本記事ではフラグメントをプログラムから呼び出し、
切り替える方法について解説しました。
フラグメントを切り替えられるようになると、
画面レイアウトのぐっと広がると思います。
ぜひ使ってみてください。
本記事が初心者の方の参考になれば幸いです。
コメント