android:子フラグメントから親フラグメントに値を渡したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 118

mimon

score 7

前提・実現したいこと

タイトル通り、AフラグメントからBフラグメントに遷移時、Bフラグメントで入力した値をAフラグメントに渡す方法がわかりません。

具体的には、
・日付を表示しているAフラグメントで、日付をクリック
・カレンダービューが貼ってあるBフラグメントを表示
・カレンダーで日付を選択時にBフラグメントを閉じて選択した日付をAフラグメントに表示

以上になります。

フラグメントの遷移はナビゲーションエディタでNavHostFragmentを使用して行っています。
Aフラグメント表示時には初期表示の日付をSafeArgsで受け取っています。
Bフラグメントの非表示はpopBackStack()でひとつ前(Aフラグメント)に戻っています。

簡単なサンプルを載せておきます。
※Aフラグメントは別のフラグメントから呼び出しています。
※onCreateView部分は省略しています。

AFragment.tk

class AFragment: Fragment() {
val args: AFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 初期表示用日付
        val date:String = args.showDate

        if(date:String == "")
            // 当日を表示
     else
            // パラメタの日付を表示
        //***********************************************     
            // ここでパラメタではなく、Bで選択時はBの日付をセットしたい!!
        // Bからargs.showDateを書き換えられる?
        // または別のパラメタを渡せる?
            //***********************************************
        textDate.text = date

     // 日付クリック時
     textDate.setOnClickListener {
       // Bフラグメントを表示
       val action = AFragmentDirections.actionAFragmentToBFragment()
            Navigation.findNavController(view as View).navigate(action)
    }
}

BFragment.tk

class BFragment: Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        calendarView.setOnDateChangeListener { view, year, month, dayOfMonth ->
            // カレンダーの選択日付をセット
            val setDate = year.toString() + java.lang.String.format("%0${2}d", 
                month + 1) + java.lang.String.format("%0${2}d", dayOfMonth)
        
            //*********************************************** 
            // ここで、Aフラグメントになんとかして値を渡したい!!
            //***********************************************       
       
            // Aフラグメントに戻る
            FragmentManager?.popBackStack()
    }
}

試したこと

単純に、BからAに直接セットできないかと、popBackStackする前に
activity?.textDate?.setText(setDate) なんてしてみました。もちろん空でしたが。
popBackStackでは前のFragmentの情報はセットされたままですので、選択した日付だけ更新して表示したいです。
今回はカレンダービューを使用していますが、例えばBで入力した値をAに表示する単純な動きを実装したいだけなのです。
Fragmentの初期表示時にパラメタを渡す方法はわかるのですが、ライフサイクルが終わるときに前のFragment(存在する場合ですが)に値を渡すことは可能でしょうか?
基本的なことですがよろしくお願いいたします。

補足情報(FW/ツールのバージョンなど)

Android Studio 3.4.1
SDK 29
kotlin

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

Navigationコンポーネントをお使いなら、次の1,2の方法があります。

1. navigationリソースで「戻る」アクションを定義する

厳密にはpopBackStackで1つ戻る遷移ではなく、2つ戻って新しいFragmentを表示する動きになりますが、それっぽいアクションを定義することができます。app:popUpToInclusiveを付けるところがポイントです。

<action
    android:id="@+id/action_B_to_A"
    app:destination="@+id/a_fragment"
    app:popUpTo="@+id/a_fragment"
    app:popUpToInclusive="true"
    />

プログラム側からは普通にSafeArgsを渡して上記の遷移の処理を呼べばOKです。

val action = BFragmentDirections.actionBToA(...)
findNavController().navigate(action)

2.共有するViewModelを通してFragment間の通信を行う

単純な値の受け渡しなら1.の方法で済むと思いますが、ある程度やり取りが複雑になるようなら共有のViewModelを利用するのが現在Googleの推奨している方法です。ViewModelをすでに導入済み、もしくは導入に抵抗がないようでしたら後々便利なのでこの方法が良いかもしれません。

ドキュメント: https://developer.android.com/topic/libraries/architecture/viewmodel#sharing


3. setTargetFragment/getTargetFragmentでやり取り

Navigationコンポーネントの利用は諦めてFragment同士のやり取りを実装するなら、こちらの方法になると思います。

参考: https://tech.mokelab.com/android/Fragment/result.html

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/12 14:08

    いつも回答ありがとうございます!

    やはりNavigationでの実装で統一したく、1の方法で解決しました。
    2についはいずれ必ず必要になると思いますので勉強します。
    3については、以後必ずやこちらの方法で・・・
    (s.m_1さんにせっかく教えていただいたのに、私の呼び方がバカでしたね)
    まだまだ基本編なので頑張ります。ありがとうございました!

    キャンセル

0

他に解決策はあるかもしれませんが、私なら interface を使うと思います。

interface OnDateChangeListener {
  fun onDateChange(渡したい値: 型)
}
class BFragment : Fragment() {

  var listener: OnDateChangeListene? = null

  calendarView.setOnDateChangeListener { view, year, month, dayOfMonth ->
    // カレンダーの選択日付をセット
    val setDate = year.toString() + java.lang.String.format("%0${2}d", 
    month + 1) + java.lang.String.format("%0${2}d", dayOfMonth)
        
    //*********************************************** 
    // ここで、Aフラグメントになんとかして値を渡したい!!
    listener?.onDateChange(渡したい値)      
       
    // Aフラグメントに戻る
    FragmentManager?.popBackStack()
 }

}
class AFragment : Fragment(), OnDateChangeListener {

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     super.onViewCreated(view, savedInstanceState)
     // BFragment のインスタンス生成
     bFragment = ***
     bFragment.listener = this
  }

  override fun onDateChange(渡したい値: 型) {
    // BFragment で設定された日付を取得
  }

}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/11 17:00

    どうにもうまく取得できず、結局この部分のみナビゲーションでの遷移を諦め
    fragmentManagerを使用して
    addToBackStack(null)
    replace(id.XX, bFragment)
    commit()
    の流れで遷移させるとコールバックできました。
    知識不足でせっかくのアドバイスを活かせませんでしたが、interfaceの実装を教えていただき大変助かりました。ありがとうございました!!

    キャンセル

  • 2019/09/11 19:33

    この方法だと画面回転等でFragmentが再生成された場合にコールバックが解除されてしまって良くないです。せめてsetTargetFragment/getTargetFragmentでやり取りするようにするべきです。

    キャンセル

  • 2019/09/12 11:19

    >kakajikaさん

    はっ!そうなのですねっ!?
    本編の回答もありがとうございます。
    まだまだ基本編の状態で大変お恥ずかしいですが、
    すぐにまずは1の方法を試してみます。
    ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる