Android data binding : view does not update when property is changed

48,847

Solution 1

In case you might be experiencing this issue with LiveData, you might've forgotten to set lifecycle owner of your binding; binding.setLifecycleOwner(lifecycleOwner)

Solution 2

I had a similar issue when I needed to refresh the views after an event, with my variable objects not observable, so I added this:

binding?.invalidateAll()

I hope it helps.

Solution 3

You need to call executePendingBindings() for immediately update binding value in view:

When a variable or observable changes, the binding will be scheduled to change before the next frame. There are times, however, when binding must be executed immediately. To force execution, use the executePendingBindings() method.

Look at Advanced Binding chapter for more info

Solution 4

  • Make your model class fields private.
  • Change getCalcResult() should be public.
  • BaseView should be extending BaseObservable
  • If you don't change model value dynamically. Then you need not to make every accessor @Bindable. You can remove that. And also remove notifyPropertyChanged(). Opposite of this if you need to change model values dynamically then use @Bindable attribute.

You are good to go.

public class BaseCalcModel extends BaseObservable {
private String calcResult;
private BaseView firstNumView;
private BaseView secondNumView;
private BaseView resultNumView;
private int firstNumBase;
private int secondNumBase;
private int resultNumBase;
private String error;

public String getCalcResult() {
    return calcResult;
}

public void setCalcResult(String calcResult) {
    this.calcResult = calcResult;
}

public BaseView getFirstNumView() {
    return firstNumView;
}

public void setFirstNumView(BaseView firstNumView) {
    this.firstNumView = firstNumView;
}

public BaseView getSecondNumView() {
    return secondNumView;
}

public void setSecondNumView(BaseView secondNumView) {
    this.secondNumView = secondNumView;
}

public BaseView getResultNumView() {
    return resultNumView;
}

public void setResultNumView(BaseView resultNumView) {
    this.resultNumView = resultNumView;
}

public int getFirstNumBase() {
    return firstNumBase;
}

public void setFirstNumBase(int firstNumBase) {
    this.firstNumBase = firstNumBase;
}

public int getSecondNumBase() {
    return secondNumBase;
}

public void setSecondNumBase(int secondNumBase) {
    this.secondNumBase = secondNumBase;
}

public int getResultNumBase() {
    return resultNumBase;
}

public void setResultNumBase(int resultNumBase) {
    this.resultNumBase = resultNumBase;
}

public String getError() {
    return error;
}

public void setError(String error) {
    this.error = error;
}

}

Solution 5

I had the exact same problem. Apparently it was a single line that had been causing the issue. All you have to do is set the licycleowner to the binding.

binding.lifecycleOwner = this
Share:
48,847

Related videos on Youtube

Jack Guo
Author by

Jack Guo

Updated on July 09, 2022

Comments

  • Jack Guo
    Jack Guo almost 2 years

    let me first start with showing the code:

    build.gradle (module):

    android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    dataBinding {
        enabled = true
    }
    defaultConfig {
        applicationId "com.example.oryaa.basecalculator"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    
    
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.google.android.gms:play-services-appindexing:8.1.0'
    

    activity_main.xml:

    <data>
        <import type="android.view.View" />
        <variable
            name="baseCalcModel"
            type="com.example.oryaa.basecalculator.BaseCalcModel">
        </variable>
    </data>  <TextView
            android:id="@+id/resultOutput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/resultTextView"
            android:textColor="@color/DarkBlue"
            android:text="@{baseCalcModel.calcResult}"
            android:textSize="32dp" />
    

    MainActicity.java:

    public class MainActivity extends AppCompatActivity {
    
    EditText userInput = null;
    TextView resultTV = null;
    Spinner fromBaseSpinner = null;
    Spinner toBaseSpinner = null;
    ArrayList<String> spinnerArray = new ArrayList<>();
    String _allowedChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String _onlyOnceChar = "-+*/";
    BaseCalcModel baseCalcModel = new BaseCalcModel();
    
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setBaseCalcModel(this.baseCalcModel);
        this.resultTV = (TextView) this.findViewById(R.id.resultOutput);
        this.fromBaseSpinner = (Spinner) findViewById(R.id.fromBaseSpinner);
        this.toBaseSpinner = (Spinner) findViewById(R.id.toBaseSpinner);
        this.userInput = (EditText) findViewById(R.id.userInput);
        SetupUI();
        baseCalcModel.setCalcResult("test");
    
        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
    }
    

    BaseCalcModel.java:

    ublic class BaseCalcModel extends BaseObservable {
    
    
    public String calcResult;
    public BaseView firstNumView;
    public BaseView secondNumView;
    public BaseView resultNumView;
    public int firstNumBase;
    public int secondNumBase;
    public int resultNumBase;
    public String error;
    
    
    @Bindable
    String getCalcResult() {
        return calcResult;
    }
    
    @Bindable
    public BaseView getFirstNumView() {
        return firstNumView;
    }
    
    @Bindable
    public BaseView getSecondNumView() {
        return secondNumView;
    }
    
    @Bindable
    public BaseView getResultNumView() {
        return this.resultNumView;
    }
    
    @Bindable
    public int getFirstNumBase() {
        return this.firstNumBase;
    }
    
    @Bindable
    public int getSecondNumBase() {
        return this.secondNumBase;
    }
    
    @Bindable
    public int getResultNumBase() {
        return this.resultNumBase;
    }
    
    @Bindable
    public String getError() {
        return this.error;
    }
    
    public void setCalcResult(String calcResult) {
        this.calcResult = calcResult;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.calcResult);
    }
    
    public void setFirstNumView(BaseView firstNumView) {
        this.firstNumView = firstNumView;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.firstNumView);
    }
    
    public void setSecondNumView(BaseView secondNumView) {
        this.secondNumView = secondNumView;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.secondNumView);
    }
    
    public void setResultNumView(BaseView resultNumView) {
        this.resultNumView = resultNumView;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.resultNumView);
    }
    
    public void setFirstNumBase(int firstNumBase) {
        this.firstNumBase = firstNumBase;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.firstNumBase);
    }
    
    public void setSecondNumBase(int secondNumBase) {
        this.secondNumBase = secondNumBase;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.secondNumBase);
    }
    
    public void setResultNumBase(int resultNumBase) {
        this.resultNumBase = resultNumBase;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.resultNumBase);
    }
    
    public void setError(String error) {
        this.error = error;
        notifyPropertyChanged(com.example.oryaa.basecalculator.BR.error);
    }
    
    
    public BaseCalcModel() {
        firstNumView = new BaseView();
        secondNumView = new BaseView();
        resultNumView = new BaseView();
        firstNumBase = 0;
        secondNumBase = 0;
        resultNumBase = 0;
        calcResult = "";
        error = "";
    }
    
    public BaseCalcModel(BaseView firstNumView, BaseView secondNumView, BaseView resultNumView,
                         int firstNumBase, int secondNumBase, int resultNumBase, String clcResult,
                         String error) {
        this.firstNumView = firstNumView;
        this.secondNumView = secondNumView;
        this.resultNumView = resultNumView;
        this.firstNumBase = firstNumBase;
        this.secondNumBase = secondNumBase;
        this.resultNumBase = resultNumBase;
        this.calcResult = clcResult;
        this.error = error;
    }
    

    enter image description here

    Im trying to do simple data binding, but my view doesn't updating after the proparty is changing. as you can see in the image, my code arriving to:

                notifyPropertyChanged(com.example.oryaa.basecalculator.BR.calcResult);
    

    but the view is updating only when the app started or when I'm rotating my phone for vertical to horizontal or vice versa.

    Where is my problem?

    Thanks a lot, Or Yaacov

    • GvSharma
      GvSharma over 7 years
      remove this line from MainActivity() "setContentView(R.layout.activity_main);" And try again
    • Admin
      Admin over 7 years
      No, still doesn't work
    • George Mount
      George Mount over 7 years
      Try making the fields private. It may be preferring to access the fields over the accessor methods.
    • Kirill Kulakov
      Kirill Kulakov over 7 years
      Why is your DTO holds on to a BaseView? I doubt it's related to the issue, but it sounds like really bad practice
    • Admin
      Admin over 7 years
      @GeorgeMount If I'm changing all the fields to private I'm getting the next compelation error: Error:(14, 52) error: package com.example.oryaa.basecalculator.databinding does not exist Error:Execution failed for task ':app:compileDebugJavaWithJavac'. > java.lang.RuntimeException: Found data binding errors. ****/ data binding error ****msg:Could not find accessor com.example.oryaa.basecalculator.BaseCalcModel.calcResult file:...\activity_main.xml loc:78:28 - 78:51 ****\ data binding error **** and KirillKulakov BaseView is an self made object that contains 4 Strings.
    • George Mount
      George Mount over 7 years
      Yes, getCalcResult() must be public.
  • Alif Hasnain
    Alif Hasnain over 4 years
    is this the optimal solution?
  • oxied
    oxied about 4 years
    invalidateAll works for me. executePendingBindings doesn't.
  • Serdar Samancıoğlu
    Serdar Samancıoğlu over 3 years
    For me, this was the case. Thank you!
  • Panos Gr
    Panos Gr over 3 years
    binding?.invalidateAll() causes weird layout problems and should be avoided. Prefer to use LiveData or ObservableFields to wrap your data and pass it into your bindings if your data needs to handle change of state.
  • Andrew
    Andrew almost 3 years
    Where exactly do we but this, if we use databinding inside a fragment????
  • Sergei Bubenshchikov
    Sergei Bubenshchikov almost 3 years
    @Andrew you can call this method when your data updated (when you bind data to view holder for example)
  • UnknownDeveloper
    UnknownDeveloper over 2 years
    Why did something like this happen?