Java 与EditText冲突:视图的观察者+;MutableLiveData的观察者
我很难理解片段+视图模型范式如何与编辑文本这样的视图一起工作 作为一个EditText,它显然会在视图(片段)中被修改。但我也希望能够在ViewModel中对其进行修改:例如,删除其文本 下面是Fragment类中的代码:Java 与EditText冲突:视图的观察者+;MutableLiveData的观察者,java,android,view,viewmodel,mutablelivedata,Java,Android,View,Viewmodel,Mutablelivedata,我很难理解片段+视图模型范式如何与编辑文本这样的视图一起工作 作为一个EditText,它显然会在视图(片段)中被修改。但我也希望能够在ViewModel中对其进行修改:例如,删除其文本 下面是Fragment类中的代码: public void onActivityCreated(@Nullable Bundle savedInstanceState) { ... comment = mViewModel.getComment(); comment.observ
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
...
comment = mViewModel.getComment();
comment.observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
commentView.setText(s);
}
});
...
commentView.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
mViewModel.setComment(String.valueOf(s));
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
});
当我运行应用程序时,不会弹出错误,但它会挂起。我猜是因为一个无限循环。
我应该如何使用这个视图+视图模型范例来处理EditText?我不明白什么
提前多谢 您可以为此使用双向数据绑定:
- 当用户输入文本时:实时数据将被更新
- 如果以编程方式设置live data值,则将更新EditText内容
android {
dataBinding {
enabled = true
}
}
布局:
- 在顶层添加一个
元素 - 为viewmodel定义一个变量
- 将编辑文本连接到视图模型
@=
才能进行双向数据绑定。如果只使用@{viewModel.getComment()}
,则如果以编程方式设置实时数据值,则编辑文本将被更新,但另一种方式不起作用
注:
- 如果愿意,可以使用
而不是observefield
进行数据绑定MutableLiveData
- 也许您可以使用字段引用而不是方法引用来引用xml中的实时数据,如
@={viewModel.comment}
参考:用于双向数据绑定的Android文档:在comment liveData的observer中,只需先注销TextWatcher,然后在comment liveData中设置文本后,重新注册TextWatcher,应该可以:),因为接受的答案在所有情况下都不适用于我(当文本在ViewModel中通过EditText本身以外的其他方式更改时),我也不想使用数据绑定,我提出了以下解决方案,其中一个标志跟踪更新,由TextWatcher启动,并在调用observer时中断循环: 这是我在Kotlin的代码。 对于活动:
class SecondActivity : AppCompatActivity() {
/** Flag avoids endless loops from TextWatcher and observer */
private var textChangedByListener = true
private val viewModel by viewModels<SecondViewModel>()
private lateinit var binding:SecondActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = SecondActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.editText.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun afterTextChanged(editable: Editable?) {
textChangedByListener = true
viewModel.editText = editable.toString()
}
})
viewModel.editTextLiveData.observe(this) { text -> setEditTextFromViewModel(text) }
}
private fun setEditTextFromViewModel(text: String?) {
if (!textChangedByListener) {
//text change was not initiated by the EditText itself, and
//therefore EditText does not yet contain the new text.
binding.editText.setText(text)
} else {
//Don't move that outside of else, because it would then
//immediately overwrite the value set by TextWatcher
//which is triggered by the above setText() call.
textChangedByListener = false
}
}
}
与
findViewById(R.id.editTextId)作为EditText
在哪里实现了getComment()
?能否显示所有ViewModelclass@RafsanjanigetComment()只返回注释。这是片段:这是ViewModel:你能提供ViewModel类代码吗?@SamirSpahic是的,这是:尝试将getter字段改为LiveData
类型,而不是MutableLiveData
,非常感谢你的回复,我肯定会研究这个问题。尽管我读过一些负面评论关于数据绑定库,说将逻辑或函数放在静态xml文件中是不好的做法。你认为呢?Android的数据绑定现在是一个标准吗?你应该避免在xml文件中放入任何重要的逻辑。但是,作为一个典型的基本数据绑定示例,这里实际上没有逻辑:它只是映射一个视图到viewmodel字段(通常我将MutableLiveData
s作为字段公开(kotlinval
s),因此我通常使用android:text=“@={viewmodel.comment}”
而不是函数getComment()
)取消注册TextWatcher是什么意思?目前您的代码是commentView.addTextChangedListener(anonymouseTextWatcherClass)
。您可以这样做,首先初始化textWatcher对象,如textWatcher=textWatcher()
,并将其保存为字段变量。然后,当您准备将值设置为EditText时,首先通过调用此函数commentView来注销textWatcher。removeTextChangedListener(textWatcher);
,这样您就可以安全地设置值,而无需触发textWatcher。设置完值后,再次注册textWatcher,以便听到:)也许您可以将其设置为已接受的答案
<layout>
<data>
<variable
name="viewModel"
type="com.mycompany.AddRegisterViewModel" />
</data>
<EditText
android:id="..."
android:layout_width="..."
android:layout_height="..."
android:text="@={viewModel.getComment()}" />
</layout>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding: MyFragmentBinding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false)
binding.setViewModel(myViewModel)
class SecondActivity : AppCompatActivity() {
/** Flag avoids endless loops from TextWatcher and observer */
private var textChangedByListener = true
private val viewModel by viewModels<SecondViewModel>()
private lateinit var binding:SecondActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = SecondActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.editText.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun afterTextChanged(editable: Editable?) {
textChangedByListener = true
viewModel.editText = editable.toString()
}
})
viewModel.editTextLiveData.observe(this) { text -> setEditTextFromViewModel(text) }
}
private fun setEditTextFromViewModel(text: String?) {
if (!textChangedByListener) {
//text change was not initiated by the EditText itself, and
//therefore EditText does not yet contain the new text.
binding.editText.setText(text)
} else {
//Don't move that outside of else, because it would then
//immediately overwrite the value set by TextWatcher
//which is triggered by the above setText() call.
textChangedByListener = false
}
}
}
class SecondViewModel() : ViewModel()
{
var editText: String
get() {
return editTextLiveData.value ?: "InitialLiveData"
}
set(value) {
editTextLiveData.value = value
}
var editTextLiveData = MutableLiveData<String>()
}
binding.editText