【Android】Fragmentから別FragmentのViewを更新するサンプル【Kotlin】

iOSのアプリ開発とAndroidのアプリ開発の仕組みの大きな違いとしてActivityとFragmentがあります。

Androidアプリ開発の際、1つのActivityに対して2つのフラグメントが存在し、FragmentAのクリックイベントを受けてFragmentBの表示を更新する機能を作る機会が有りました。

今回はこちらの機能を含んだかんたんなサンプルを作ってみます。

LiveDataやDatabindingをある程度理解している初心者向けの記事となります。

1. 実現したいこと


FragmentAのボタンをタップしてFragmentBのテキスト表示を更新する。

環境

・MacOS Ventura 13.0
・Android Studio
・Kotlin
・Minimum SDK API 26: Android 8.0(Oreo)

2. 画面構成、設定等

・1つのActivityに対してFragmentを2つ設置する
・FirstFragment(画面上)にはFragment名のTextViewとButtonを設置
・SecondFragment(画面下)にはFragment名のTextViewと表示の更新確認用のTextViewを設置

完成イメージ


3. Android Studioで実際に作ってみる

ここからはAndroidStudioで実際に作って行きます。

3-1. プロジェクトの作成

NewProjectから[Empty Activity]を選択


プロジェクトを作成、プロジェクト名は[SampleApplication]としました

3-2. Fragmentを2つ準備する

プロジェクトの作成が完了したらFragmentを2つ作成します。

パッケージを右クリック→New→Fragment→Fragment(with ViewModel)で作成します。

Fragment名はFirstFragmentとSecondFragmentとしました。

この方法でFragmentを作成するとFirstViewModel,SecondViewModelの様にViewModelクラスのファイルも同時に作成されるので楽です。


3-3. レイアウト

・activity_main

activity_mainの変更点は全体をConstraintLayoutで囲い、内部にFragmentContainerViewを2つ、2つのFragmentContainerViewをGuideLineで仕切りました。

<?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">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/first"
        android:name="com.example.sampleapplication.FirstFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/guideHalf"
        android:layout_margin="10dp"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout="@layout/fragment_first" />
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideHalf"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5" />
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/second"
        android:name="com.example.sampleapplication.SecondFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="@id/guideHalf"
        tools:layout="@layout/fragment_second" />

・fragment_first

全体をLayoutで囲い、内にViewModelへの参照を設定します。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"

3-4. Fragmentのコード

binding.lifecycleOwner = thisを忘れずに追加します。

【FirstFragment】

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    viewModel = ViewModelProvider(this).get(FirstViewModel::class.java)
    val binding = DataBindingUtil.inflate<FragmentFirstBinding>(inflater, R.layout.fragment_first, container, false)
    binding.viewModel = viewModel
    binding.lifecycleOwner = this
    return binding.root
}

【FirstViewModel】

FirstFragmentのレイアウト内のViewの表示を更新する場合、このViewModelクラス内にMutableLiveDataを追加しますが、今回はSecondViewFragmentのViewを更新したいのでライブデータは別のSampleDataと言うファイルに作成します。

SampleDataに"更新されました"の文字列をセットします。

import androidx.lifecycle.ViewModel
class FirstViewModel : ViewModel() {
    fun buttonTapped() {
        SampleData.updateData.value = "更新されました"
    }
}

【SampleData】

SampleDataでは更新仕様の文字列を保持するMutableLiveDataを定義します。

パッケージを右クリック→New→Kotlin Class/File→Objectでファイルを作成します。

import androidx.lifecycle.MutableLiveData
object SampleData {
    val updateData: MutableLiveData<String> by lazy { MutableLiveData<String>() }
}

fragment_second.xmlのTextViewにSampleDataのupdateDataがバインドされているか再度確認します。

【SecondFragment】

SampleDataのバインド、初期値の設定、lifecycleOwnerの設定を行います。

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    viewModel = ViewModelProvider(this).get(SecondViewModel::class.java)
    val binding = DataBindingUtil.inflate<FragmentSecondBinding>(inflater, R.layout.fragment_second, container, false)
    binding.viewModel = viewModel
    binding.lifecycleOwner = this
    binding.sampleData = SampleData
    SampleData.updateData.value =  "データ更新前"
    return binding.root
}

3-5. シミュレータで動作確認

レイアウト側、コード側どちらも実装したので動作確認してみます。

FirstFragmentの更新ボタンをクリックすると


SecondFragmentの文字列「データ更新前」が「更新されました」に変わりました。


3-6. 全体の流れ

更新ボタンをタップ

→ボタンタップをFirstViewModelで検知

→タップのイベント内で&quot;更新されました&quot;の文字列をSampleDataオブジェクト内のLiveDataにセット

→SecondFragmentで値がセットされたことを検知してTextViewに&quot;更新されました&quot;の文字列を反映。

4. まとめ

いかがでしたでしょうか。

今回はFragmentから別のFragmentの表示を更新する方法をかんたんに作成しました。

実際に簡単なサンプルを作ってみることで、より理解が深まりました。

私自身Androidの開発を始めたばかりで、理解できていない点が多々ありますが、このサンプルプログラムが同じようなことを実現したい方の参考になれば幸いです。