Two-way DataBinding in Android

In the earlier posts we have seen how to use DataBinding, Setting custom font and Image loading. Today we will discuss about two-way DataBinding.
What is Two-way DataBinding?
Till now we have seen how to set values to xml view, but in controls like EditText we need to fetch value. In simple term two-way DataBinding is setting values to that control and fetching it after having some changes.
ObservableField
ObservableField can be used instead of extending BaseObservable class. ObservableFields are self-contained observable objects that have a single field.
1 2 3 4 5 6 7 |
public class User { public final ObservableField<String> userName = new ObservableField<>(); public final ObservableField<String> password = new ObservableField<>(); } |
There is no need to create getter setter methods when we are using ObservableFields. Now attach custom TextWatcher to get values of EditText control.
1 2 |
public TextWatcherAdapter userNameWatcher = new TextWatcherAdapter(userName); public TextWatcherAdapter passwordWatcher = new TextWatcherAdapter(password); |
TextWatcherAdapter is custom class which extends TextWatcher. It will be used to fetch updated value of EditText.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public class TextWatcherAdapter implements TextWatcher { public final ObservableField<String> value = new ObservableField<>(); private final ObservableField<String> field; private boolean isInEditMode = false; public TextWatcherAdapter(ObservableField<String> f) { this.field = f; field.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback(){ @Override public void onPropertyChanged(Observable sender, int propertyId) { if (isInEditMode){ return; } value.set(field.get()); } }); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // } @Override public void afterTextChanged(Editable s) { if (!Objects.equals(field.get(), s.toString())) { isInEditMode = true; field.set(s.toString()); isInEditMode = false; } } } |
Layout file is very simple for this, with 2 EditTexts and a button. We need to set addTextChangeListener to bind custom TextWatcher with EditText.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="user" type="com.androidgig.logindemo.Model.User"></variable> <variable name="handler" type="com.androidgig.logindemo.Handler.ClickHandler"></variable> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:padding="20dp" android:orientation="vertical"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="User name" android:singleLine="true" android:imeOptions="actionNext" android:addTextChangedListener="@{user.userNameWatcher}"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Password" android:inputType="textPassword" android:singleLine="true" android:imeOptions="actionDone" android:layout_marginTop="10dp" android:addTextChangedListener="@{user.passwordWatcher}"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Login" android:layout_marginTop="20dp" android:onClick="@{handler.onClickLogin}"/> </LinearLayout> </layout> |
Here you have noticed handler.onClickLogin in a Button, that is nothing but an interface which we have binded to Button.
1 2 3 |
public interface ClickHandler { void onClickLogin(View view); } |
Finally we will write our Activity code to perform all the actions. Binding will be same as we have done earlier.
1 2 3 4 5 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_main); } |
we need to set Model/Pojo class to view and set handler for onClick event. Here is full code of our MainActivity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class MainActivity extends AppCompatActivity implements ClickHandler { ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding= DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setUser(new User()); binding.setHandler(this); } @Override public void onClickLogin(View view) { User user=binding.getUser(); if(validate(user)) { Toast.makeText(MainActivity.this,"Hello " + user.userName.get() + " your password is : " + user.password.get(),Toast.LENGTH_SHORT).show(); } } private boolean validate(User user) { if(user.userName.get() == null || user.userName.get().trim().length()<=0) { Toast.makeText(MainActivity.this,"Please enter username.",Toast.LENGTH_SHORT).show(); return false; } else if(user.password.get() == null || user.password.get().trim().length()<=0) { Toast.makeText(MainActivity.this,"Please enter password.",Toast.LENGTH_SHORT).show(); return false; } return true; } } |
With this you are done with your simple login screen using DataBinding.
Download code
Update :
With the latest update of DataBinding announced in Google I/O16, you will be able to use Two-Way DataBinding with minimal number of code. You don’t need to write those listeners. Here is the thing you need to change in your xml file.
1 |
android:text="@={user.name}" |
By adding = sign in your expression will support Two-Way DataBinding.
Ravi Rupareliya
Latest posts by Ravi Rupareliya (see all)
- Dialogflow Entities With Actions on Google - May 7, 2020
- Actions on Google Using Cloud Functions - February 3, 2020
- Create WhatsApp Stickers Android Application - April 19, 2019