Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Data binding on Android

Data binding on Android

872f26792da7176d69d5add3cf0b7e25?s=128

Kevin Pelgrims

March 09, 2017
Tweet

Transcript

  1. Data binding on Android How to use it in the

    real world
  2. Agenda • What, why, how? • Two-way binding • Custom

    attributes • Event handlers • Architecture • Tips and tricks
  3. What, why, how?

  4. What is data binding? • Connect data sources with views

    • Automatic updates (requires some extra effort) • Common approach in other frameworks • WPF & Windows Phone (C#) • Angular (JavaScript)
  5. Why use data binding? • Cleaner code • Simple expressions

    in views • Powerful custom attributes
  6. Getting started – Enable data binding android { ... dataBinding

    { enabled = true } ... }
  7. Getting started – Layout setup <?xml version="1.0" encoding="utf-8"?> <layout xmlns="...">

    <data> <variable name="user" type="com.databinding.User"/> </data> <TextView android:text="@{user.firstName}"/> </layout>
  8. Getting started – Activity setup @Override protected void onCreate(Bundle savedInstanceState)

    { super.onCreate(savedInstanceState); DataBindingUtil .setContentView(this, R.layout.activity_main); }
  9. Getting started – Activity setup @Override protected void onCreate(Bundle savedInstanceState)

    { super.onCreate(savedInstanceState); DataBindingUtil .setContentView(this, R.layout.activity_main); } ActivityMainBinding.java
  10. Getting started – Activity setup @Override protected void onCreate(Bundle savedInstanceState)

    { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil .setContentView(this, R.layout.activity_main); User user = new User("George"); binding.setUser(user); }
  11. How does it work? • Process layout files • Parse

    expressions • Generates Java code • Traverses the view hierarchy once to find all views • Keeps a reference to the views
  12. Usage • Use it to replace findViewById or Butter Knife

    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil .setContentView(this, R.layout.activity_main); binding.userName.setText(user.getName()); }
  13. Usage • Transforming text android:text="@{String.valueOf(index + 1)}" • Setting views

    visible based on certain conditions android:visibility="@{user.age < 18 ? View.GONE : View.VISIBLE}" ⚠
  14. Usage <ImageView app:imageUrl="@{viewModel.user.pictureUrl}" /> <EditText android:text="@{viewModel.user.name}"/> <EditText android:text="@{viewModel.user.email}"/> <EditText android:text="@{viewModel.user.location}"

    />
  15. Dealing with updates – Option 1 public class User {

    public final ObservableField<String> firstName = new ObservableField<>(); } user.firstName.set("Louise")
  16. Dealing with updates – Option 2 public class User extends

    BaseObservable { private String lastName; @Bindable public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; notifyPropertyChanged(BR.lastName); }
  17. Dealing with updates - Internals private long mDirtyFlags = 0xffffffffffffffffL;

    /* flag mapping flag 0 (0x1L): user.firstName flag 1 (0x2L): user flag 2 (0x3L): user.lastName flag 3 (0x4L): null flag mapping end*/
  18. Dealing with updates - Internals public void setUser(User user) {

    updateRegistration(1, user); this.mUser = user; synchronized(this) { mDirtyFlags |= 0x2L; } notifyPropertyChanged(BR.user); super.requestRebind(); }
  19. Dealing with updates - Internals protected void executeBindings() { if

    ((dirtyFlags & 0xeL) != 0) { if (user != null) { // read user.lastName lastNameUser = user.getLastName(); } } }
  20. Two-way binding how to handle user input

  21. Two-way binding <EditText android:text="@{user.firstName}" … /> <EditText android:text="@={user.firstName}" … />

  22. Custom attributes aka binding adapters

  23. Custom attributes – image loading @BindingAdapter({"app:imageUrl", "app:error"}) public static void

    loadImage(ImageView view, String url, Drawable error { Glide.with(view.getContext()) .load(url) .error(error) .into(view); }
  24. Custom attributes – image loading @BindingAdapter({"app:imageUrl", "app:error"}) public static void

    loadImage(ImageView view, String url, Drawable error { Glide.with(view.getContext()) .load(url) .error(error) .into(view); }
  25. Custom attributes – image loading @BindingAdapter({"app:imageUrl", "app:error"}) public static void

    loadImage(ImageView view, String url, Drawable error { Glide.with(view.getContext()) .load(url) .error(error) .into(view); }
  26. Custom attributes – font @BindingAdapter({"app:font"}) public static void setFont(TextView textView,

    String fontName) { textView .setTypeface(FontCache.getInstance(context) .get(fontName)); } <TextView app:font="@{'alegreya'}" /> https://github.com/lisawray/fontbinding
  27. Custom attributes – animations @BindingAdapter({"app:animatedVisibility"}) public static void setVisibility(View view,

    int visibility) { … ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, startAlpha, endAlpha); … alpha.start(); } Full example: https://medium.com/google-developers/android-data-binding-animations-55f6b5956a64
  28. Event handlers

  29. Event handlers public class UserViewModel { … public View.OnClickListener clickListener;

    … } <View android:onClick="@{viewModel.clickListener}" … />
  30. Event handlers public class UserViewModel { … public void clickListener(View

    view) {…} … } <View android:onClick="@{viewModel.clickListener}" … />
  31. Event handlers textChangedListener = new TextWatcher() { public void beforeTextChanged

    … public void onTextChanged … public void afterTextChanged … } <EditText android:addTextChangedListener= "@{viewModel.textChangedListener}"/>
  32. Event handlers <EditText android:addTextChangedListener= "@{viewModel.textChangedListener}" … /> <EditText android:afterTextChanged= "@{viewModel.afterTextChanged}"

    … />
  33. Event handlers with parameters public class UserViewModel { public void

    save(User user) { userRepository.save(user); } } <Button android:onClick= "@{()->userViewModel.save(user)}"/>
  34. Architecture

  35. MVVM View ViewModel Model

  36. Data binding MVVM View ViewModel Model getTasks() callback

  37. Tips & tricks

  38. RecyclerView public class ViewHolder extends RecyclerView.ViewHolder { private final ListItemBinding

    listItemBinding; public ViewHolder(ListItemBinding binding) { super(binding.getRoot()); this.listItemBinding = binding; } … }
  39. RecyclerView public class ViewHolder extends RecyclerView.ViewHolder { private final ListItemBinding

    listItemBinding; … public void bind(User user) { listItemBinding.setUser(user); listItemBinding.executePendingBindings(); } }
  40. RecyclerView public class UserAdapter extends RecyclerView.Adapter { private List<User> users;

    … @Override public void onBindViewHolder(ViewHolder holder, int position) { User user = users.get(position); holder.bind(user); } }
  41. RecyclerView public class MainActivity extends … { @Override protected void

    onCreate() { ActivityMainBinding binding = DataBindingUtil .setContentView(this, R.layout.activity_main); binding.users.setLayoutManager( new LinearLayoutManager(this)); binding.users.setAdapter(new UserAdapter(users)); } … }
  42. RecyclerView advanced @BindingAdapter("items") public static <T> void setItems(RecyclerView recyclerView, Collection<T>

    items) { BindingRecyclerViewAdapter<T> adapter = (BindingRecyclerViewAdapter<T>) recyclerView.getAdapter(); adapter.setItems(items); }
  43. RecyclerView advanced <android.support.v7.widget.RecyclerView … app:items="@{usersVM.users}" app:itemViewBinder="@{usersVM.itemViewBinder}" app:clickHandler="@{usersVM.click}" app:longClickHandler="@{usersVM.longClick}" /> https://github.com/radzio/android-data-binding-recyclerview

  44. String formatting <TextView android:text= "@{@string/greeting(user.firstName)}" /> <resources> <string name="greeting">Hello, %s</string>

    </resources>
  45. Math in expressions <TextView android:padding="@dimen/padding" android:padding="@{@dimen/padding}" android:padding="@{@dimen/padding * 2}" android:padding="@{@dimen/padding

    + @dimen/padding}" android:padding="@{largeScreen ? @dimen/padding * 2 : @dimen/padding}" />
  46. Overriding Android attributes @BindingAdapter("android:layout_margin") public static void setMargin(View view, float

    margin) { MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams(); lp.setMargins(margin, margin, margin, margin); view.setLayoutParams(layoutParams); }
  47. Referencing other views <CheckBox android:id="@+id/showName"/> <TextView android:visibility="@{showName.isChecked ? View.VISIBLE :

    View.GONE}" android:text="@{user.name}"/>
  48. Binding conversions @BindingConversion public static int convertBooleanToVisibility(boolean visible) { return

    visible ? View.VISIBLE : View.GONE; } <TextView … android:visibility="@{user.isAdult}"/>
  49. Resources • Official documentation https://developer.android.com/topic/libraries/data-binding/index.html • George Mount https://medium.com/@georgemount007 •

    Advanced data binding (Google IO 2016) https://www.youtube.com/watch?v=DAmMN7m3wLU
  50. Data binding on Android twitter.com/kevinpelgrims kevinpelgrims.com