Android中使用Data Binding保留EditText光标位置
解决EditText过滤字符时光标跳转到开头的问题
这个问题我之前开发Android项目时也碰到过,本质原因是你每次在setBuildingname()里调用notifyChange()后,DataBinding会重新给EditText设置处理后的文本,这就会强制把光标重置到文本开头。下面给你两个实用的解决思路:
方案一:通过TextWatcher在View层处理过滤并恢复光标位置
把字符过滤的逻辑从ViewModel的setter移到EditText的TextWatcher中,这样可以在修改文本前后手动保存和恢复光标位置,避免DataBinding触发的光标重置。
示例代码:
// 在你的Activity/Fragment中 EditText buildingNameEt = findViewById(R.id.building_name_et); int cursorPosition = 0; buildingNameEt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // 保存当前光标位置 cursorPosition = buildingNameEt.getSelectionStart(); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // 执行字符过滤 String filteredText = s.toString().replaceAll(alphanumericregex, ""); // 只有过滤后的文本和原文本不同时才更新 if (!filteredText.equals(s.toString())) { // 先移除监听,避免循环触发 buildingNameEt.removeTextChangedListener(this); buildingNameEt.setText(filteredText); // 计算并恢复光标位置,要考虑过滤后字符减少的情况 int newCursorPos = cursorPosition - (s.length() - filteredText.length()); buildingNameEt.setSelection(Math.max(0, newCursorPos)); // 重新添加监听 buildingNameEt.addTextChangedListener(this); // 同步更新ViewModel的值 yourViewModel.setBuildingname(filteredText); } } @Override public void afterTextChanged(Editable s) { // 这里可以留空或做其他后续处理 } });
同时ViewModel里的setter可以简化,不用再做过滤(因为已经在View层处理了):
private String buildingname=""; @Bindable public String getBuildingname() { return this.buildingname; } public void setBuildingname(String buildingname) { if(!this.buildingname.equals(buildingname)) { this.buildingname = buildingname; this.pcr.notifyChange(this, com.tomtom.sangrahit.BR.buildingname); } }
方案二:自定义BindingAdapter处理文本过滤
这种方式更符合DataBinding的设计思想,把过滤逻辑封装成自定义的BindingAdapter,在内部处理光标位置的保存和恢复。
示例代码:
// 自定义BindingAdapter工具类 public class EditTextBindingAdapters { @BindingAdapter("app:filteredText") public static void setFilteredText(EditText editText, String text) { // 避免重复设置文本触发不必要的刷新 if (editText.getText().toString().equals(text)) { return; } // 保存当前光标位置 int cursorPos = editText.getSelectionStart(); // 设置过滤后的文本 editText.setText(text); // 恢复光标位置,防止越界 editText.setSelection(Math.min(cursorPos, text.length())); } @InverseBindingAdapter(attribute = "app:filteredText") public static String getFilteredText(EditText editText) { // 这里执行字符过滤逻辑 return editText.getText().toString().replaceAll(alphanumericregex, ""); } @BindingAdapter(value = {"app:filteredTextAttrChanged"}) public static void setListener(EditText editText, final InverseBindingListener listener) { editText.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) { listener.onChange(); } @Override public void afterTextChanged(Editable s) {} }); } }
然后在布局文件中使用这个自定义属性:
<EditText android:id="@+id/building_name_et" android:layout_width="match_parent" android:layout_height="wrap_content" app:filteredText="@={viewModel.buildingname}" />
ViewModel里的setter和方案一一致,不需要再做过滤操作。
为什么原来的方式会导致光标跳转?
当你在setBuildingname()中修改buildingname并调用notifyChange()时,DataBinding会自动把EditText的text属性重新设置为新的buildingname值。这个重新设置的操作会让EditText默认把光标移到文本的起始位置,这就是问题的根源。通过上面的两种方式,我们要么在修改文本时手动控制光标位置,要么通过BindingAdapter封装这个逻辑,就能避免光标跳转的问题了。
内容的提问来源于stack exchange,提问作者Nigel D'souza
相关产品推荐
相关产品推荐

