Android高尔夫计分App EditText实时同步TextView计分异常
问题根因
现有代码存在3个核心错误,直接导致功能异常:
- 监听器内部定义了
int j = 0,每次输入触发回调时j都会被重置为0,永远只会读取索引为0的第一个洞的输入框内容,后续洞的输入完全不会被读取 - 错误判断
actionId == j:actionId是输入法动作常量(比如完成、下一步动作的标识),根本不是球洞序号,这个判断逻辑完全不成立 - 采用单次输入差值累加的逻辑存在缺陷:如果用户修改已经填过的球洞分数,会重复累加差值导致总分错误;且
OnEditorActionListener仅在用户点击输入法的动作按钮时触发,无法做到输入过程中实时更新分数。
原生实现修复方案(无需引入数据绑定)
放弃累加单次输入差值的逻辑,改为任意输入框内容变化时,全量遍历所有球洞重新计算总分,从根源避免索引错误、重复累加问题,同时用TextWatcher替代编辑器动作监听器,实现输入即更新的实时效果:
// 提前初始化球洞标准杆列表、输入框列表的逻辑和原有代码一致 List<EditText> userScores = new ArrayList<>(); userScores.add(etUserHole1); userScores.add(etUserHole2); userScores.add(etUserHole3); userScores.add(etUserHole4); userScores.add(etUserHole5); userScores.add(etUserHole6); userScores.add(etUserHole7); userScores.add(etUserHole8); userScores.add(etUserHole9); List<Integer> coursePars = new ArrayList<>(); coursePars.add(Integer.parseInt(tvPar1.getText().toString())); coursePars.add(Integer.parseInt(tvPar2.getText().toString())); coursePars.add(Integer.parseInt(tvPar3.getText().toString())); coursePars.add(Integer.parseInt(tvPar4.getText().toString())); coursePars.add(Integer.parseInt(tvPar5.getText().toString())); coursePars.add(Integer.parseInt(tvPar6.getText().toString())); coursePars.add(Integer.parseInt(tvPar7.getText().toString())); coursePars.add(Integer.parseInt(tvPar8.getText().toString())); coursePars.add(Integer.parseInt(tvPar9.getText().toString())); // 统一的分数计算方法,每次触发都全量重算 private void calculateTotalScore() { int totalDiff = 0; for (int i = 0; i < userScores.size(); i++) { String inputStr = userScores.get(i).getText().toString().trim(); // 跳过还没填内容的输入框 if (inputStr.isEmpty()) continue; try { int userHoleScore = Integer.parseInt(inputStr); totalDiff += userHoleScore - coursePars.get(i); } catch (NumberFormatException e) { // 输入非法内容时跳过,避免崩溃 e.printStackTrace(); } } tvCurrentScoreUser.setText(String.valueOf(totalDiff)); } // 给所有输入框加文本变化监听器 TextWatcher scoreTextWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // 输入内容变化时立刻重算总分 calculateTotalScore(); } @Override public void afterTextChanged(Editable s) {} }; for (EditText et : userScores) { et.addTextChangedListener(scoreTextWatcher); }
这个方案不需要处理索引对应关系,不管用户修改哪个洞的分数、输入顺序是什么,都能正确计算实时总分,不会出现重复累加的问题。
规范双向数据绑定实现方案
如果要使用双向数据绑定实现,按照以下步骤配置即可:
- 首先在模块级
build.gradle中开启数据绑定开关:
android { // 原有其他配置,比如compileSdkVersion等 buildFeatures { dataBinding true } }
- 定义计分卡的ViewModel,持有每个球洞的分数和自动计算的总杆差:
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import androidx.lifecycle.Observer; import java.util.List; public class ScoreCardViewModel extends ViewModel { // 9个洞的用户输入分数 public MutableLiveData<Integer>[] holeScores = new MutableLiveData[9]; // 球洞标准杆数据 private List<Integer> coursePars; // 可观察的总杆差,自动同步到UI public MutableLiveData<String> totalScoreDiff = new MutableLiveData<>("0"); public ScoreCardViewModel() { for (int i = 0; i < 9; i++) { holeScores[i] = new MutableLiveData<>(); // 每个洞的分数变化时,自动重算总分 holeScores[i].observeForever(score -> recalculateTotal()); } } public void setCoursePars(List<Integer> pars) { this.coursePars = pars; recalculateTotal(); } private void recalculateTotal() { if (coursePars == null) return; int sum = 0; for (int i = 0; i < 9; i++) { Integer score = holeScores[i].getValue(); if (score != null) { sum += score - coursePars.get(i); } } totalScoreDiff.setValue(String.valueOf(sum)); } }
- 修改布局文件,用
layout标签包裹根布局,添加数据绑定的变量配置,给输入框和总分文本框设置绑定关系:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="viewModel" type="替换成你自己的包名.ScoreCardViewModel" /> </data> <!-- 原有布局内容,以下为第一个洞输入框的绑定示例,剩余8个洞依次修改对应索引即可 --> <EditText android:id="@+id/etUserHole1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="number" android:text="@={viewModel.holeScores[0]}" /> <!-- etUserHole2到etUserHole9分别绑定holeScores[1]到holeScores[8] --> <!-- 总分展示TextView --> <TextView android:id="@+id/tvCurrentScoreUser" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.totalScoreDiff}" /> </layout>
- 在Activity中初始化绑定对象、ViewModel,传入标准杆数据即可,不需要手动编写任何控件监听器:
// 初始化DataBinding ActivityScorecardBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_scorecard); ScoreCardViewModel viewModel = new ViewModelProvider(this).get(ScoreCardViewModel.class); binding.setViewModel(viewModel); binding.setLifecycleOwner(this); // 读取9个洞的标准杆值传入ViewModel List<Integer> pars = new ArrayList<>(); pars.add(Integer.parseInt(tvPar1.getText().toString())); pars.add(Integer.parseInt(tvPar2.getText().toString())); // 剩余7个洞的par值按顺序添加,和原有逻辑一致 viewModel.setCoursePars(pars);
数据绑定方案会自动处理双向数据同步,只要输入框内容变化,就会自动更新ViewModel里的分数,触发总得分重算并刷新TextView,代码维护性更高。
内容的提问来源于stack exchange,提问作者charbs29
相关产品推荐
相关产品推荐

