我无法理解 Fragment + ViewModel 范例如何与像 EditText 这样的View一起使用。
它是一个EditText,显然将在View(片段)中进行修改。但我也希望能够在ViewModel中进行修改:例如删除其文本。
这是Fragment类中的代码:
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) { }
});
如您所见,我设置了一个观察者,因此当我更改MutableLiveData的值时,视图也会更改。我设置了 watcher ,因此(当我使用应用程序时)更改View的值时,MutableLiveData也会更改。
这是ModelView类的代码:
public void addRegister() {
...
String comment = this.comment.getValue();
...
this.comment.setValue("");
当我运行该应用程序时,没有错误弹出,但挂起。我猜是因为无限循环。 如何使用此View + ViewModel范例处理EditTexts?我不明白什么?
非常感谢!
答案 0 :(得分:0)
您可以为此使用双向数据绑定:
您应该能够删除活动中的两个侦听器,因为数据绑定会为您这样做。
build.gradle:
android {
dataBinding {
enabled = true
}
}
布局:
<layout>
元素EditText
连接到视图模型<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)
请注意,您需要等号@=
才能进行双向数据绑定。如果仅使用@{viewModel.getComment()}
,则以编程方式设置实时数据值时,编辑文本将被更新,但另一种方法将无效。
注意:
ObservableField
代替MutableLiveData
进行数据绑定@={viewModel.comment}
这样的方法引用来引用xml中的实时数据参考:双向数据绑定的Android文档:https://developer.android.com/topic/libraries/data-binding/two-way
答案 1 :(得分:0)
在您的评论liveData观察者中,只需先取消注册TextWatcher,然后从评论liveData中取消setText,然后重新注册TextWatcher,就可以了:)
答案 2 :(得分:0)
由于接受的答案在所有情况下都不适用于我(当 ViewModel 中的文本通过 EditText 本身以外的其他方式更改时),而且我也不想使用数据绑定,我想出了以下解决方案,其中一个标志跟踪由 TextWatcher 发起的更新,并在调用观察者时中断循环:
这是我在 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
}
}
}
为了完整性还有 ViewModel:
class SecondViewModel() : ViewModel()
{
var editText: String
get() {
return editTextLiveData.value ?: "InitialLiveData"
}
set(value) {
editTextLiveData.value = value
}
var editTextLiveData = MutableLiveData<String>()
}
以防万一,您不熟悉视图绑定:您可以替换
binding.editText
与
findViewById(R.id.editTextId) as EditText
。