Deep dive into Android Data Binding

Deep dive into Android Data Binding

Deep dive into Android Data Binding talk @ Droidcon Berlin 2016

C5f9cc43b0c9c49b5c97d58952b3e225?s=128

Radek Piekarz

June 16, 2016
Tweet

Transcript

  1. Deep dive into Android Data Binding +RadoslawPiekarz @radzio Radosław Piekarz

    Head of Mobile at Tango Agency
  2. droidconde2016 talixo.de 10 € discount

  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. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  5. Setup // <app-module>/build.gradle apply plugin: "com.android.application" android { dataBinding {

    enabled = true } }
  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. 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. 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. 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. 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. 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. Binding expression operators android:text="@{user.displayName ?? user.lastName}" android:text="@{user.displayName != null ?

    user.displayName : user.lastName}" =
  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. 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. 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. 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. 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. 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. Binding Component public class MyDataBindingCoponent implements DataBindingComponent { public EditTextBindings

    getEditTextBindings() { return new EditTextBindings(); } }
  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. Binding Conversions android:background="@{isError ? @color/red : @color/white}" @BindingConversion public static

    ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
  22. Binding methods @BindingMethods({ @BindingMethod(type = CircularProgressBar.class, attribute = "progressText", method

    = "setProgressMessage") }) public class ViewBindings { }
  23. RecyclerView bindings https://github.com/radzio/android-data-binding-recyclerview

  24. Talk is cheap. Show me the code

  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. 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. 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. 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. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  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. How it works? public void setViewModel(TwoWayViewModel viewModel) { updateRegistration(0, viewModel);

    this.mViewModel = viewModel; synchronized(this) { mDirtyFlags |= 0x1L; notifyPropertyChanged(BR.viewModel); super.requestRebind(); }
  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. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  34. Lambdas <Button android:onClick="@{() -> viewModel.sendAction()} /> public class MainViewModel extends

    BaseObservable { public void sendAction() { //code } }
  35. Method References <Button android:onClick="@{viewModel::sendAction}" /> public class MainViewModel extends BaseObservable

    { public void sendAction(View view) { //code } }
  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. Two-Way data binding <layout> ... <LinearLayout> <EditText android:text="@{viewModel.twoWayText}" /> </LinearLayout>

    </layout>
  38. Two-Way data binding <layout> ... <LinearLayout> <EditText android:text="@={viewModel.twoWayText}" /> </LinearLayout>

    </layout>
  39. Two-Way data binding classpath 'com.android.tools.build:gradle:2.1.0-alpha3'// or above

  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. What about custom views?

  42. NO PROBLEM!

  43. Two-Way data binding @InverseBindingAdapter(attribute = "color", event = "colorAttrChanged") public

    static int getColor(ColorPickerView view) { return view.getColor(); } attribute + AttrChanged
  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. Two-Way data binding @BindingAdapter("color") public static void setColor(ColorPickerView view, int

    color) { if (color != view.getColor()) { view.setColor(color); } } Avoiding cycles
  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. 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. 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. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  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. Implied Event Updates <CheckBox android:id="@+id/cb_show_more" /> <TextView android:visibility="@{cbShowMore.checked ? View.VISIBLE

    : View.GONE}" />
  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. 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. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  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. Dos • Use Data Binding for backward compatibility!

  57. Dos • Always check if data binging library will work

    with your other libraries (squidb won’t work )
  58. Don’ts • Don’t reinvent the wheel, on Github there are

    many ready to use bindings • Don’t forget about unit tests!
  59. None
  60. 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!
  61. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  62. Say hello to MVVM

  63. 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); }
  64. Basics How it works? Lambdas Two-Way data binding New stuff

    announced during Google IO 2016 Dos and don'ts MVVM & TDD Summary
  65. 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
  66. 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
  67. Q&A Q&A https://github.com/radzio/DeepDiveIntoAndroidDataBinding +RadoslawPiekarz @radzio Radosław Piekarz Head of Mobile

    at Tango Agency