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

RecyclerView项EditText修改值后重启Activity无法保留(Room+TextWatcher)

问题原因与解决方案

核心问题分析

  1. 输入值未持久化到数据库:EditText的TextWatcher仅修改了内存中SetItem的字段值,但没有调用Room的更新方法将这些值写入数据库,导致重启后数据丢失。
  2. 冗余字段设计:storedRep/storedWeight和rep/weight功能完全重复,既增加维护成本,也导致UI显示依赖的字段未被正确初始化和更新。
  3. UI绑定逻辑错误:重启后从数据库读取的SetItem中,storedRep/storedWeight始终是初始值0,而Adapter绑定UI时依赖的正是这些未被正确更新的字段。

具体修复步骤

1. 简化SetItem实体(移除冗余字段)

删除storedRep和storedWeight字段,直接用rep和weight存储用户输入:

@Entity(foreignKeys = @ForeignKey(entity = ExerciseItem.class,
        parentColumns = "id",
        childColumns = "exercise_id",
        onDelete = ForeignKey.CASCADE))
public class SetItem {
    @PrimaryKey(autoGenerate = true)
    public int id;
    @ColumnInfo(name = "exercise_rep")
    public int rep;
    @ColumnInfo(name = "exercise_weight")
    public double weight;
    @ColumnInfo(name = "exercise_id")
    public int exercise_id;

    public int getId() {
        return id;
    }

    public int getRep() {
        return rep;
    }
    public double getWeight() {
        return weight;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setExercise_id(int exercise_id) {
        this.exercise_id = exercise_id;
    }

    public void setRep(int rep) {
        this.rep = rep;
    }
    public void setWeight(double weight) {
        this.weight = weight;
    }
    public int getExercise_id(){
        return exercise_id;
    }
}

2. 给SetDao添加更新方法

新增更新接口,用于将输入值持久化到数据库:

@Dao
public interface SetDao {
    @Query("SELECT * FROM setitem WHERE exercise_id = :exerciseId")
    List<SetItem> getSetsForExercise(int exerciseId);
    @Insert
    void insertSetItem(SetItem... setItems);
    @Delete
    void delete(SetItem setItem);
    // 新增更新方法
    @Update
    void updateSetItem(SetItem setItem);
}

3. 修改SetAdapter的TextWatcher逻辑

移除对冗余字段的操作,直接更新rep/weight并同步到数据库:

@Override
public void onBindViewHolder(@NonNull SetAdapter.ViewHolder holder, int position) {
    SetItem set = setList.get(position);
    holder.textView_setNumber.setText(String.valueOf(position + 1));

    // 初始化EditText显示值
    holder.editText_rep.setText(String.valueOf(set.getRep()));
    holder.editText_weight.setText(String.valueOf(set.getWeight()));

    holder.editText_rep.addTextChangedListener(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){
            try {
                int reps = Integer.parseInt(s.toString());
                set.setRep(reps);
                // 同步更新数据库
                AppDatabase.getInstance(context).setDao().updateSetItem(set);
            } catch (NumberFormatException e) {
                // 处理非法输入,默认设为0
                set.setRep(0);
                AppDatabase.getInstance(context).setDao().updateSetItem(set);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {}
    });

    holder.editText_weight.addTextChangedListener(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) {
            try {
                double weight = Double.parseDouble(s.toString());
                set.setWeight(weight);
                // 同步更新数据库
                AppDatabase.getInstance(context).setDao().updateSetItem(set);
            } catch (NumberFormatException e) {
                // 处理非法输入,默认设为0
                set.setWeight(0);
                AppDatabase.getInstance(context).setDao().updateSetItem(set);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {}
    });
}

4. 清理Exercise Activity的冗余代码

删除loadSavedValues和setStoredValues方法,直接通过loadSetList加载数据库数据:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_exercise);

    setAdd = findViewById(R.id.textView_setAdd);
    exerciseName = findViewById(R.id.textView_exerciseName);
    btnSetAdd = findViewById(R.id.btnAddSet);

    String selectedExerciseName = getIntent().getStringExtra("EXERCISE_NAME");
    exerciseName.setText(selectedExerciseName);

    initRecyclerView();
    loadSetList(); // 仅保留这一行即可加载历史数据

    btnSetAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int rep = 0;
            double weight = 0;
            int exerciseId = getIntent().getIntExtra("EXERCISE_ID", -1);

            addNewSet(rep, weight, exerciseId);
        }
    });
}

// 删除loadSavedValues和setStoredValues方法

private void loadSetList() {
    AppDatabase database = AppDatabase.getInstance(this.getApplicationContext());
    int exerciseId = getIntent().getIntExtra("EXERCISE_ID", -1);
    if (exerciseId != -1) {
        List<SetItem> setsForExercise = database.setDao().getSetsForExercise(exerciseId);
        recyclerViewAdapter.setSetList(setsForExercise);
    } else {
        Log.e("WorkoutActivity", "No workout ID found in the intent");
    }
}

5. 更新数据库版本

由于修改了SetItem实体,需要升级Room数据库版本:

@Database(entities = {WorkoutItem.class, ExerciseItem.class, SetItem.class}, version = 2) // 版本号从1改为2
public abstract class AppDatabase extends RoomDatabase {
    // 其余代码保持不变
}

测试阶段可直接卸载APP重新安装;正式环境需编写Migration逻辑避免数据丢失。


额外优化建议

  • 移除主线程数据库操作:当前使用allowMainThreadQueries(),正式环境建议用Coroutines或AsyncTask执行数据库操作,避免ANR。
  • 限制EditText输入类型:给edittext_rep设置数字输入类型,edittext_weight设置带小数点的数字输入类型,减少非法输入。
  • 添加输入防抖:TextWatcher输入时会频繁触发,可添加500ms延迟再更新数据库,减少数据库操作次数。

内容的提问来源于stack exchange,提问作者VGoychev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 19:57:00