RecyclerView项EditText修改值后重启Activity无法保留(Room+TextWatcher)
问题原因与解决方案
核心问题分析
- 输入值未持久化到数据库:EditText的TextWatcher仅修改了内存中
SetItem的字段值,但没有调用Room的更新方法将这些值写入数据库,导致重启后数据丢失。 - 冗余字段设计:
storedRep/storedWeight和rep/weight功能完全重复,既增加维护成本,也导致UI显示依赖的字段未被正确初始化和更新。 - 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
相关产品推荐
相关产品推荐

