The android data binding is not working properly

11,212

EDIT: According to Remi David Android Studio now features two way databinding. The solution I describe below should no longer be nessesary.

Thanks Remi for pointing that out.


With respect to user input Android Databinding is only one way. The GUI will automagically reflect any changes in the model, but not vice versa.

You need to add a TextChangeListener to your model class which sets the model property when the user changes anything.

Example:

public class AmanteEditModel extends BaseObservable {

    private String senhaConfirm;

    @Bindable
    public String getSenhaConfirm() {
        return senhaConfirm;
    }

    public void setSenhaConfirm(String senhaConfirm) {
        this.senhaConfirm = senhaConfirm;
        notifyPropertyChanged(BR.senhaConfirm);
    }

    // Textwatcher Reference: http://developer.android.com/reference/android/text/TextWatcher.html
    public TextWatcher getMyEditTextWatcher() {
        return new TextWatcher() {

            public void afterTextChanged(Editable s) {
            }

            public void beforeTextChanged(CharSequence s, int start,
                                          int count, int after) {
            }

            public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
                // Important! Use the property setter, otherwhise the model won't be informed about the change.
                setSenhaConfirm(s);
            }
        };
    }

}

In your layout xml change EditText to this:

<EditText
    android:id="@+id/amante_edit_senha_confirm"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:hint="Confirme a senha"
    android:inputType="textPassword"
    android:maxLines="1"
    android:text="@{model.senhaConfirm}"
    app:addTextChangeListener="@{model.myEditTextWatcher}"
    />

Watch for the namespace of addTextChangeListener. This method might not be available through the android: namespace, so I'm using app: here. You may also use bind: to make the binding more clear.

So don't miss to add

xmlns:app="http://schemas.android.com/apk/res-auto"

or

xmlns:bind="http://schemas.android.com/apk/res-auto"

to your XML namespaces.

This solution works for all input controls, custom included, given you provide the correct Listeners in your model.

TextWatcher Reference

Share:
11,212
araraujo
Author by

araraujo

Updated on June 24, 2022

Comments

  • araraujo
    araraujo about 2 years

    I would like to help to fix a problem.

    First, following the details of my code:

    build.gradle (Project: android)
    
    buildscript {
        repositories {
            jcenter()
            mavenCentral()
            maven { url '/home/melti/java/repository' }
        }
        dependencies {
            classpath "com.android.tools.build:gradle:1.3.0-beta4"
            classpath "com.android.databinding:dataBinder:1.0-rc0"
    
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            mavenCentral()
            maven { url '/home/melti/java/repository' }
    
        }
    }
    

    build.gradle (Module: app)

    apply plugin: 'com.android.application'
    apply plugin: 'com.android.databinding'
    
    android {
        compileSdkVersion 22
        buildToolsVersion "23.0.0 rc2"
    
        defaultConfig {
            applicationId "br.com.soma"
            minSdkVersion 16
            targetSdkVersion 21
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.0.0'
        compile 'com.android.support:recyclerview-v7:21.0.3'
        compile 'com.android.support:cardview-v7:21.0.3'
        compile 'com.squareup.picasso:picasso:2.5.2'
        compile 'com.squareup.okhttp:okhttp:2.4.0'
        compile 'com.google.code.gson:gson:2.3.1'
        compile 'org.springframework:spring-core:4.1.7.RELEASE'
        compile 'org.apache.commons:commons-io:1.3.2'
    
    
    }
    

    AmanteEditModel

    package br.com.soma.amante.edit;
    
    import android.databinding.BaseObservable;
    import android.databinding.Bindable;
    
    import br.com.soma.BR;
    
    /**
     * Created by spassu on 09/07/15.
     */
    public class AmanteEditModel extends BaseObservable {
    
        private String senhaConfirm;
    
    
    
        @Bindable
        public String getSenhaConfirm() {
            return senhaConfirm;
        }
    
        public void setSenhaConfirm(String senhaConfirm) {
            this.senhaConfirm = senhaConfirm;
            notifyPropertyChanged(BR.senhaConfirm);
        }
    }
    

    fragment_amante_edit

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto">
    
        <data>
            <variable name="model" type="br.com.soma.amante.edit.AmanteEditModel"/>
        </data>
    
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
    
            <android.support.v7.widget.CardView
                xmlns:android="http://schemas.android.com/apk/res/android"
                style="@style/card_edit_style"
                android:layout_gravity="top"
                card_view:cardCornerRadius="0dp">
    
    
                        <EditText
                            android:id="@+id/amante_edit_senha_confirm"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center_vertical"
                            android:hint="Confirme a senha"
                            android:inputType="textPassword"
                            android:maxLines="1"
                            android:text="@{model.senhaConfirm}"
                        />
    
            </android.support.v7.widget.CardView>
    
        </ScrollView>
    </layout>
    

    AmanteEditFragment

    package br.com.soma.amante.edit;
    
    import android.app.Fragment;
    import android.databinding.DataBindingUtil;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Toast;
    
    import br.com.soma.R;
    import br.com.soma.amante.view.AmanteViewActivity;
    import br.com.soma.databinding.FragmentAmanteEditBinding;
    
    /**
     * Created by spassu on 27/05/15.
     */
    public class AmanteEditFragment extends Fragment {
    
        private AmanteEditModel model;
    
        private static final String AMANTE_ID = "amanteId";
        public static final String DEFAULT_FRAGMENT_TAG = "amanteEditFragment";
    
        // Views
        private long amanteId;
    
        public AmanteEditFragment() {
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            model = new AmanteEditModel();
            FragmentAmanteEditBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_amante_edit, container, false);
            binding.setModel(model);
            return binding.getRoot();
        }
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            setHasOptionsMenu(true);
        }
    
        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            super.onCreateOptionsMenu(menu, inflater);
            inflater.inflate(R.menu.menu_amante_edit, menu);
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch(item.getItemId()) {
                // The Save button was pressed
                case R.id.menu_amante_edit_save:
                    save();
                    getActivity().finish();
                    return true;
                }
            return super.onOptionsItemSelected(item);
        }
    
    
        public static AmanteEditFragment newInstance(long id) {
            AmanteEditFragment fragment = new AmanteEditFragment();
            Bundle args = new Bundle();
            args.putLong(AmanteViewActivity.EXTRA_AMANTEID, id);
            fragment.setArguments(args);
            return fragment;
        }
    
        private void save() {
            Toast.makeText(getActivity(), model.getSenhaConfirm(), Toast.LENGTH_SHORT).show();
        }
    }
    

    After enter data in senhaConfirm and clickingsave(), I found that the binding does not work, that is, the senhaConfirm into AmanteEditModel is null yet. Can anyone help me?