コードはこちらです
https://github.com/yuchan2215/AndroidMoneycalculator/tree/5737c1d5a64d02f2efb6805819b8dd56657314b7
アプリ説明
ArrayAdapterを利用して動的に生成したListView内の値をViewModeと双方向バインディングし、合計金額も表示するものです。
問題点
ここで、1円や10円など、上部にあるものは問題ないのですが、スクロールして表示するところ(10000円)などに文字を打ち込むと、ListViewが一番上に戻ってしまいました。
この行(GitHub)
Kotlin
1rootViewModel.amountMaps.postValue(map?.toMutableMap()) //オブジェクトを更新する
を消すとこの現象が起きなくなるので、合計金額の表示を変更すると一番上に戻されてしまうと思うのですが、解決方法が分かりません。smoothScrollPosition
を上記ソースの次の行に追加したりしましたが、解決しませんでした。3時間ほど格闘しましたが解決しないので質問させていただきます。
一応、Android開発を触り始めて20時間ほどしか経っていない初心者なので、大きく実装方法を変えても大丈夫です。(双方向バインディングができれば)良い方法があればご教授いただければ幸いです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答1件
0
ViewModel や Binding は関係無さそうですのでその辺りを省いて試してみましたが、恐らく「 10000 の行」の EditText を選択・編集しても ListView の選択行が「 1 の行」のままなため、 notifyDataSetChanged で ListView の表示を更新した際に選択行を表示しようとして先頭に戻っているのではないでしょうか。
EditText (や +/- のボタンも?) にフォーカスが当たった時点で ListView の選択行をその EditView のある行に設定しては如何でしょう。
ただ、 notify すると EditText の編集モードが解除されてしまいますので、 notify を起こさずに各データを更新するルートを通すのが良さそうです。
(入りきれなくなったので当初のコードは削除)
ListView に依らず、コードでレイアウトを作ってしまうほうが簡単かもしれません。
kotlin
1import androidx.appcompat.app.AppCompatActivity 2import android.os.Bundle 3import android.text.Editable 4import android.text.TextWatcher 5import android.view.LayoutInflater 6import android.view.View 7import android.view.ViewGroup 8import android.widget.* 9import androidx.activity.viewModels 10import androidx.lifecycle.MutableLiveData 11import androidx.lifecycle.ViewModel 12 13class MainActivity : AppCompatActivity() { 14 override fun onCreate(savedInstanceState: Bundle?) { 15 super.onCreate(savedInstanceState) 16 setContentView(R.layout.activity_main) 17 18 val model: AmountsViewModel by viewModels() 19 20 val contentsView = findViewById<LinearLayout>(R.id.contents) 21 for(moneyType in MoneyType.values()) { 22 contentsView.addView(createView(contentsView, moneyType, model), 23 LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 24 ViewGroup.LayoutParams.WRAP_CONTENT)) 25 } 26 27 val sumMoney: TextView = findViewById(R.id.lvSumMoney) 28 model.sum.observe(this) { total -> sumMoney.setText(total.toString()) } 29 30 findViewById<RadioGroup>(R.id.addTypes).apply { 31 setOnCheckedChangeListener { _, id -> 32 when (id) { 33 R.id.ten -> model.delta = 10 34 R.id.twenty -> model.delta = 20 35 R.id.fifty -> model.delta = 50 36 else -> model.delta = 1 37 } 38 } 39 check(R.id.single) 40 } 41 } 42 43 private fun createView(parent: ViewGroup, moneyType: MoneyType, model: AmountsViewModel): View { 44 val view = LayoutInflater.from(parent.context).inflate(R.layout.money_input, parent, false) 45 46 view.findViewById<TextView>(R.id.moneyType).apply { 47 setText(moneyType.value.toString()) 48 } 49 50 val inputMoneyAmount = view.findViewById<EditText>(R.id.inputMoneyAmount).apply { 51 addTextChangedListener(object : TextWatcher { 52 override fun afterTextChanged(p0: Editable?) { 53 model.set(moneyType, p0.toString().toLongOrNull() ?: 0L) 54 } 55 override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} 56 override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} 57 }) 58 setSelectAllOnFocus(true) 59 setText(model.amountMap.getOrDefault(moneyType, 0L).toString()) 60 } 61 62 view.findViewById<Button>(R.id.subButton).apply { 63 setOnClickListener { 64 val amount = Math.max(model.get(moneyType) - model.delta, 0) 65 inputMoneyAmount.setText(amount.toString()) 66 } 67 } 68 69 view.findViewById<Button>(R.id.addButton).apply { 70 setOnClickListener { 71 val amount = model.get(moneyType) + model.delta 72 inputMoneyAmount.setText(amount.toString()) 73 } 74 } 75 76 return view 77 } 78} 79 80enum class MoneyType(val value: Int) { 81 T1(1), T5(5), T10(10), T50(50), T100(100), T500(500), T1000(1000), T5000(5000), T10000(10000) 82} 83 84class AmountsViewModel: ViewModel() { 85 val amountMap = mutableMapOf<MoneyType,Long>().apply { 86 for(moneyType in MoneyType.values()) this.set(moneyType, 0L) 87 } 88 89 fun set(moneyType: MoneyType, amount: Long) { 90 amountMap.put(moneyType, amount) 91 recalc() 92 } 93 fun get(moneyType: MoneyType): Long { 94 return amountMap.get(moneyType) ?: 0L 95 } 96 97 private fun recalc() { 98 var total = 0L 99 amountMap.forEach { mapEntry -> //種類*数を足していく。 100 total += mapEntry.key.value * mapEntry.value 101 } 102 sum.value = total 103 } 104 105 val sum = MutableLiveData<Long>() 106 107 var delta: Int = 1 108}
res/layout/activity_main.xml
xml
1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 xmlns:tools="http://schemas.android.com/tools" 6 android:layout_width="match_parent" 7 android:layout_height="match_parent"> 8 9 <ScrollView 10 android:id="@+id/scrollView" 11 android:layout_width="match_parent" 12 android:layout_height="0dp" 13 android:orientation="vertical" 14 app:layout_constraintBottom_toTopOf="@id/lvSumMoney" 15 app:layout_constraintEnd_toEndOf="parent" 16 app:layout_constraintStart_toStartOf="parent" 17 app:layout_constraintTop_toTopOf="parent"> 18 19 <LinearLayout 20 android:id="@+id/contents" 21 android:layout_width="match_parent" 22 android:layout_height="wrap_content" 23 android:orientation="vertical" /> 24 </ScrollView> 25 26 <TextView 27 android:id="@+id/lvSumTitle" 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_marginEnd="8dp" 31 android:text="合計金額" 32 app:layout_constraintBaseline_toBaselineOf="@id/lvYen" 33 app:layout_constraintEnd_toStartOf="@id/lvSumMoney" /> 34 <TextView 35 android:id="@+id/lvSumMoney" 36 android:layout_width="wrap_content" 37 android:layout_height="wrap_content" 38 android:layout_marginTop="8dp" 39 android:layout_marginEnd="8dp" 40 android:text="12345" 41 android:textSize="24sp" 42 app:layout_constraintEnd_toStartOf="@id/lvYen" 43 app:layout_constraintBottom_toBottomOf="@id/lvYen" /> 44 <TextView 45 android:id="@+id/lvYen" 46 android:layout_width="wrap_content" 47 android:layout_height="wrap_content" 48 android:layout_marginEnd="8dp" 49 android:text="円" 50 app:layout_constraintBottom_toTopOf="@id/addTypes" 51 app:layout_constraintEnd_toEndOf="parent" /> 52 53 <RadioGroup 54 android:id="@+id/addTypes" 55 android:layout_width="wrap_content" 56 android:layout_height="wrap_content" 57 android:layout_marginTop="8dp" 58 android:orientation="horizontal" 59 app:layout_constraintBottom_toBottomOf="parent" 60 app:layout_constraintEnd_toEndOf="parent" 61 app:layout_constraintStart_toStartOf="parent"> 62 <RadioButton 63 android:id="@+id/single" 64 android:layout_width="wrap_content" 65 android:layout_height="wrap_content" 66 android:layout_marginTop="8dp" 67 android:layout_marginBottom="8dp" 68 android:text="1枚" /> 69 <RadioButton 70 android:id="@+id/ten" 71 android:layout_width="wrap_content" 72 android:layout_height="wrap_content" 73 android:layout_marginStart="8dp" 74 android:layout_marginTop="8dp" 75 android:layout_marginBottom="8dp" 76 android:text="10枚" /> 77 <RadioButton 78 android:id="@+id/twenty" 79 android:layout_width="wrap_content" 80 android:layout_height="wrap_content" 81 android:layout_marginStart="8dp" 82 android:layout_marginTop="8dp" 83 android:layout_marginBottom="8dp" 84 android:text="20枚" /> 85 <RadioButton 86 android:id="@+id/fifty" 87 android:layout_width="wrap_content" 88 android:layout_height="wrap_content" 89 android:layout_marginStart="8dp" 90 android:layout_marginTop="8dp" 91 android:layout_marginBottom="8dp" 92 android:text="50枚" /> 93 </RadioGroup> 94</androidx.constraintlayout.widget.ConstraintLayout>
res/layout/money_input.xml
xml
1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 xmlns:tools="http://schemas.android.com/tools" 6 android:layout_width="match_parent" 7 android:layout_height="wrap_content"> 8 9 <TextView 10 android:id="@+id/moneyType" 11 android:layout_width="100dp" 12 android:layout_height="wrap_content" 13 android:layout_marginStart="8dp" 14 android:text="10" 15 app:layout_constraintBaseline_toBaselineOf="@id/addButton" 16 app:layout_constraintStart_toStartOf="parent" /> 17 18 <Button 19 android:id="@+id/subButton" 20 android:layout_width="50dp" 21 android:layout_height="50sp" 22 android:layout_margin="8dp" 23 android:insetTop="0dp" 24 android:insetBottom="0dp" 25 android:text="-" 26 android:textSize="14sp" 27 app:layout_constraintEnd_toStartOf="@+id/inputMoneyAmount" 28 app:layout_constraintStart_toEndOf="@id/moneyType" 29 app:layout_constraintTop_toTopOf="parent" /> 30 31 <EditText 32 android:id="@+id/inputMoneyAmount" 33 android:layout_width="0dp" 34 android:layout_height="wrap_content" 35 android:layout_marginStart="8dp" 36 android:ems="10" 37 android:hint="硬貨・紙幣の枚数" 38 android:importantForAutofill="no" 39 android:inputType="number" 40 android:minHeight="48dp" 41 android:text="12" 42 app:layout_constraintBaseline_toBaselineOf="@id/addButton" 43 app:layout_constraintEnd_toStartOf="@id/addButton" 44 app:layout_constraintStart_toEndOf="@id/subButton" /> 45 46 <Button 47 android:id="@+id/addButton" 48 android:layout_width="50sp" 49 android:layout_height="50sp" 50 android:layout_margin="8dp" 51 android:insetTop="0dp" 52 android:insetBottom="0dp" 53 android:text="+" 54 android:textSize="14sp" 55 app:layout_constraintEnd_toEndOf="parent" 56 app:layout_constraintStart_toEndOf="@id/inputMoneyAmount" 57 app:layout_constraintTop_toTopOf="parent" /> 58</androidx.constraintlayout.widget.ConstraintLayout>
投稿2022/05/21 06:18
編集2022/05/21 11:57総合スコア12648
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/05/21 07:09