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

Data binding on Android

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Data binding on Android

Avatar for Kevin Pelgrims

Kevin Pelgrims

March 09, 2017
Tweet

More Decks by Kevin Pelgrims

Other Decks in Programming

Transcript

  1. Agenda • What, why, how? • Two-way binding • Custom

    attributes • Event handlers • Architecture • Tips and tricks
  2. 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)
  3. Why use data binding? • Cleaner code • Simple expressions

    in views • Powerful custom attributes
  4. 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>
  5. Getting started – Activity setup @Override protected void onCreate(Bundle savedInstanceState)

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

    { super.onCreate(savedInstanceState); DataBindingUtil .setContentView(this, R.layout.activity_main); } ActivityMainBinding.java
  7. 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); }
  8. 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
  9. 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()); }
  10. 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}" ⚠
  11. Dealing with updates – Option 1 public class User {

    public final ObservableField<String> firstName = new ObservableField<>(); } user.firstName.set("Louise")
  12. 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); }
  13. 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*/
  14. Dealing with updates - Internals public void setUser(User user) {

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

    ((dirtyFlags & 0xeL) != 0) { if (user != null) { // read user.lastName lastNameUser = user.getLastName(); } } }
  16. 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); }
  17. 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); }
  18. 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); }
  19. 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
  20. 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
  21. Event handlers public class UserViewModel { … public View.OnClickListener clickListener;

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

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

    … public void onTextChanged … public void afterTextChanged … } <EditText android:addTextChangedListener= "@{viewModel.textChangedListener}"/>
  24. Event handlers with parameters public class UserViewModel { public void

    save(User user) { userRepository.save(user); } } <Button android:onClick= "@{()->userViewModel.save(user)}"/>
  25. RecyclerView public class ViewHolder extends RecyclerView.ViewHolder { private final ListItemBinding

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

    listItemBinding; … public void bind(User user) { listItemBinding.setUser(user); listItemBinding.executePendingBindings(); } }
  27. 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); } }
  28. 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)); } … }
  29. RecyclerView advanced @BindingAdapter("items") public static <T> void setItems(RecyclerView recyclerView, Collection<T>

    items) { BindingRecyclerViewAdapter<T> adapter = (BindingRecyclerViewAdapter<T>) recyclerView.getAdapter(); adapter.setItems(items); }
  30. 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); }
  31. Binding conversions @BindingConversion public static int convertBooleanToVisibility(boolean visible) { return

    visible ? View.VISIBLE : View.GONE; } <TextView … android:visibility="@{user.isAdult}"/>