質問するログイン新規登録

回答編集履歴

1

質問の意図を勘違いして、長文で回答してしまっていたので、内容を削除しました。

2018/12/05 17:16

投稿

kafumi
kafumi

スコア87

answer CHANGED
@@ -1,66 +1,1 @@
1
- 上記コードでは、 `kakeiboActivity` が `DatePickerDialog.OnDateSetListener` 実装して、 `DatePickk.onCreateDialog()` `kakeiboActivity` `DatePickerDialog` にリスナーとして設定ていすが、`DatePickerDialog` に関する処理は `DatePickk` 内部完結したほうがよいと思います。以下に示すように`DatePickk` 自身が `DatePickerDialog.OnDateSetListener` 実装、Activity向けには、別のコールバックインターフェースを提供するのがよさそうです
2
-
3
- ```kotlin
4
- class DatePickk : DialogFragment(), DatePickerDialog.OnDateSetListener {
5
- var callback: Callback? = null
6
-
7
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
8
- val c = Calendar.getInstance()
9
- val year = c.get(Calendar.YEAR)
10
- val month = c.get(Calendar.MONTH)
11
- val day = c.get(Calendar.DAY_OF_MONTH)
12
-
13
- return DatePickerDialog(activity, this, year, month, day)
14
- }
15
-
16
- override fun onDateSet(view: DatePicker, year: Int, month: Int, dayOfMonth: Int) {
17
- callback?.onDatePicked(year, month, dayOfMonth)
18
- }
19
-
20
- interface Callback {
21
- fun onDatePicked(year: Int, monthOfYear: Int, dayOfMonth: Int)
22
- }
23
- }
24
- ```
25
-
26
- こうすることで、例えば将来、`DatePickk` が `DatePickerDialog` を使うのをやめて、別の方法で日付を選ぶように実装を変えたとしても、`DatePickk` の公開インターフェースを変える必要がないので、`kakeiboActivity` を修正せずにすみます。
27
-
28
- `DatePickk` を上記のように修正した場合、`kakeiboActivity` は次のように、`DatePickerDialog.OnDateSetListener` の代わりに `DatePickk.Callback` を実装して、`onDatePicked()` で選択された日付を受け取ることになります。
29
-
30
- ```kotlin
31
- class kakeiboActivity : FragmentActivity(), DatePickk.Callback {
32
- // 中略...
33
-
34
- override fun onDatePicked(year: Int, monthOfYear: Int, dayOfMonth: Int) {
35
- val str = String.format(Locale.US, "%d/%d", year, monthOfYear+1, dayOfMonth)
36
- calendar.text = str
37
- }
38
- ```
39
-
40
- また、`kakeiboActivity` は自身をコールバックとして `DatePickk.callback` にセットする必要があります。これは、[Androidの公式ガイド](https://developer.android.com/training/basics/fragments/communicating#DefineInterface)にも書かれているように、`FragmentActivity.onAttachFragment(Fragment)` にて行うのが良い方法です。
41
-
42
- ```kotlin
43
- // 良いコールバックのセットの仕方
44
-
45
- // kakeiboActivity
46
- override fun onAttachFragment(fragment: Fragment) {
47
- if (fragment is DatePickk) {
48
- fragment.callback = this
49
- }
50
- }
51
- ```
52
-
53
- 普通に考えると、下のように、Fragmentを生成した直後にコールバックを設定したくなると思いますが、このコードは、画面回転をしたときなどにうまく動きません。例えば、ダイアログ表示中に画面が回転されて、その後、ダイアログで日付が選択されると、`kakeiboActivity.onDatePicked()` が呼びだされない動作となります。
54
-
55
- ```kotlin
56
- // 悪いコールバックのセットの仕方
57
-
58
- // kakeiboActivity
59
- fun showDatePickerDialog1(view: View) {
60
- val newFragment = DatePickk()
61
- newFragment.show(supportFragmentManager, "datePicker")
62
- newFragment.callback = this // ここでコールバックをセットしてはだめ
63
- }
64
- ```
65
-
66
- 画面回転時、Activityは一度破棄されて再生成されますが、これはFragmentも同じで、画面回転時にFragmentが表示されていた場合、Fragmentの状態が保存されてから破棄され、そして自動的に再生成されます。再生成のケースでは`showDatePickerDialog1()` がよばれず、システムが自動的にFragmentのインスタンスを作成するので、上の悪いセットの仕方をしていると、Fragmentにコールバックがセットされません。一方で、`onAttachFragment()` は再生成時にも必ず呼ばれる関数なので、ここでコールバックをセットするようにすれば、自分でFragmentを作成したケースでも、システムがFragmentを再生成したケースでも、もれなくコールバックがセットできる、というわけです。
1
+ 質問意図勘違いして、ActivityとFragmentのやりとりの仕方について長文回答をしてしまったので、内容削除ました