回答編集履歴

1

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

2018/12/05 17:16

投稿

kafumi
kafumi

スコア87

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