FragmentをActivityからコードで設定してHello Worldを表示させてみましたが、今度はActivityからFragment画面への切り替えをしてみましょう。いわゆる画面遷移ですが、Activity間の移動ではないので重くありません。
2021.2.1
ActivityからFragmentに画面遷移
Fragmentが現実的に使われるケースを想定して、
- 任意のタイミングで起動
- パラメータを渡してインスタンス生成
- Activityに戻る
という観点を考えてみます。
任意のタイミングで起動
これはButtonを使って任意のタイミングでFragmentを起動できます。
ActivityにボタンタップでFragmentのインスタンス生成を行います。これは前回のコードで記述したFragmentの張り付け部分を使います。
Activity:
1 2 3 4 5 6 7 8 9 10 |
Button button01 = findViewById(R.id.button); button01.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view){ FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.replace(R.id.container, new SampleFragment()); fragmentTransaction.commit(); } }); |
パラメータを渡してインスタンス生成
Fragmentの場合コンストラクタの引数で渡したり、setterから取り出したりすると問題があります。
Fragmentは引数のないコンストラクタが必要で、フレームワークは必要に応じFragmentを再インスタンス化することが多く、引数のないコンストラクタが利用できなと例外が発生してしまいます。
引数を前提としてインスタンスを生成させていると、システムからFragmentの再生成されるときにクラッシュするかもしれないわけです。
解決方法としては、
静的な newInstance()
を使ってフラグメントをインスタンス化する
これがベストプラクティスと言われています。
Activity:
1 2 3 |
× fragmentTransaction.replace(R.id.container, new SampleFragment("Fragment")); 〇 fragmentTransaction.replace(R.id.container, SampleFragment.newInstance("Fragment")); |
newでインスタンス生成して、Bundleに設定したいパラメータをsetArguments()で追加します。
Fragment:
1 2 3 4 5 6 7 8 9 10 11 12 |
public static SampleFragment newInstance(String str){ // Fragemnt01 インスタンス生成 SampleFragment fragment = new SampleFragment (); // Bundle にパラメータを設定 Bundle barg = new Bundle(); barg.putString("Message", str); fragment.setArguments(barg); return fragment; } |
Bundleから getArguments() で取り出す
1 2 |
Bundle args = getArguments(); String str = args.getString("Message"); |
Activityに戻る
ButtonでFragmentが張り付けを行い、Activityに戻りたい場合にバックスタックを使います。
1 |
fragmentTransaction.addToBackStack(null); |
これをトランザクションに設定すると、トランザクションの開始時のFragment状態がスタックに積まれまれて、戻るボタンで前の状態に戻ることが可能になります。
サンプルコード
ActivityにFragmentに画面遷移、”Fragment”の文字列をTextViewに表示させる。(実際はactvity_mainにあるcontainerにFragmentを張り付け)
BackボタンでFragmentからActivityに戻る
まとめてみます。
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//package your.package.name; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import android.widget.Button; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button01 = findViewById(R.id.button); if(savedInstanceState == null){ // lambda button01.setOnClickListener( v -> { FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); // BackStackを設定 fragmentTransaction.addToBackStack(null); // パラメータを設定 fragmentTransaction.replace(R.id.container, SampleFragment.newInstance("Fragment")); fragmentTransaction.commit(); }); } } } |
SampleFragment .java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//package your.package.name; import androidx.fragment.app.Fragment; import androidx.annotation.NonNull; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class SampleFragment extends Fragment { public static SampleFragment newInstance(String str){ // Fragemnt01 インスタンス生成 SampleFragment fragment = new SampleFragment (); // Bundle にパラメータを設定 Bundle barg = new Bundle(); barg.putString("Message", str); fragment.setArguments(barg); return fragment; } // FragmentのViewを生成して返す @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_main, container, false); } @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); Bundle args = getArguments(); if(args != null ){ String str = args.getString("Message"); TextView textView = view.findViewById(R.id.text_fragment); textView.setText(str); } } } |
onViewCreated() はonCreateView()のViewを作成の直後に呼ばれます。ですからonCreateView()に入れ込んでいる記述も多く見かけます。
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?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" android:background="#dfe" tools:context=".MainActivity"> <TextView android:text="@string/activity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button" android:text="@string/button" android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <androidx.fragment.app.FragmentContainerView android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> |
fragment_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?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" android:background="#fde" tools:context=".MainActivity"> <TextView android:id="@+id/text_fragment" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
strings.xml
1 2 3 4 5 |
<resources> <string name="app_name">Your App Name</string> <string name="button">Button</string> <string name="activity">Activity</string> </resources> |
サンプル動画
Fragmentはactivity_mainのcontainerに張り付けているためButtonが残ってしまいますが、containerを画面一杯にするようにレイアウトすればこれを避けることができます。
関連ページ:
- Fragment をHello World から始める
- Fragment コードで記述する
- Activity と Fragment の画面遷移
- Fragment から別の Fragment に画面遷移させてみる
References:
androidx.fragment.app | Android Developers
FragmentTransaction
フラグメント | Android Developers