Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Deep dive into Android Data Binding

4,946 views

Published on

Deep dive into Android Data Binding talk @ Droidcon Berlin 2016

Published in: Software

Deep dive into Android Data Binding

  1. 1. Deep dive into Android Data Binding +RadoslawPiekarz @radzio Radosław Piekarz Head of Mobile at Tango Agency
  2. 2. droidconde2016 talixo.de 10 € discount
  3. 3. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  4. 4. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  5. 5. Setup // <app-module>/build.gradle apply plugin: "com.android.application" android { dataBinding { enabled = true } }
  6. 6. Changes in layout file <layout xmlns:android="http://schemas.android.com/apk/res/android"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/my_id"/> </layout>
  7. 7. Changes in Activity / Fragment code @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainActivityBinding binding = DataBindingUtil .setContentView(this, R.layout.main_activity); binding.myId.setText("John Doe") } Type safe!
  8. 8. Binding utils DataBindingUtil.setContentView(activity, layoutId); DataBindingUtil.inflate(inflater, layoutId, parent, attachToParrent); ListItemBinding binding = ListItemBinding.bind(viewRoot); Use for activities Use for any view Bind view that was already inflated
  9. 9. Changes in layout file <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data class="CustomClassName"> <variable name="user" type="com.example.User"/> </data> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{user.firstName}" /> </layout>
  10. 10. Changes in Activity / Fragment Code @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil .setContentView(this, R.layout.main_activity); binding.setUser(new User()); }
  11. 11. Binding expression operators Grouping () Literals character, String, numeric, null Method calls, field access Ternary operator ?: Array access [] Null coalescing operator ?? Mathematical + - / * % String concatenation + Logical && || Binary & | ^ Unary + - ! ~ Shift >> >>> << Comparison == > < >= <= instanceof, cast
  12. 12. Binding expression operators android:text="@{user.displayName ?? user.lastName}" android:text="@{user.displayName != null ? user.displayName : user.lastName}" =
  13. 13. Notifying view #1 public class User extends BaseObservable { private String firstName; public User(String firstName) { this.firstName = firstName; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } @Bindable public String getFirstName() { return this.firstName; } }
  14. 14. Notifying view #2 public class User { public final ObservableField<String> firstName; public User(String name) { this.firstName = new ObservableField<>(firstName); } public void setName(String firstName) { this.firstName.set(firstName); } }
  15. 15. Observable fields & collections Observable<T> ObservableBoolean ObservableByte ObservableChar ObservableShort ObservableInt ObservableLong ObservableFloat ObservableDouble ObservableParcelable<T> ObservableList<T> ObservableArrayList<T> ObservableMap<K, V> ObservableArrayMap<K, V>
  16. 16. Binding Adapters <layout> <ImageView bind:imageUrl="@{viewModel.someNiceImageUrl}" bind:error="@{@drawable/defaultImage}"/> </layout> @BindingAdapter({"bind:imageUrl", "bind:error"}) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.with(view.getContext()).load(url).error(error).into(view); }
  17. 17. Binding Adapters @BindingAdapter({"bind:imageUrl", "bind:error"}) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.with(view.getContext()).load(url).error(error).into(view); }
  18. 18. java.lang.IllegalStateException Required DataBindingComponent is null. If you don't use an inflation method taking a DataBindingComponent, use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static.
  19. 19. Binding Component public class MyDataBindingCoponent implements DataBindingComponent { public EditTextBindings getEditTextBindings() { return new EditTextBindings(); } }
  20. 20. Binding Component @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil .setContentView(this, R.layout.activity_main, new MyDataBindingCoponent()); binding.setViewModel(new ViewModel()); }
  21. 21. Binding Conversions android:background="@{isError ? @color/red : @color/white}" @BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
  22. 22. Binding methods @BindingMethods({ @BindingMethod(type = CircularProgressBar.class, attribute = "progressText", method = "setProgressMessage") }) public class ViewBindings { }
  23. 23. RecyclerView bindings https://github.com/radzio/android-data-binding-recyclerview
  24. 24. Talk is cheap. Show me the code
  25. 25. RecyclerView Binding #1 <android.support.v7.widget.RecyclerView android:id="@+id/activity_users_recycler" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" app:items="@{usersViewModel.users}" app:itemViewBinder="@{view.itemViewBinder}" />
  26. 26. RecyclerView Binding #2 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); usersViewModel = new UsersViewModel(); usersViewModel.users .add(new SuperUserViewModel(new User("Android", "Dev"))); binding = DataBindingUtil.setContentView(this, R.layout.users_view); binding.setUsersViewModel(usersViewModel); binding.setView(this); }
  27. 27. RecyclerView Binding #3 public class UsersViewModel extends BaseObservable { @Bindable public ObservableArrayList<UserViewModel> users; public UsersViewModel() { this.users = new ObservableArrayList<>(); } public void addUser(String name, String surname) { this.users.add(new UserViewModel(new User(name, surname))); } }
  28. 28. RecyclerView Binding #4 public ItemBinder<UserViewModel> itemViewBinder() { return new CompositeItemBinder<UserViewModel>( new SuperUserBinder(BR.user, R.layout.item_super_user), new UserBinder(BR.user, R.layout.item_user) ); }
  29. 29. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  30. 30. How it works? Bind ViewModel to View Register for property change callbacks Trigger notifyPropertyChanged Request rebind Execute pending bindings User input or from code
  31. 31. How it works? public void setViewModel(TwoWayViewModel viewModel) { updateRegistration(0, viewModel); this.mViewModel = viewModel; synchronized(this) { mDirtyFlags |= 0x1L; notifyPropertyChanged(BR.viewModel); super.requestRebind(); }
  32. 32. How it works? @Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } if ((dirtyFlags & 0x7L) != 0) { if (viewModel != null) { colorViewModel = viewModel.getColor(); } if ((dirtyFlags & 0x7L) != 0) { ColorPickerViewBindings.setColor(this.colorpicker, colorViewModel); EditTextBindings.setText(this.mboundView2, colorViewModel); } if ((dirtyFlags & 0x4L) != 0) { ColorPickerViewBindings.setColorListener(this.colorpicker, null, colorpickercolorAttr); TextViewBindingAdapter.setTextWatcher(this.mboundView2, null, null, null, mboundView2androidTe); this.mboundView3.setOnClickListener(mCallback2); } }
  33. 33. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  34. 34. Lambdas <Button android:onClick="@{() -> viewModel.sendAction()} /> public class MainViewModel extends BaseObservable { public void sendAction() { //code } }
  35. 35. Method References <Button android:onClick="@{viewModel::sendAction}" /> public class MainViewModel extends BaseObservable { public void sendAction(View view) { //code } }
  36. 36. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  37. 37. Two-Way data binding <layout> ... <LinearLayout> <EditText android:text="@{viewModel.twoWayText}" /> </LinearLayout> </layout>
  38. 38. Two-Way data binding <layout> ... <LinearLayout> <EditText android:text="@={viewModel.twoWayText}" /> </LinearLayout> </layout>
  39. 39. Two-Way data binding classpath 'com.android.tools.build:gradle:2.1.0-alpha3'// or above
  40. 40. Views with Two-Way Binding support • AbsListView -> android:selectedItemPosition • CalendarView -> android:date • CompoundButton -> android:checked • DatePicker -> android:year, android:month, android:day • NumberPicker -> android:value • RadioGroup -> android:checkedButton • RatingBar -> android:rating • SeekBar -> android:progres • TabHost -> android:currentTab • TextView -> android:text • TimePicker -> android:hour, android:minute
  41. 41. What about custom views?
  42. 42. NO PROBLEM!
  43. 43. Two-Way data binding @InverseBindingAdapter(attribute = "color", event = "colorAttrChanged") public static int getColor(ColorPickerView view) { return view.getColor(); } attribute + AttrChanged
  44. 44. Activity @BindingAdapter("colorAttrChanged") public static void setColorListener(ColorPickerView view, final InverseBindingListener colorChange) { if (colorChange == null) { view.setOnColorChangedListener(null); } else { view.setOnColorChangedListener(new OnColorChangedListener() { @Override public void onColorChanged(int newColor) { colorChange.onChange(); } }); } }
  45. 45. Two-Way data binding @BindingAdapter("color") public static void setColor(ColorPickerView view, int color) { if (color != view.getColor()) { view.setColor(color); } } Avoiding cycles
  46. 46. Two-Way data binding public class TwoWayViewModel extends BaseObservable { private int color; public void setColor(int color) { this.color = color; this.notifyPropertyChanged(BR.color); } @Bindable public int getColor() { return this.color; } }
  47. 47. Two-Way data binding public class TwoWayBindingActivity extends AppCompatActivity { private ActivityTwoWayBinding binding; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_two_way); binding.setViewModel(new TwoWayViewModel()); } }
  48. 48. Two-Way data binding <LinearLayout> <com.github.danielnilsson9.colorpickerview.view.ColorPickerView bind:color="@={viewModel.color}" /> <EditText android:text="@={viewModel.color}" /> <Button android:text="Set defined color" android:onClick="@{() -> viewModel.setColor(Color.parseColor(`#C97249`))}" /> </LinearLayout>
  49. 49. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  50. 50. Special Variables <TextView android:id="@+id/tv_congrats" /> <Button android:onClick="@{() -> clickHandler.clicked(tvCongrats)}" /> <TextView android:text="@{TextService.load(context, myModel)}" /> Context View IDs
  51. 51. Implied Event Updates <CheckBox android:id="@+id/cb_show_more" /> <TextView android:visibility="@{cbShowMore.checked ? View.VISIBLE : View.GONE}" />
  52. 52. Repeated expressions <TextView android:id="@+id/tv_congrats" android:visibility="@{cbShowMore.checked ? View.VISIBLE : View.GONE}" /> <TextView android:visibility="@{cbShowMore.checked ? View.VISIBLE : View.GONE}" /> <TextView android:visibility="@{cbShowMore.checked ? View.VISIBLE : View.GONE}" />
  53. 53. Repeated expressions <TextView android:id="@+id/tv_congrats" android:visibility="@{cbShowMore.checked ? View.VISIBLE : View.GONE}" /> <TextView android:visibility="@{tvCongrats.visibility}" /> <TextView android:visibility="@{tvCongrats.visibility}" />
  54. 54. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  55. 55. Dos • From time to time look at generated code • Learn from already implemented bindings for framework views • Move view operations to custom bindings as much as possible • Try to use it together with MVVM design pattern • Give it a chance!
  56. 56. Dos • Use Data Binding for backward compatibility!
  57. 57. Dos • Always check if data binging library will work with your other libraries (squidb won’t work )
  58. 58. Don’ts • Don’t reinvent the wheel, on Github there are many ready to use bindings • Don’t forget about unit tests!
  59. 59. Don’ts <EditText android:layout_height="wrap_content" android:hint="@string/hint_ticketNumber" android:inputType="number" android:layout_width="fill_parent" android:text="@{viewModel.name == null? String.format(viewModel.format, viewModel.surname, viewModel.nick) : viewModel.name + viewModel.city}" /> Don’t move your business logic to xml layout Just don’t!
  60. 60. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  61. 61. Say hello to MVVM
  62. 62. MVVM & TDD @Mock ISendService sendService; @Test public void mainViewModel_sendAction_sendService_send() { final MainViewModel viewModel = new MainViewModel(sendService); final String givenText = "my text"; viewModel.setTwoWayText(givenText); viewModel.sendAction(); verify(sendService).send(givenText); }
  63. 63. Basics How it works? Lambdas Two-Way data binding New stuff announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  64. 64. Method count • ~700 methods from databinding library • n methods from your custom binding adapters, conversions etc. • k methods from generated code for each layout xml with data binding enabled
  65. 65. Summary Cons Compiler errors are sometimes not saying too much Still in beta Documentation is not updated May break other libraries (for example squidb) Pros Easy to start Less boilerplate code Code generation during compilation Easy to integrate with custom views and libraries Really powerful Officialy created and supported by Google Android Team
  66. 66. Q&A Q&A https://github.com/radzio/DeepDiveIntoAndroidDataBinding +RadoslawPiekarz @radzio Radosław Piekarz Head of Mobile at Tango Agency

×