Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 与EditText冲突:视图的观察者+;MutableLiveData的观察者_Java_Android_View_Viewmodel_Mutablelivedata - Fatal编程技术网

Java 与EditText冲突:视图的观察者+;MutableLiveData的观察者

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

我很难理解片段+视图模型范式如何与编辑文本这样的视图一起工作

作为一个EditText,它显然会在视图(片段)中被修改。但我也希望能够在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) { }
        });
当我运行应用程序时,不会弹出错误,但它会挂起。我猜是因为一个无限循环。 我应该如何使用这个视图+视图模型范例来处理EditText?我不明白什么


提前多谢

您可以为此使用双向数据绑定:

  • 当用户输入文本时:实时数据将被更新
  • 如果以编程方式设置live data值,则将更新EditText内容
您应该能够删除活动中的两个侦听器,因为数据绑定为您做到了这一点

build.gradle:

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作为字段公开(kotlin
val
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