You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
}

这个方案不需要处理索引对应关系,不管用户修改哪个洞的分数、输入顺序是什么,都能正确计算实时总分,不会出现重复累加的问题。

规范双向数据绑定实现方案

如果要使用双向数据绑定实现,按照以下步骤配置即可:

  1. 首先在模块级build.gradle中开启数据绑定开关:
android {
    // 原有其他配置,比如compileSdkVersion等
    buildFeatures {
        dataBinding true
    }
}
  1. 定义计分卡的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));
    }
}
  1. 修改布局文件,用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>
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:27:30