#30 2020年版、AndroidでFragmentの結果を呼び出し元に伝える
注意:本記事はアルファ版ライブラリを使用しています。製品のコードを書き換える場合は正式版がリリースされてからにしましょう。
Fragment:1.3.0-alpha04
Fragmentのバージョン1.3.0-alpha04が2020年4月29日にリリースされています。このバージョンから、
Fragment
の
startActivityForResult() / onActivityResult() /
requestPermissions() / onRequestPermissionsResult()
がdeprecatedになっています。
onActivityResult
deprecatedになったメソッドの中にonActivityResult
も入っています。
このメソッドはフラグメントで呼び出し元に結果を伝える方法として使われてきました。
では、これからはどのようにして呼び出し元に結果を伝えればよいでしょうか?
setFragmentResult
まずは結果を伝える側です。新しいメソッドとしてFragmentManager
にsetFragmentResult()
というメソッドが追加されています。第1引数にはリクエストコードに相当する文字列を指定し、第2引数で
結果データをBundle
型で指定します。Intent
型ではないので、既存コードは書き換えが必要になります。
//データの入れ物
val data = Bundle()
data.putString("text", text)
// FragmentManager経由で結果を伝える
parentFragmentManager.setFragmentResult("input", data)
setFragmentResultListener
続いて結果を受け取る側です。今まではonActivityResult()
をオーバーライドし、
そこで処理を書いていましたが、これからはsetFragmentResultListener
を使います。
リスナーなので、onCreate()
あたりでセットするとよいでしょう。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setFragmentResultListener("input") { key, data ->
val text = data.getString("text", "")
// 結果を使った処理
}
}
第1引数はリクエストコードに相当する文字列を指定します。第2引数では結果を受け取ったときに実行するブロックを指定します。
なお、setFragmentResultListener()
はFragment
の拡張関数として定義されており、中身は次のようになっています。
fun Fragment.setFragmentResultListener(
requestKey: String,
listener: ((resultKey: String, bundle: Bundle) -> Unit)?
) {
parentFragmentManager.setFragmentResultListener(requestKey, this, listener)
}
FragmentManager
のsetFragmentResultListener()
を呼んでいるだけですが、第2引数に
LifecyclerOwner
としてフラグメントを渡しています。これは、フラグメントが
開始状態(ON_START)になった時点で、未処理の結果処理を行い、
破棄された(ON_DESTROY)ときに自動でリスナーの解除を行うために使用しているようです。
// FragmentManagerはJavaで記述されています
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_START) {
// once we are started, check for any stored results
Bundle storedResult = mResults.get(requestKey);
if (storedResult != null) {
// if there is a result, fire the callback
listener.onFragmentResult(requestKey, storedResult);
// and clear the result
setFragmentResult(requestKey, null);
}
}
if (event == Lifecycle.Event.ON_DESTROY) {
lifecycle.removeObserver(this);
mResultListeners.remove(requestKey);
}
}
};
(リクエストキーに対するリスナーのセットを忘れると、結果のBundle
がmResults
から消されず、ずっとメモリ上に生存しちゃいますね。。。)
まとめ
Fragment:1.3.0-alpha04で変更となった、呼び出し元フラグメントに結果を伝える方法を紹介しました。 新方式は画面遷移にNavigationライブラリを使用している際に威力を発揮するので、正式版が出たら書き換えていきましょう。