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

More Decks by Kevin Pelgrims

Other Decks in Programming

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