23

I'm trying to come up with a way to have an EditText update the data of a ViewModel and simultaneously observe that data for any changes (e.g. changes brought about by manipulating the DB). Is there a way to do this without using the data binding library?

The main problem I'm facing while simply using MutableLiveData is the following:

when the user enters text in the EditText, a TextWatcher pokes the ViewModel to update its data, which in turn will set the new text to the MutableLiveData object. Because the EditText is observing the LiveData, the onChange is triggered and sets the text of the EditText accordingly, which in turn will trigger the TextWatcher again creating an infinite loop.

kazume
  • 1,333
  • 2
  • 16
  • 30
  • You can do MutableLiveData<> ... Have you tried? – Ümañg ßürmån Sep 26 '18 at 11:06
  • I added some more (missing) info on that matter. – kazume Sep 26 '18 at 11:11
  • Okay. Why don't you want to use the databinding with live data.? – Ümañg ßürmån Sep 26 '18 at 11:27
  • 4
    One way to prevent the infinite loop: when your `TextWatcher` gets triggered, you can check if the data has changed. If there are no changes, than just dont set the `MutableLiveData`. But i guess its not the best solution, there must be some other way to get the desired result. – kAliert Sep 26 '18 at 11:28
  • As i figured out, you are using LiveData and ViewModel in wrong way, i have a question: Why your edit text observes that MutableLiveData? – mortezahosseini Sep 26 '18 at 11:47
  • Maybe you're right. I want it this way, because the user is not the only one that can alter the data via the UI. So I figured, to reflect any DB changes from other sources, I'd have to let the UI observe the LiveData as well. It might just not be the best approach. – kazume Sep 26 '18 at 14:33
  • 1
    Please refer - https://stackoverflow.com/questions/33362533/create-two-way-binding-with-android-data-binding . and https://www.bignerdranch.com/blog/two-way-data-binding-on-android-observing-your-view-with-xml/ . Also @kAliert comment seems working solution. – maveroid Sep 28 '18 at 07:55
  • 4
    Please, stop posting data binding solutions, it states in the title: without using data binding. – Ghedeon Dec 18 '18 at 12:55

4 Answers4

7

I also ran into this problem since I don't like the databinding library. I did as @kAliert said, but in my ViewModel to keep the logic there. I just added a simple catch on the function that receives my text changes events in the ViewModel. It works well.

fun editTextChanged(newText: String) {
    if (newText == textLiveData.value) {
        return
    }
}
Carson Holzheimer
  • 2,890
  • 25
  • 36
2

May be a little late, but hope useful for somebody. I had this problem and what I did was consider a string member for the viewmodel class to maintain last value of editText. Everytime it would be to return livedata, It checks it first is there difference between new edittext input(getData() parameter) and its past value (saved in myVariable):

  public class MyViewModel extends ViewModel {
  private MutableLiveData<DataType> data;
  private String myVariable; //maintainer variable

  public LiveData<DataType> getData(String input){
    if(data == null || !myVariable.equals(input)){
      myVariable = input;
      data = new MutableLiveData<>();
      loadData(data, input);
    }
    return data;
  }

and in your owner(eg. Activity) observe data changes:

myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);    
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) {
     myViewModel.getData(s).observe(this, new Observer<DataType>() {
      @Override
      public void onChanged(<DataType> data) {
        //do everything you want like update UI by data
      }
    });
  }

  @Override
  public void afterTextChanged(Editable s) {         
  }
});
Reyhane Farshbaf
  • 453
  • 6
  • 12
  • `//do everything you want like update UI by data` is it okay to keep emty if i dont do anything ? – IRON MAN Oct 19 '20 at 11:47
  • What do you want to keep empty?! If you mean keep edittext empty, so it's text not changed and onChanged of observer not triggered. and anything inside it's body happen. – Reyhane Farshbaf Oct 22 '20 at 08:25
0

In opposition to Carson Holzheimer solution I moved the validation to the UI layer (e.g. Activity or Fragment):

viewModel.textValue.observe(this, { text ->
    if (myEditText.text.toString() != text) {
        myEditText.setText(text)
    }
})

This helped me to avoid an infinite loop.

Mikhail
  • 2,612
  • 3
  • 22
  • 37
-1

I think you can also use LiveData.distinctUntilChanged(), which will only emit data when new value is set.

Jeff Padgett
  • 2,380
  • 22
  • 34