質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

1回答

1169閲覧

ArrayAdapterしたListViewで、値を書き換えるとスクロールがリセットされてしまう

yuu1111main

総合スコア6

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2022/05/19 04:44

コードはこちらです
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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
jimbe

総合スコア12648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2022/05/21 07:09

データバインディングの為に ViewHolder が面倒なことになっていますので、いっそ ListView は使わず、1円 ~ 1万円のフィールドを縦に並べたレイアウトをスクロール枠に入れたような構造のほうが良いかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問