回答編集履歴

4

コード入れ替え

2022/05/21 11:57

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -1,152 +1,9 @@
1
1
  ViewModel や Binding は関係無さそうですのでその辺りを省いて試してみましたが、恐らく「 10000 の行」の EditText を選択・編集しても ListView の選択行が「 1 の行」のままなため、 notifyDataSetChanged で ListView の表示を更新した際に選択行を表示しようとして先頭に戻っているのではないでしょうか。
2
2
  EditText (や +/- のボタンも?) にフォーカスが当たった時点で ListView の選択行をその EditView のある行に設定しては如何でしょう。
3
-
4
3
  ただ、 notify すると EditText の編集モードが解除されてしまいますので、 notify を起こさずに各データを更新するルートを通すのが良さそうです。
5
-
6
- 試したコードはこちらです。 (kotlin に不慣れな為 "kotlin 的に変 " な個所があるかもしれません。)
4
+
7
- ```kotlin
8
- //import android.database.DataSetObserver
9
- import androidx.appcompat.app.AppCompatActivity
10
- import android.os.Bundle
11
- import android.text.Editable
12
- import android.text.TextWatcher
13
- import android.view.LayoutInflater
14
- import android.view.View
15
- import android.view.ViewGroup
16
- import android.widget.*
17
- import java.lang.IllegalArgumentException
18
- import java.util.function.Consumer
19
-
20
- class MainActivity : AppCompatActivity() {
21
- override fun onCreate(savedInstanceState: Bundle?) {
22
- super.onCreate(savedInstanceState)
23
- setContentView(R.layout.activity_main)
24
-
25
- val sumMoney: TextView = findViewById(R.id.lvSumMoney)
26
-
27
- val listView = findViewById<ListView>(R.id.lvItems)
28
- val adapter = InputAdapter { sum -> sumMoney.text = sum.toString() }
29
- listView.adapter = adapter
30
-
31
- //adapter.registerDataSetObserver(object: DataSetObserver() {
32
- // override fun onChanged() {
33
- // sumMoney.text = adapter.sum().toString()
34
- // }
35
- // override fun onInvalidated() {
36
- // sumMoney.text = "0"
37
- // }
38
- //})
39
-
40
- findViewById<RadioGroup>(R.id.addTypes).apply {
41
- setOnCheckedChangeListener { _, id ->
42
- when (id) {
43
- R.id.ten -> adapter.delta = 10
44
- R.id.twenty -> adapter.delta = 20
45
- R.id.fifty -> adapter.delta = 50
46
- else -> adapter.delta = 1
47
- }
48
- }
49
- check(R.id.single)
50
- }
51
- }
52
- }
53
-
54
- class InputAdapter(val listView: ListView? = null, val sumConsumer: Consumer<Int>? = null) : BaseAdapter() {
55
- companion object {
56
- private val moneyTypes = listOf(1, 5, 10, 50, 100, 500, 1000, 2000, 5000, 10000)
57
- }
58
-
59
- private inner class Money(val type: Int, var amount: Int = 0) {
60
- fun value(): Int = type * amount
61
- }
62
- private val moneys = List<Money>(moneyTypes.size) { i -> Money(moneyTypes[i]) }
63
- var delta: Int = 1
64
-
65
- override fun getCount(): Int = moneys.size
66
- override fun getItem(position: Int): Any = moneys[position]
67
- override fun getItemId(position: Int): Long = 0
68
-
69
- override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
70
- val view: View
71
- if(convertView == null) {
5
+ (入りきれなくなったので当初のコードは削除)
72
- view = LayoutInflater.from(parent.context).inflate(R.layout.money_input, parent, false)
6
+
73
- view.tag = ViewHolder(view)
74
- } else {
75
- view = convertView
76
- }
77
- val tag = view.tag as ViewHolder
78
- tag.moneyType.setText(moneys[position].type.toString())
79
- tag.inputMoneyAmount.setText(moneys[position].amount.toString())
80
- return view
81
- }
82
-
83
- fun sum(): Int {
84
- var sum = 0
85
- for(money in moneys) sum += money.value()
86
- return sum
87
- }
88
-
89
- private fun set(type: Int, amount: Int): Int {
90
- moneys[getTypePosition(type)].amount = amount
91
- //notifyDataSetChanged()
92
- sumConsumer?.accept(sum())
93
- return amount
94
- }
95
- private fun get(type: Int): Int {
96
- return moneys[getTypePosition(type)].amount
97
- }
98
-
99
- private fun countUp(type: Int): Int {
100
- return (get(type) + delta).also { set(type, it) }
101
- }
102
- private fun countDown(type: Int): Int {
103
- return (Math.max(get(type) - delta, 0)).also { set(type, it) }
104
- }
105
-
106
- //private fun setRowFocus(type: Int) {
107
- // listView.setSelection(getTypePosition(type))
108
- //}
109
-
110
- private fun getTypePosition(type: Int): Int {
111
- for(i in 0..moneys.size-1) if(moneys[i].type == type) return i
112
- throw IllegalArgumentException("unknown type: " + type)
113
- }
114
-
115
- inner class ViewHolder(view: View) {
116
- val moneyType: TextView
117
- val inputMoneyAmount: EditText
118
- init {
119
- moneyType = view.findViewById(R.id.moneyType)
120
- inputMoneyAmount = view.findViewById<EditText>(R.id.inputMoneyAmount).apply {
121
- addTextChangedListener(object: TextWatcher {
122
- override fun afterTextChanged(p0: Editable?) {
123
- val amount = p0.toString().toIntOrNull() ?: 0
124
- set(moneyType.text.toString().toInt(), amount)
125
- }
126
- override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
127
- override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
128
- })
129
- //setOnFocusChangeListener { v, hasFocus ->
130
- // if(hasFocus) setRowFocus(moneyType.text.toString().toInt())
131
- //}
132
- setSelectAllOnFocus(true)
133
- }
134
- view.findViewById<Button>(R.id.subButton).apply {
135
- setOnClickListener {
136
- inputMoneyAmount.setText(countDown(moneyType.text.toString().toInt()).toString())
137
- }
138
- isFocusable = false
139
- }
140
- view.findViewById<Button>(R.id.addButton).apply {
141
- setOnClickListener {
142
- inputMoneyAmount.setText(countUp(moneyType.text.toString().toInt()).toString())
143
- }
144
- isFocusable = false
145
- }
146
- }
147
- }
148
- }
149
- ```
150
7
  ---
151
8
  ListView に依らず、コードでレイアウトを作ってしまうほうが簡単かもしれません。
152
9
  ```kotlin
@@ -259,3 +116,161 @@
259
116
  var delta: Int = 1
260
117
  }
261
118
  ```
119
+ res/layout/activity_main.xml
120
+ ```xml
121
+ <?xml version="1.0" encoding="utf-8"?>
122
+ <androidx.constraintlayout.widget.ConstraintLayout
123
+ xmlns:android="http://schemas.android.com/apk/res/android"
124
+ xmlns:app="http://schemas.android.com/apk/res-auto"
125
+ xmlns:tools="http://schemas.android.com/tools"
126
+ android:layout_width="match_parent"
127
+ android:layout_height="match_parent">
128
+
129
+ <ScrollView
130
+ android:id="@+id/scrollView"
131
+ android:layout_width="match_parent"
132
+ android:layout_height="0dp"
133
+ android:orientation="vertical"
134
+ app:layout_constraintBottom_toTopOf="@id/lvSumMoney"
135
+ app:layout_constraintEnd_toEndOf="parent"
136
+ app:layout_constraintStart_toStartOf="parent"
137
+ app:layout_constraintTop_toTopOf="parent">
138
+
139
+ <LinearLayout
140
+ android:id="@+id/contents"
141
+ android:layout_width="match_parent"
142
+ android:layout_height="wrap_content"
143
+ android:orientation="vertical" />
144
+ </ScrollView>
145
+
146
+ <TextView
147
+ android:id="@+id/lvSumTitle"
148
+ android:layout_width="wrap_content"
149
+ android:layout_height="wrap_content"
150
+ android:layout_marginEnd="8dp"
151
+ android:text="合計金額"
152
+ app:layout_constraintBaseline_toBaselineOf="@id/lvYen"
153
+ app:layout_constraintEnd_toStartOf="@id/lvSumMoney" />
154
+ <TextView
155
+ android:id="@+id/lvSumMoney"
156
+ android:layout_width="wrap_content"
157
+ android:layout_height="wrap_content"
158
+ android:layout_marginTop="8dp"
159
+ android:layout_marginEnd="8dp"
160
+ android:text="12345"
161
+ android:textSize="24sp"
162
+ app:layout_constraintEnd_toStartOf="@id/lvYen"
163
+ app:layout_constraintBottom_toBottomOf="@id/lvYen" />
164
+ <TextView
165
+ android:id="@+id/lvYen"
166
+ android:layout_width="wrap_content"
167
+ android:layout_height="wrap_content"
168
+ android:layout_marginEnd="8dp"
169
+ android:text="円"
170
+ app:layout_constraintBottom_toTopOf="@id/addTypes"
171
+ app:layout_constraintEnd_toEndOf="parent" />
172
+
173
+ <RadioGroup
174
+ android:id="@+id/addTypes"
175
+ android:layout_width="wrap_content"
176
+ android:layout_height="wrap_content"
177
+ android:layout_marginTop="8dp"
178
+ android:orientation="horizontal"
179
+ app:layout_constraintBottom_toBottomOf="parent"
180
+ app:layout_constraintEnd_toEndOf="parent"
181
+ app:layout_constraintStart_toStartOf="parent">
182
+ <RadioButton
183
+ android:id="@+id/single"
184
+ android:layout_width="wrap_content"
185
+ android:layout_height="wrap_content"
186
+ android:layout_marginTop="8dp"
187
+ android:layout_marginBottom="8dp"
188
+ android:text="1枚" />
189
+ <RadioButton
190
+ android:id="@+id/ten"
191
+ android:layout_width="wrap_content"
192
+ android:layout_height="wrap_content"
193
+ android:layout_marginStart="8dp"
194
+ android:layout_marginTop="8dp"
195
+ android:layout_marginBottom="8dp"
196
+ android:text="10枚" />
197
+ <RadioButton
198
+ android:id="@+id/twenty"
199
+ android:layout_width="wrap_content"
200
+ android:layout_height="wrap_content"
201
+ android:layout_marginStart="8dp"
202
+ android:layout_marginTop="8dp"
203
+ android:layout_marginBottom="8dp"
204
+ android:text="20枚" />
205
+ <RadioButton
206
+ android:id="@+id/fifty"
207
+ android:layout_width="wrap_content"
208
+ android:layout_height="wrap_content"
209
+ android:layout_marginStart="8dp"
210
+ android:layout_marginTop="8dp"
211
+ android:layout_marginBottom="8dp"
212
+ android:text="50枚" />
213
+ </RadioGroup>
214
+ </androidx.constraintlayout.widget.ConstraintLayout>
215
+ ```
216
+ res/layout/money_input.xml
217
+ ```xml
218
+ <?xml version="1.0" encoding="utf-8"?>
219
+ <androidx.constraintlayout.widget.ConstraintLayout
220
+ xmlns:android="http://schemas.android.com/apk/res/android"
221
+ xmlns:app="http://schemas.android.com/apk/res-auto"
222
+ xmlns:tools="http://schemas.android.com/tools"
223
+ android:layout_width="match_parent"
224
+ android:layout_height="wrap_content">
225
+
226
+ <TextView
227
+ android:id="@+id/moneyType"
228
+ android:layout_width="100dp"
229
+ android:layout_height="wrap_content"
230
+ android:layout_marginStart="8dp"
231
+ android:text="10"
232
+ app:layout_constraintBaseline_toBaselineOf="@id/addButton"
233
+ app:layout_constraintStart_toStartOf="parent" />
234
+
235
+ <Button
236
+ android:id="@+id/subButton"
237
+ android:layout_width="50dp"
238
+ android:layout_height="50sp"
239
+ android:layout_margin="8dp"
240
+ android:insetTop="0dp"
241
+ android:insetBottom="0dp"
242
+ android:text="-"
243
+ android:textSize="14sp"
244
+ app:layout_constraintEnd_toStartOf="@+id/inputMoneyAmount"
245
+ app:layout_constraintStart_toEndOf="@id/moneyType"
246
+ app:layout_constraintTop_toTopOf="parent" />
247
+
248
+ <EditText
249
+ android:id="@+id/inputMoneyAmount"
250
+ android:layout_width="0dp"
251
+ android:layout_height="wrap_content"
252
+ android:layout_marginStart="8dp"
253
+ android:ems="10"
254
+ android:hint="硬貨・紙幣の枚数"
255
+ android:importantForAutofill="no"
256
+ android:inputType="number"
257
+ android:minHeight="48dp"
258
+ android:text="12"
259
+ app:layout_constraintBaseline_toBaselineOf="@id/addButton"
260
+ app:layout_constraintEnd_toStartOf="@id/addButton"
261
+ app:layout_constraintStart_toEndOf="@id/subButton" />
262
+
263
+ <Button
264
+ android:id="@+id/addButton"
265
+ android:layout_width="50sp"
266
+ android:layout_height="50sp"
267
+ android:layout_margin="8dp"
268
+ android:insetTop="0dp"
269
+ android:insetBottom="0dp"
270
+ android:text="+"
271
+ android:textSize="14sp"
272
+ app:layout_constraintEnd_toEndOf="parent"
273
+ app:layout_constraintStart_toEndOf="@id/inputMoneyAmount"
274
+ app:layout_constraintTop_toTopOf="parent" />
275
+ </androidx.constraintlayout.widget.ConstraintLayout>
276
+ ```

3

コード追加

2022/05/21 11:46

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -147,3 +147,115 @@
147
147
  }
148
148
  }
149
149
  ```
150
+ ---
151
+ ListView に依らず、コードでレイアウトを作ってしまうほうが簡単かもしれません。
152
+ ```kotlin
153
+ import androidx.appcompat.app.AppCompatActivity
154
+ import android.os.Bundle
155
+ import android.text.Editable
156
+ import android.text.TextWatcher
157
+ import android.view.LayoutInflater
158
+ import android.view.View
159
+ import android.view.ViewGroup
160
+ import android.widget.*
161
+ import androidx.activity.viewModels
162
+ import androidx.lifecycle.MutableLiveData
163
+ import androidx.lifecycle.ViewModel
164
+
165
+ class MainActivity : AppCompatActivity() {
166
+ override fun onCreate(savedInstanceState: Bundle?) {
167
+ super.onCreate(savedInstanceState)
168
+ setContentView(R.layout.activity_main)
169
+
170
+ val model: AmountsViewModel by viewModels()
171
+
172
+ val contentsView = findViewById<LinearLayout>(R.id.contents)
173
+ for(moneyType in MoneyType.values()) {
174
+ contentsView.addView(createView(contentsView, moneyType, model),
175
+ LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
176
+ ViewGroup.LayoutParams.WRAP_CONTENT))
177
+ }
178
+
179
+ val sumMoney: TextView = findViewById(R.id.lvSumMoney)
180
+ model.sum.observe(this) { total -> sumMoney.setText(total.toString()) }
181
+
182
+ findViewById<RadioGroup>(R.id.addTypes).apply {
183
+ setOnCheckedChangeListener { _, id ->
184
+ when (id) {
185
+ R.id.ten -> model.delta = 10
186
+ R.id.twenty -> model.delta = 20
187
+ R.id.fifty -> model.delta = 50
188
+ else -> model.delta = 1
189
+ }
190
+ }
191
+ check(R.id.single)
192
+ }
193
+ }
194
+
195
+ private fun createView(parent: ViewGroup, moneyType: MoneyType, model: AmountsViewModel): View {
196
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.money_input, parent, false)
197
+
198
+ view.findViewById<TextView>(R.id.moneyType).apply {
199
+ setText(moneyType.value.toString())
200
+ }
201
+
202
+ val inputMoneyAmount = view.findViewById<EditText>(R.id.inputMoneyAmount).apply {
203
+ addTextChangedListener(object : TextWatcher {
204
+ override fun afterTextChanged(p0: Editable?) {
205
+ model.set(moneyType, p0.toString().toLongOrNull() ?: 0L)
206
+ }
207
+ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
208
+ override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
209
+ })
210
+ setSelectAllOnFocus(true)
211
+ setText(model.amountMap.getOrDefault(moneyType, 0L).toString())
212
+ }
213
+
214
+ view.findViewById<Button>(R.id.subButton).apply {
215
+ setOnClickListener {
216
+ val amount = Math.max(model.get(moneyType) - model.delta, 0)
217
+ inputMoneyAmount.setText(amount.toString())
218
+ }
219
+ }
220
+
221
+ view.findViewById<Button>(R.id.addButton).apply {
222
+ setOnClickListener {
223
+ val amount = model.get(moneyType) + model.delta
224
+ inputMoneyAmount.setText(amount.toString())
225
+ }
226
+ }
227
+
228
+ return view
229
+ }
230
+ }
231
+
232
+ enum class MoneyType(val value: Int) {
233
+ T1(1), T5(5), T10(10), T50(50), T100(100), T500(500), T1000(1000), T5000(5000), T10000(10000)
234
+ }
235
+
236
+ class AmountsViewModel: ViewModel() {
237
+ val amountMap = mutableMapOf<MoneyType,Long>().apply {
238
+ for(moneyType in MoneyType.values()) this.set(moneyType, 0L)
239
+ }
240
+
241
+ fun set(moneyType: MoneyType, amount: Long) {
242
+ amountMap.put(moneyType, amount)
243
+ recalc()
244
+ }
245
+ fun get(moneyType: MoneyType): Long {
246
+ return amountMap.get(moneyType) ?: 0L
247
+ }
248
+
249
+ private fun recalc() {
250
+ var total = 0L
251
+ amountMap.forEach { mapEntry -> //種類*数を足していく。
252
+ total += mapEntry.key.value * mapEntry.value
253
+ }
254
+ sum.value = total
255
+ }
256
+
257
+ val sum = MutableLiveData<Long>()
258
+
259
+ var delta: Int = 1
260
+ }
261
+ ```

2

コード修正・追記

2022/05/21 07:06

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -1,5 +1,7 @@
1
1
  ViewModel や Binding は関係無さそうですのでその辺りを省いて試してみましたが、恐らく「 10000 の行」の EditText を選択・編集しても ListView の選択行が「 1 の行」のままなため、 notifyDataSetChanged で ListView の表示を更新した際に選択行を表示しようとして先頭に戻っているのではないでしょうか。
2
2
  EditText (や +/- のボタンも?) にフォーカスが当たった時点で ListView の選択行をその EditView のある行に設定しては如何でしょう。
3
+
4
+ ただ、 notify すると EditText の編集モードが解除されてしまいますので、 notify を起こさずに各データを更新するルートを通すのが良さそうです。
3
5
 
4
6
  試したコードはこちらです。 (kotlin に不慣れな為 "kotlin 的に変 " な個所があるかもしれません。)
5
7
  ```kotlin
@@ -23,7 +25,7 @@
23
25
  val sumMoney: TextView = findViewById(R.id.lvSumMoney)
24
26
 
25
27
  val listView = findViewById<ListView>(R.id.lvItems)
26
- val adapter = InputAdapter(listView) { sum -> sumMoney.text = sum.toString() }
28
+ val adapter = InputAdapter { sum -> sumMoney.text = sum.toString() }
27
29
  listView.adapter = adapter
28
30
 
29
31
  //adapter.registerDataSetObserver(object: DataSetObserver() {
@@ -49,7 +51,7 @@
49
51
  }
50
52
  }
51
53
 
52
- class InputAdapter(val listView: ListView, val sumConsumer: Consumer<Int>? = null) : BaseAdapter() {
54
+ class InputAdapter(val listView: ListView? = null, val sumConsumer: Consumer<Int>? = null) : BaseAdapter() {
53
55
  companion object {
54
56
  private val moneyTypes = listOf(1, 5, 10, 50, 100, 500, 1000, 2000, 5000, 10000)
55
57
  }
@@ -101,9 +103,9 @@
101
103
  return (Math.max(get(type) - delta, 0)).also { set(type, it) }
102
104
  }
103
105
 
104
- private fun setRowFocus(type: Int) {
106
+ //private fun setRowFocus(type: Int) {
105
- listView.setSelection(getTypePosition(type))
107
+ // listView.setSelection(getTypePosition(type))
106
- }
108
+ //}
107
109
 
108
110
  private fun getTypePosition(type: Int): Int {
109
111
  for(i in 0..moneys.size-1) if(moneys[i].type == type) return i
@@ -124,9 +126,9 @@
124
126
  override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
125
127
  override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
126
128
  })
127
- setOnFocusChangeListener { v, hasFocus ->
129
+ //setOnFocusChangeListener { v, hasFocus ->
128
- if(hasFocus) setRowFocus(moneyType.text.toString().toInt())
130
+ // if(hasFocus) setRowFocus(moneyType.text.toString().toInt())
129
- }
131
+ //}
130
132
  setSelectAllOnFocus(true)
131
133
  }
132
134
  view.findViewById<Button>(R.id.subButton).apply {

1

コード追加

2022/05/21 06:51

投稿

jimbe
jimbe

スコア12659

test CHANGED
@@ -1,3 +1,147 @@
1
1
  ViewModel や Binding は関係無さそうですのでその辺りを省いて試してみましたが、恐らく「 10000 の行」の EditText を選択・編集しても ListView の選択行が「 1 の行」のままなため、 notifyDataSetChanged で ListView の表示を更新した際に選択行を表示しようとして先頭に戻っているのではないでしょうか。
2
2
  EditText (や +/- のボタンも?) にフォーカスが当たった時点で ListView の選択行をその EditView のある行に設定しては如何でしょう。
3
3
 
4
+ 試したコードはこちらです。 (kotlin に不慣れな為 "kotlin 的に変 " な個所があるかもしれません。)
5
+ ```kotlin
6
+ //import android.database.DataSetObserver
7
+ import androidx.appcompat.app.AppCompatActivity
8
+ import android.os.Bundle
9
+ import android.text.Editable
10
+ import android.text.TextWatcher
11
+ import android.view.LayoutInflater
12
+ import android.view.View
13
+ import android.view.ViewGroup
14
+ import android.widget.*
15
+ import java.lang.IllegalArgumentException
16
+ import java.util.function.Consumer
17
+
18
+ class MainActivity : AppCompatActivity() {
19
+ override fun onCreate(savedInstanceState: Bundle?) {
20
+ super.onCreate(savedInstanceState)
21
+ setContentView(R.layout.activity_main)
22
+
23
+ val sumMoney: TextView = findViewById(R.id.lvSumMoney)
24
+
25
+ val listView = findViewById<ListView>(R.id.lvItems)
26
+ val adapter = InputAdapter(listView) { sum -> sumMoney.text = sum.toString() }
27
+ listView.adapter = adapter
28
+
29
+ //adapter.registerDataSetObserver(object: DataSetObserver() {
30
+ // override fun onChanged() {
31
+ // sumMoney.text = adapter.sum().toString()
32
+ // }
33
+ // override fun onInvalidated() {
34
+ // sumMoney.text = "0"
35
+ // }
36
+ //})
37
+
38
+ findViewById<RadioGroup>(R.id.addTypes).apply {
39
+ setOnCheckedChangeListener { _, id ->
40
+ when (id) {
41
+ R.id.ten -> adapter.delta = 10
42
+ R.id.twenty -> adapter.delta = 20
43
+ R.id.fifty -> adapter.delta = 50
44
+ else -> adapter.delta = 1
45
+ }
46
+ }
47
+ check(R.id.single)
48
+ }
49
+ }
50
+ }
51
+
52
+ class InputAdapter(val listView: ListView, val sumConsumer: Consumer<Int>? = null) : BaseAdapter() {
53
+ companion object {
54
+ private val moneyTypes = listOf(1, 5, 10, 50, 100, 500, 1000, 2000, 5000, 10000)
55
+ }
56
+
57
+ private inner class Money(val type: Int, var amount: Int = 0) {
58
+ fun value(): Int = type * amount
59
+ }
60
+ private val moneys = List<Money>(moneyTypes.size) { i -> Money(moneyTypes[i]) }
61
+ var delta: Int = 1
62
+
63
+ override fun getCount(): Int = moneys.size
64
+ override fun getItem(position: Int): Any = moneys[position]
65
+ override fun getItemId(position: Int): Long = 0
66
+
67
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
68
+ val view: View
69
+ if(convertView == null) {
70
+ view = LayoutInflater.from(parent.context).inflate(R.layout.money_input, parent, false)
71
+ view.tag = ViewHolder(view)
72
+ } else {
73
+ view = convertView
74
+ }
75
+ val tag = view.tag as ViewHolder
76
+ tag.moneyType.setText(moneys[position].type.toString())
77
+ tag.inputMoneyAmount.setText(moneys[position].amount.toString())
78
+ return view
79
+ }
80
+
81
+ fun sum(): Int {
82
+ var sum = 0
83
+ for(money in moneys) sum += money.value()
84
+ return sum
85
+ }
86
+
87
+ private fun set(type: Int, amount: Int): Int {
88
+ moneys[getTypePosition(type)].amount = amount
89
+ //notifyDataSetChanged()
90
+ sumConsumer?.accept(sum())
91
+ return amount
92
+ }
93
+ private fun get(type: Int): Int {
94
+ return moneys[getTypePosition(type)].amount
95
+ }
96
+
97
+ private fun countUp(type: Int): Int {
98
+ return (get(type) + delta).also { set(type, it) }
99
+ }
100
+ private fun countDown(type: Int): Int {
101
+ return (Math.max(get(type) - delta, 0)).also { set(type, it) }
102
+ }
103
+
104
+ private fun setRowFocus(type: Int) {
105
+ listView.setSelection(getTypePosition(type))
106
+ }
107
+
108
+ private fun getTypePosition(type: Int): Int {
109
+ for(i in 0..moneys.size-1) if(moneys[i].type == type) return i
110
+ throw IllegalArgumentException("unknown type: " + type)
111
+ }
112
+
113
+ inner class ViewHolder(view: View) {
114
+ val moneyType: TextView
115
+ val inputMoneyAmount: EditText
116
+ init {
117
+ moneyType = view.findViewById(R.id.moneyType)
118
+ inputMoneyAmount = view.findViewById<EditText>(R.id.inputMoneyAmount).apply {
119
+ addTextChangedListener(object: TextWatcher {
120
+ override fun afterTextChanged(p0: Editable?) {
121
+ val amount = p0.toString().toIntOrNull() ?: 0
122
+ set(moneyType.text.toString().toInt(), amount)
123
+ }
124
+ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
125
+ override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
126
+ })
127
+ setOnFocusChangeListener { v, hasFocus ->
128
+ if(hasFocus) setRowFocus(moneyType.text.toString().toInt())
129
+ }
130
+ setSelectAllOnFocus(true)
131
+ }
132
+ view.findViewById<Button>(R.id.subButton).apply {
133
+ setOnClickListener {
134
+ inputMoneyAmount.setText(countDown(moneyType.text.toString().toInt()).toString())
135
+ }
136
+ isFocusable = false
137
+ }
138
+ view.findViewById<Button>(R.id.addButton).apply {
139
+ setOnClickListener {
140
+ inputMoneyAmount.setText(countUp(moneyType.text.toString().toInt()).toString())
141
+ }
142
+ isFocusable = false
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ```