53

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

Jack Guo
  • 3,959
  • 8
  • 39
  • 60
  • remove this line from MainActivity() "setContentView(R.layout.activity_main);" And try again – GvSharma Jan 28 '17 at 19:44
  • No, still doesn't work –  Jan 28 '17 at 19:55
  • Try making the fields private. It may be preferring to access the fields over the accessor methods. – George Mount Jan 29 '17 at 03:17
  • Why is your DTO holds on to a `BaseView`? I doubt it's related to the issue, but it sounds like really bad practice – Kirill Kulakov Jan 29 '17 at 07:45
  • @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. –  Jan 29 '17 at 20:17
  • Yes, getCalcResult() must be public. – George Mount Jan 30 '17 at 02:36

7 Answers7

97

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

dgngulcan
  • 3,101
  • 1
  • 24
  • 26
52

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.

uan
  • 857
  • 7
  • 17
  • 1
    is this the optimal solution? – Alif Hasnain Feb 05 '20 at 09:48
  • 3
    `invalidateAll` works for me. `executePendingBindings` doesn't. – oxied Apr 23 '20 at 10:32
  • 1
    `binding?.invalidateAll()` causes weird layout problems and should be avoided. Prefer to use `LiveData` or `ObservableField`s to wrap your data and pass it into your bindings if your data needs to handle change of state. – Panos Gr Oct 12 '20 at 16:51
18

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

Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60
3

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
PraZ
  • 57
  • 1
  • 8
3

In my case I was forgetting to setContentView(binding.root). I'm using a ResultReceiver as a subclass of my Activity class. After following the setup documentation I ended up with this:

class MyActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener, ServiceConnection {

    lateinit var txtV     : TextView
    lateinit var binding  : MyActivityBinding
    lateinit var receiver : MyResultReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = MyActivityBinding.inflate(layoutInflater)
        setContentView(binding.root)                           // <--
        receiver = MyResultReceiver(Handler(), binding)
        ...
    }

    class MyResultReceiver(handler: Handler?, binding : MyActivityBinding) : ResultReceiver(handler) {
        private var mBinding = binding
        override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
            mBinding.txtV.append("\n${resultData?.getString("theTag")}")
        }
    }
}
vesperto
  • 804
  • 1
  • 6
  • 26
2
  • 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;
}

}

Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
1

Please make sure you are updating the same object field when data binding does not work on you. The object's instance must be the same in order to update UI. Maybe my answer helps to someone, enjoy coding!

nAkhmedov
  • 3,522
  • 4
  • 37
  • 72