回答編集履歴
5
微修正
test
CHANGED
@@ -41,7 +41,7 @@
|
|
41
41
|
}
|
42
42
|
}
|
43
43
|
```
|
44
|
-
Page0Fragment.java
|
44
|
+
Page0Fragment.java (これを MainFragment の ViewPager page:0 に載せる)
|
45
45
|
```java
|
46
46
|
import android.os.Bundle;
|
47
47
|
import android.view.View;
|
@@ -99,7 +99,7 @@
|
|
99
99
|
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
100
100
|
|
101
101
|
Button addButton = view.findViewById(R.id.add_button);
|
102
|
-
addButton.setOnClickListener(v -> transitionToPass(
|
102
|
+
addButton.setOnClickListener(v -> transitionToPass());
|
103
103
|
|
104
104
|
ListView budgetListView = view.findViewById(R.id.budget_listview);
|
105
105
|
Adapter adapter = new Adapter();
|
@@ -108,8 +108,8 @@
|
|
108
108
|
budgetListView.setAdapter(adapter);
|
109
109
|
}
|
110
110
|
|
111
|
-
private void transitionToPass(
|
111
|
+
private void transitionToPass() {
|
112
|
-
@IdRes int containerViewId = ((View)
|
112
|
+
@IdRes int containerViewId = ((View)getView().getParent()).getId();
|
113
113
|
getParentFragmentManager().beginTransaction()
|
114
114
|
.replace(containerViewId, new PassFragment())
|
115
115
|
.commit();
|
@@ -215,7 +215,7 @@
|
|
215
215
|
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
216
216
|
|
217
217
|
Button cancelButton = view.findViewById(R.id.cancel_button);
|
218
|
-
cancelButton.setOnClickListener(v -> transitionToReceive(
|
218
|
+
cancelButton.setOnClickListener(v -> transitionToReceive());
|
219
219
|
|
220
220
|
Button saveButton = view.findViewById(R.id.save_button);
|
221
221
|
saveButton.setEnabled(false);
|
@@ -235,12 +235,12 @@
|
|
235
235
|
|
236
236
|
saveButton.setOnClickListener(v -> {
|
237
237
|
viewModel.addBudgetName(budgetNameEdit.getText().toString());
|
238
|
-
transitionToReceive(
|
238
|
+
transitionToReceive();
|
239
239
|
});
|
240
240
|
}
|
241
241
|
|
242
|
-
private void transitionToReceive(
|
242
|
+
private void transitionToReceive() {
|
243
|
-
@IdRes int containerViewId = ((View)
|
243
|
+
@IdRes int containerViewId = ((View)getView().getParent()).getId();
|
244
244
|
getParentFragmentManager().beginTransaction()
|
245
245
|
.replace(containerViewId, new ReceiveFragment())
|
246
246
|
.commit();
|
@@ -253,8 +253,8 @@
|
|
253
253
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
254
254
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
255
255
|
android:layout_margin="8dp"
|
256
|
-
android:layout_width="
|
256
|
+
android:layout_width="match_parent"
|
257
|
-
android:layout_height="
|
257
|
+
android:layout_height="match_parent">
|
258
258
|
|
259
259
|
<EditText
|
260
260
|
android:id="@+id/budget_name_edit"
|
@@ -272,17 +272,13 @@
|
|
272
272
|
android:layout_height="wrap_content"
|
273
273
|
android:text="CANCEL"
|
274
274
|
android:layout_marginEnd="10dp"
|
275
|
-
android:layout_marginStart="50dp"
|
276
|
-
app:layout_constraintBottom_toBottomOf="parent"
|
277
275
|
app:layout_constraintEnd_toStartOf="@id/save_button"
|
278
|
-
app:layout_constraintStart_toStartOf="parent"
|
279
276
|
app:layout_constraintTop_toTopOf="@id/save_button" />
|
280
277
|
|
281
278
|
<Button
|
282
279
|
android:id="@+id/save_button"
|
283
280
|
android:layout_width="wrap_content"
|
284
281
|
android:layout_height="wrap_content"
|
285
|
-
android:layout_marginTop="10dp"
|
286
282
|
android:text="SAVE"
|
287
283
|
app:layout_constraintBottom_toBottomOf="parent"
|
288
284
|
app:layout_constraintEnd_toEndOf="parent"
|
4
修正抜け
test
CHANGED
@@ -191,7 +191,7 @@
|
|
191
191
|
|
192
192
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
193
193
|
```
|
194
|
-
PassFragment.java
|
194
|
+
PassFragment.java
|
195
195
|
```java
|
196
196
|
import android.os.Bundle;
|
197
197
|
|
3
Page0Fragment を追加、 PassFragment を ReceiveFragment と切り替える形に
test
CHANGED
@@ -19,7 +19,7 @@
|
|
19
19
|
---
|
20
20
|
全体の構造が
|
21
21
|
![Activity内の各フラグメントとViewModelの関係](https://ddjkaamml8q8x.cloudfront.net/questions/2024-03-09/4fb59ba6-d1d8-4086-87be-e6fb95a83c6f.png)
|
22
|
-
であれば、 PassFragment はダイアログにしたほうが簡単だと思います。
|
22
|
+
であれば、 ~~PassFragment はダイアログにしたほうが簡単だと~~ page:0 上で二つのフラグメントを切り替える FragmentContainerView を作るのが自然かと 思います。
|
23
23
|
|
24
24
|
MainViewModel.java
|
25
25
|
```java
|
@@ -41,12 +41,44 @@
|
|
41
41
|
}
|
42
42
|
}
|
43
43
|
```
|
44
|
+
Page0Fragment.java
|
45
|
+
```java
|
46
|
+
import android.os.Bundle;
|
47
|
+
import android.view.View;
|
48
|
+
|
49
|
+
import androidx.annotation.*;
|
50
|
+
import androidx.fragment.app.*;
|
51
|
+
|
52
|
+
public class Page0Fragment extends Fragment {
|
53
|
+
Page0Fragment() {
|
54
|
+
super(R.layout.fragment_page0);
|
55
|
+
}
|
56
|
+
|
57
|
+
@Override
|
58
|
+
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
59
|
+
super.onViewCreated(view, savedInstanceState);
|
60
|
+
|
61
|
+
if(savedInstanceState == null) {
|
62
|
+
getChildFragmentManager().beginTransaction()
|
63
|
+
.replace(R.id.fragment_container_view, new ReceiveFragment())
|
64
|
+
.commit();
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
```
|
69
|
+
res/layout/fragment_page0.xml
|
70
|
+
```xml
|
71
|
+
<?xml version="1.0" encoding="utf-8"?>
|
72
|
+
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
73
|
+
android:id="@+id/fragment_container_view"
|
74
|
+
android:layout_width="match_parent"
|
75
|
+
android:layout_height="match_parent" />
|
76
|
+
```
|
44
77
|
ReceiveFragment.java
|
45
78
|
```java
|
46
79
|
import android.os.Bundle;
|
47
80
|
|
48
|
-
import androidx.annotation.
|
81
|
+
import androidx.annotation.*;
|
49
|
-
import androidx.annotation.Nullable;
|
50
82
|
import androidx.fragment.app.Fragment;
|
51
83
|
import androidx.lifecycle.ViewModelProvider;
|
52
84
|
|
@@ -67,13 +99,20 @@
|
|
67
99
|
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
68
100
|
|
69
101
|
Button addButton = view.findViewById(R.id.add_button);
|
70
|
-
addButton.setOnClickListener(v ->
|
102
|
+
addButton.setOnClickListener(v -> transitionToPass(view));
|
71
103
|
|
72
104
|
ListView budgetListView = view.findViewById(R.id.budget_listview);
|
73
105
|
Adapter adapter = new Adapter();
|
74
106
|
//MainViewModel の budgetNameListLiveData が設定 (setValue/postValue) されたら、その値をパラメータとして adapter.setList() を呼ぶように設定
|
75
107
|
viewModel.getBudgetNameList().observe(getViewLifecycleOwner(), adapter::setList);
|
76
108
|
budgetListView.setAdapter(adapter);
|
109
|
+
}
|
110
|
+
|
111
|
+
private void transitionToPass(View view) {
|
112
|
+
@IdRes int containerViewId = ((View)view.getParent()).getId();
|
113
|
+
getParentFragmentManager().beginTransaction()
|
114
|
+
.replace(containerViewId, new PassFragment())
|
115
|
+
.commit();
|
77
116
|
}
|
78
117
|
|
79
118
|
private static class Adapter extends BaseAdapter {
|
@@ -156,8 +195,7 @@
|
|
156
195
|
```java
|
157
196
|
import android.os.Bundle;
|
158
197
|
|
159
|
-
import androidx.annotation.
|
198
|
+
import androidx.annotation.*;
|
160
|
-
import androidx.annotation.Nullable;
|
161
199
|
import androidx.fragment.app.*;
|
162
200
|
import androidx.lifecycle.ViewModelProvider;
|
163
201
|
|
@@ -165,7 +203,7 @@
|
|
165
203
|
import android.view.View;
|
166
204
|
import android.widget.*;
|
167
205
|
|
168
|
-
public class PassFragment extends
|
206
|
+
public class PassFragment extends Fragment {
|
169
207
|
public PassFragment() {
|
170
208
|
super(R.layout.fragment_pass);
|
171
209
|
}
|
@@ -177,7 +215,7 @@
|
|
177
215
|
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
178
216
|
|
179
217
|
Button cancelButton = view.findViewById(R.id.cancel_button);
|
180
|
-
cancelButton.setOnClickListener(v ->
|
218
|
+
cancelButton.setOnClickListener(v -> transitionToReceive(view));
|
181
219
|
|
182
220
|
Button saveButton = view.findViewById(R.id.save_button);
|
183
221
|
saveButton.setEnabled(false);
|
@@ -197,8 +235,15 @@
|
|
197
235
|
|
198
236
|
saveButton.setOnClickListener(v -> {
|
199
237
|
viewModel.addBudgetName(budgetNameEdit.getText().toString());
|
200
|
-
|
238
|
+
transitionToReceive(view);
|
201
239
|
});
|
240
|
+
}
|
241
|
+
|
242
|
+
private void transitionToReceive(View view) {
|
243
|
+
@IdRes int containerViewId = ((View)view.getParent()).getId();
|
244
|
+
getParentFragmentManager().beginTransaction()
|
245
|
+
.replace(containerViewId, new ReceiveFragment())
|
246
|
+
.commit();
|
202
247
|
}
|
203
248
|
}
|
204
249
|
```
|
2
追加
test
CHANGED
@@ -15,3 +15,233 @@
|
|
15
15
|
new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
16
16
|
```
|
17
17
|
とすれば、同じアクティビティから同じモノを得られます。
|
18
|
+
|
19
|
+
---
|
20
|
+
全体の構造が
|
21
|
+
![Activity内の各フラグメントとViewModelの関係](https://ddjkaamml8q8x.cloudfront.net/questions/2024-03-09/4fb59ba6-d1d8-4086-87be-e6fb95a83c6f.png)
|
22
|
+
であれば、 PassFragment はダイアログにしたほうが簡単だと思います。
|
23
|
+
|
24
|
+
MainViewModel.java
|
25
|
+
```java
|
26
|
+
import androidx.annotation.NonNull;
|
27
|
+
import androidx.lifecycle.*;
|
28
|
+
|
29
|
+
import java.util.*;
|
30
|
+
|
31
|
+
public class MainViewModel extends ViewModel {
|
32
|
+
private MutableLiveData<List<String>> budgetNameListLiveData = new MutableLiveData<>(new ArrayList<>());
|
33
|
+
LiveData<List<String>> getBudgetNameList() {
|
34
|
+
//getValue したリストを書き換えられないように、 "Collections.unmodifiableList を返す LiveData" を返す
|
35
|
+
return Transformations.switchMap(budgetNameListLiveData, e -> new MutableLiveData<>(Collections.unmodifiableList(e)));
|
36
|
+
}
|
37
|
+
void addBudgetName(@NonNull String name) {
|
38
|
+
List<String> list = budgetNameListLiveData.getValue();
|
39
|
+
list.add(name);
|
40
|
+
budgetNameListLiveData.setValue(list);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
```
|
44
|
+
ReceiveFragment.java
|
45
|
+
```java
|
46
|
+
import android.os.Bundle;
|
47
|
+
|
48
|
+
import androidx.annotation.NonNull;
|
49
|
+
import androidx.annotation.Nullable;
|
50
|
+
import androidx.fragment.app.Fragment;
|
51
|
+
import androidx.lifecycle.ViewModelProvider;
|
52
|
+
|
53
|
+
import android.view.*;
|
54
|
+
import android.widget.*;
|
55
|
+
|
56
|
+
import java.util.*;
|
57
|
+
|
58
|
+
public class ReceiveFragment extends Fragment {
|
59
|
+
public ReceiveFragment() {
|
60
|
+
super(R.layout.fragment_receive);
|
61
|
+
}
|
62
|
+
|
63
|
+
@Override
|
64
|
+
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
65
|
+
super.onViewCreated(view, savedInstanceState);
|
66
|
+
|
67
|
+
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
68
|
+
|
69
|
+
Button addButton = view.findViewById(R.id.add_button);
|
70
|
+
addButton.setOnClickListener(v -> new PassFragment().show(getChildFragmentManager(), null));
|
71
|
+
|
72
|
+
ListView budgetListView = view.findViewById(R.id.budget_listview);
|
73
|
+
Adapter adapter = new Adapter();
|
74
|
+
//MainViewModel の budgetNameListLiveData が設定 (setValue/postValue) されたら、その値をパラメータとして adapter.setList() を呼ぶように設定
|
75
|
+
viewModel.getBudgetNameList().observe(getViewLifecycleOwner(), adapter::setList);
|
76
|
+
budgetListView.setAdapter(adapter);
|
77
|
+
}
|
78
|
+
|
79
|
+
private static class Adapter extends BaseAdapter {
|
80
|
+
private List<String> list = Collections.emptyList();
|
81
|
+
|
82
|
+
@SuppressWarnings("notifyDataSetChanged") //IDE での警告を抑制
|
83
|
+
void setList(List<String> list) {
|
84
|
+
this.list = new ArrayList(list); //防御コピー
|
85
|
+
notifyDataSetChanged();
|
86
|
+
}
|
87
|
+
|
88
|
+
@Override
|
89
|
+
public int getCount() {
|
90
|
+
return list.size();
|
91
|
+
}
|
92
|
+
|
93
|
+
@Override
|
94
|
+
public Object getItem(int position) {
|
95
|
+
return position;
|
96
|
+
}
|
97
|
+
|
98
|
+
@Override
|
99
|
+
public long getItemId(int position) {
|
100
|
+
return position;
|
101
|
+
}
|
102
|
+
|
103
|
+
@Override
|
104
|
+
public View getView(int position, View convertView, ViewGroup parent) {
|
105
|
+
return (convertView == null ? new ViewHolder(parent) : (ViewHolder)convertView.getTag())
|
106
|
+
.bind(list.get(position));
|
107
|
+
}
|
108
|
+
|
109
|
+
private class ViewHolder {
|
110
|
+
private final View itemView;
|
111
|
+
private final TextView text1;
|
112
|
+
|
113
|
+
ViewHolder(ViewGroup parent) {
|
114
|
+
itemView = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
|
115
|
+
itemView.setTag(this);
|
116
|
+
text1 = itemView.findViewById(android.R.id.text1);
|
117
|
+
}
|
118
|
+
|
119
|
+
View bind(String name) {
|
120
|
+
text1.setText(name);
|
121
|
+
return itemView;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
```
|
127
|
+
res/layout/fragment_receive.xml
|
128
|
+
```xml
|
129
|
+
<?xml version="1.0" encoding="utf-8"?>
|
130
|
+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
131
|
+
xmlns:app="http://schemas.android.com/apk/res-auto"
|
132
|
+
android:layout_width="match_parent"
|
133
|
+
android:layout_height="match_parent">
|
134
|
+
|
135
|
+
<ListView
|
136
|
+
android:id="@+id/budget_listview"
|
137
|
+
android:layout_width="0dp"
|
138
|
+
android:layout_height="0dp"
|
139
|
+
app:layout_constraintBottom_toTopOf="@id/add_button"
|
140
|
+
app:layout_constraintEnd_toEndOf="parent"
|
141
|
+
app:layout_constraintStart_toStartOf="parent"
|
142
|
+
app:layout_constraintTop_toTopOf="parent" />
|
143
|
+
|
144
|
+
<Button
|
145
|
+
android:id="@+id/add_button"
|
146
|
+
android:layout_width="wrap_content"
|
147
|
+
android:layout_height="wrap_content"
|
148
|
+
android:text="ADD"
|
149
|
+
app:layout_constraintBottom_toBottomOf="parent"
|
150
|
+
app:layout_constraintEnd_toEndOf="parent"
|
151
|
+
app:layout_constraintStart_toStartOf="parent" />
|
152
|
+
|
153
|
+
</androidx.constraintlayout.widget.ConstraintLayout>
|
154
|
+
```
|
155
|
+
PassFragment.java (ダイアログ)
|
156
|
+
```java
|
157
|
+
import android.os.Bundle;
|
158
|
+
|
159
|
+
import androidx.annotation.NonNull;
|
160
|
+
import androidx.annotation.Nullable;
|
161
|
+
import androidx.fragment.app.*;
|
162
|
+
import androidx.lifecycle.ViewModelProvider;
|
163
|
+
|
164
|
+
import android.text.*;
|
165
|
+
import android.view.View;
|
166
|
+
import android.widget.*;
|
167
|
+
|
168
|
+
public class PassFragment extends DialogFragment {
|
169
|
+
public PassFragment() {
|
170
|
+
super(R.layout.fragment_pass);
|
171
|
+
}
|
172
|
+
|
173
|
+
@Override
|
174
|
+
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
175
|
+
super.onViewCreated(view, savedInstanceState);
|
176
|
+
|
177
|
+
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|
178
|
+
|
179
|
+
Button cancelButton = view.findViewById(R.id.cancel_button);
|
180
|
+
cancelButton.setOnClickListener(v -> dismiss());
|
181
|
+
|
182
|
+
Button saveButton = view.findViewById(R.id.save_button);
|
183
|
+
saveButton.setEnabled(false);
|
184
|
+
|
185
|
+
EditText budgetNameEdit = view.findViewById(R.id.budget_name_edit);
|
186
|
+
//名前の入力状態によって addButton を使えるか使えないかにする (これによって saveButton 押下時に有効/無効の判定が不要になる)
|
187
|
+
budgetNameEdit.addTextChangedListener(new TextWatcher() {
|
188
|
+
@Override
|
189
|
+
public void beforeTextChanged(CharSequence s, int start, int count, int after) { /*no process*/ }
|
190
|
+
@Override
|
191
|
+
public void onTextChanged(CharSequence s, int start, int before, int count) { /*no process*/ }
|
192
|
+
@Override
|
193
|
+
public void afterTextChanged(Editable s) {
|
194
|
+
saveButton.setEnabled(s.length() > 0);
|
195
|
+
}
|
196
|
+
});
|
197
|
+
|
198
|
+
saveButton.setOnClickListener(v -> {
|
199
|
+
viewModel.addBudgetName(budgetNameEdit.getText().toString());
|
200
|
+
dismiss();
|
201
|
+
});
|
202
|
+
}
|
203
|
+
}
|
204
|
+
```
|
205
|
+
res/layout/fragment_pass.xml
|
206
|
+
```xml
|
207
|
+
<?xml version="1.0" encoding="utf-8"?>
|
208
|
+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
209
|
+
xmlns:app="http://schemas.android.com/apk/res-auto"
|
210
|
+
android:layout_margin="8dp"
|
211
|
+
android:layout_width="wrap_content"
|
212
|
+
android:layout_height="wrap_content">
|
213
|
+
|
214
|
+
<EditText
|
215
|
+
android:id="@+id/budget_name_edit"
|
216
|
+
android:layout_width="0dp"
|
217
|
+
android:layout_height="wrap_content"
|
218
|
+
android:hint="BudgetName"
|
219
|
+
app:layout_constraintBottom_toTopOf="@id/save_button"
|
220
|
+
app:layout_constraintEnd_toEndOf="parent"
|
221
|
+
app:layout_constraintStart_toStartOf="parent"
|
222
|
+
app:layout_constraintTop_toTopOf="parent" />
|
223
|
+
|
224
|
+
<Button
|
225
|
+
android:id="@+id/cancel_button"
|
226
|
+
android:layout_width="wrap_content"
|
227
|
+
android:layout_height="wrap_content"
|
228
|
+
android:text="CANCEL"
|
229
|
+
android:layout_marginEnd="10dp"
|
230
|
+
android:layout_marginStart="50dp"
|
231
|
+
app:layout_constraintBottom_toBottomOf="parent"
|
232
|
+
app:layout_constraintEnd_toStartOf="@id/save_button"
|
233
|
+
app:layout_constraintStart_toStartOf="parent"
|
234
|
+
app:layout_constraintTop_toTopOf="@id/save_button" />
|
235
|
+
|
236
|
+
<Button
|
237
|
+
android:id="@+id/save_button"
|
238
|
+
android:layout_width="wrap_content"
|
239
|
+
android:layout_height="wrap_content"
|
240
|
+
android:layout_marginTop="10dp"
|
241
|
+
android:text="SAVE"
|
242
|
+
app:layout_constraintBottom_toBottomOf="parent"
|
243
|
+
app:layout_constraintEnd_toEndOf="parent"
|
244
|
+
app:layout_constraintTop_toBottomOf="@id/budget_name_edit" />
|
245
|
+
|
246
|
+
</androidx.constraintlayout.widget.ConstraintLayout>
|
247
|
+
```
|
1
図追加
test
CHANGED
@@ -8,6 +8,8 @@
|
|
8
8
|
としており、 this つまり各々が管理している ViewModel を得ていますので、別々のモノを得ています。
|
9
9
|
両方で同じモノを使いたいのであれば両方で同じ ViewModelStoreOwner を使わなければなりませんので、双方が乗っている(であろう)アクティビティを使います。
|
10
10
|
|
11
|
+
![Activity/Fragment から ViewModel を得る](https://ddjkaamml8q8x.cloudfront.net/questions/2024-03-08/344fba56-6073-48b4-aa2f-7c13db30ef5b.png)
|
12
|
+
|
11
13
|
(PassFragment はフィールドの初期化で ViewModel を得ていますがアクティビティはその時点では接続されていないので得られませんから ReceiveFragment のように onViewCreated 内等に移動させたうえで) 双方とも
|
12
14
|
```java
|
13
15
|
new ViewModelProvider(requireActivity()).get(MainViewModel.class);
|