Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

The Data Binding Library

The Data Binding Library

Presentation of the Data Binding Library by Benoît Quenaudon

Demo repo: https://github.com/oldergod/DataBindingDemo
Video: https://www.youtube.com/watch?v=411jp1ceiH8

Benoît Quenaudon

November 27, 2016
Tweet

More Decks by Benoît Quenaudon

Other Decks in Programming

Transcript

  1. Problem with Android • Setters are complicated and verbose binding.date.setCompoundDrawablesWithIntrinsicBounds(

      ContextCompat.getDrawable(this, R.drawable.ic_giants), null, null, null);
  2. Problem with Android • Manually keep track of UI updates

    textView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged() {} @Override public void onTextChanged() {} @Override public void afterTextChanged() {} });
  3. Problem with Android • Not all setters in Java have

    XML equivalent textView.setTypeface(); drawerLayout.setScrimColor();
  4. Data Binding Library ? • A framework to connect your

    UI and model ◦ Once or persistently • One XML attribute for every Java setter • Custom XML attributes • Can override android:’s XML attributes
  5. Data Binding Library ? • A framework to connect your

    UI and model ◦ Once or persistently • One XML attribute for every Java setter • Custom XML attributes • Can override android:’s XML attributes
  6. Data Binding Library ? • A framework to connect your

    UI and model ◦ Once or persistently • One XML attribute for every Java setter • Custom XML attributes • Can override android:’s XML attributes
  7. Data Binding Library ? • A framework to connect your

    UI and model ◦ Once or persistently • One XML attribute for every Java setter • Custom XML attributes • Can override android:’s XML attributes
  8. Data Binding Library ? • A framework to connect your

    UI and model ◦ Once or persistently • One XML attribute for every Java setter • Custom XML attributes • Can override android:’s XML attributes
  9. match.xml <android.support.constraint.ConstraintLayout> <TextView android:id="@+id/date" /> <TextView android:id="@+id/stadium" /> <include android:id="@+id/homeTeam"

    layout="@layout/team" /> <include android:id="@+id/awayTeam" layout="@layout/team" /> <CheckBox android:id="@+id/showScore" /> <TextView android:id="@+id/score" /> <android.support.design.widget.BottomNavigationView /> </android.support.constraint.ConstraintLayout>
  10. match.xml <android.support.constraint.ConstraintLayout> <TextView android:id="@+id/date" /> <TextView android:id="@+id/stadium" /> <include android:id="@+id/homeTeam"

    layout="@layout/team" /> <include android:id="@+id/awayTeam" layout="@layout/team" /> <CheckBox android:id="@+id/showScore" /> <TextView android:id="@+id/score" /> <android.support.design.widget.BottomNavigationView /> </android.support.constraint.ConstraintLayout>
  11. match.xml <android.support.constraint.ConstraintLayout> <TextView android:id="@+id/date" /> <TextView android:id="@+id/stadium" /> <include android:id="@+id/homeTeam"

    layout="@layout/team" /> <include android:id="@+id/awayTeam" layout="@layout/team" /> <CheckBox android:id="@+id/showScore" /> <TextView android:id="@+id/score" /> <android.support.design.widget.BottomNavigationView /> </android.support.constraint.ConstraintLayout>
  12. match.xml <android.support.constraint.ConstraintLayout> <TextView android:id="@+id/date" /> <TextView android:id="@+id/stadium" /> <include android:id="@+id/homeTeam"

    layout="@layout/team" /> <include android:id="@+id/awayTeam" layout="@layout/team" /> <CheckBox android:id="@+id/showScore" /> <TextView android:id="@+id/score" /> <android.support.design.widget.BottomNavigationView /> </android.support.constraint.ConstraintLayout>
  13. match.xml <android.support.constraint.ConstraintLayout> <TextView android:id="@+id/date" /> <TextView android:id="@+id/stadium" /> <include android:id="@+id/homeTeam"

    layout="@layout/team" /> <include android:id="@+id/awayTeam" layout="@layout/team" /> <CheckBox android:id="@+id/showScore" /> <TextView android:id="@+id/score" /> <android.support.design.widget.BottomNavigationView /> </android.support.constraint.ConstraintLayout>
  14. match.xml <android.support.constraint.ConstraintLayout> <TextView android:id="@+id/date" /> <TextView android:id="@+id/stadium" /> <include android:id="@+id/homeTeam"

    layout="@layout/team" /> <include android:id="@+id/awayTeam" layout="@layout/team" /> <CheckBox android:id="@+id/showScore" /> <TextView android:id="@+id/score" /> <android.support.design.widget.BottomNavigationView /> </android.support.constraint.ConstraintLayout>
  15. Before: XML <?xml version="1.0" encoding="utf-8"?> <LinearLayout> <!----> </LinearLayout> After: XML

    <?xml version="1.0" encoding="utf-8"?> <layout> <LinearLayout> <!----> </LinearLayout> </layout>
  16. Before: XML <?xml version="1.0" encoding="utf-8"?> <LinearLayout> <!----> </LinearLayout> After: XML

    <?xml version="1.0" encoding="utf-8"?> <layout> <LinearLayout> <!----> </LinearLayout> </layout>
  17. Before setContentView(R.layout.activity_main); activityList = (ConstraintLayout) findViewById(R.id.activity_list); dateView = (TextView) findViewById(R.id.date);

    stadiumView = (TextView) findViewById(R.id.stadium); showScoreView = (CheckBox) findViewById(R.id.showScore); scoreView = (TextView) findViewById(R.id.score); bottomNavigationView = (BottomNavigationView)   findViewById(R.id.bottom_navigation); View homeTeam = findViewById(R.id.homeTeam); homeTeamIconView = (ImageView) homeTeam.findViewById(R.id.teamicon); homeTeamNameView = (TextView) homeTeam.findViewById(R.id.teamName); View awayTeam = findViewById(R.id.awayTeam); M O R E
  18. Before: Java if (match.getStadium() != null) { stadiumView.setText(match.getStadium()); } else

    { stadiumView.setText(R.string.unknown_stadium); } After: XML <TextView android:text="@{matchVM.match.stadium ?? @string/unknown_stadium}" />
  19. Before: Java if (match.getStadium() != null) { stadiumView.setText(match.getStadium()); } else

    { stadiumView.setText(R.string.unknown_stadium); } After: XML <TextView android:text="@{matchVM.match.stadium ?? @string/unknown_stadium}" />
  20. Before: Java if (match.getStadium() != null) { stadiumView.setText(match.getStadium()); } else

    { stadiumView.setText(R.string.unknown_stadium); } After: XML <TextView android:text="@{matchVM.match.stadium ?? @string/unknown_stadium}" />
  21. Before: Java <string name="year_date">%1$d年%2$d月%3$d日</string> dateView.setText( getString( R.string.year_date, match.getYear(), match.getMonth(), match.getDay()

    ) ); After: XML <TextView android:text="@{@string/year_date(matchVM.match.year, matchVM.match.month, matchVM.match.day)}" />
  22. Before: Java <string name="year_date">%1$d年%2$d月%3$d日</string> dateView.setText( getString( R.string.year_date, match.getYear(), match.getMonth(), match.getDay()

    ) ); After: XML <TextView android:text="@{@string/year_date(matchVM.match.year, matchVM.match.month, matchVM.match.day)}" />
  23. Before: Java <string name="year_date">%1$d年%2$d月%3$d日</string> dateView.setText( getString( R.string.year_date, match.getYear(), match.getMonth(), match.getDay()

    ) ); After: XML <TextView android:text="@{@string/year_date(matchVM.match.year, matchVM.match.month, matchVM.match.day)}" />
  24. Before: Java scoreView.setVisibility(match.isShowScore() ? VISIBLE : GONE); @Override public void

    onCheckedChanged(CompoundButton compoundButton, boolean b) { scoreView.setVisibility(b ? VISIBLE : GONE); } After: XML <TextView android:visibility="@{matchVM.match.showScore ? View.VISIBLE : View.GONE}" />
  25. Before: Java scoreView.setVisibility(match.isShowScore() ? VISIBLE : GONE); @Override public void

    onCheckedChanged(CompoundButton compoundButton, boolean b) { scoreView.setVisibility(b ? VISIBLE : GONE); } After: XML <CheckBox android:id="@+id/showScore" /> <TextView android:visibility="@{matchVM.match.showScore ? View.VISIBLE : View.GONE}" />
  26. Before: Java scoreView.setVisibility(match.isShowScore() ? VISIBLE : GONE); @Override public void

    onCheckedChanged(CompoundButton compoundButton, boolean b) { scoreView.setVisibility(b ? VISIBLE : GONE); } After: XML <TextView android:visibility="@{showScore.checked ? View.VISIBLE : View.GONE}" /> <CheckBox android:id="@+id/showScore" />
  27. Before: Java scoreView.setVisibility(match.isShowScore() ? VISIBLE : GONE); @Override public void

    onCheckedChanged(CompoundButton compoundButton, boolean b) { scoreView.setVisibility(b ? VISIBLE : GONE); } After: XML <TextView android:visibility="@{showScore.checked ? View.VISIBLE : View.GONE}" /> <CheckBox android:id="@+id/showScore" />
  28. Before: Java scoreView.setVisibility(match.isShowScore() ? VISIBLE : GONE); @Override public void

    onCheckedChanged(CompoundButton compoundButton, boolean b) { scoreView.setVisibility(b ? VISIBLE : GONE); } After: XML <TextView android:visibility="@{showScore.checked ? View.VISIBLE : View.GONE}" />
  29. Before: Java bottomNavigationView.setOnNavigationItemSelectedListener(this); @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) {}

    After: XML <BottomNavigationView app:onNavigationItemSelectedListener="@{handler::onNavigationClick}" />
  30. Before: Java bottomNavigationView.setOnNavigationItemSelectedListener(this); @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) {}

    After: XML <BottomNavigationView app:onNavigationItemSelectedListener="@{handler::onNavigationClick}" />
  31. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout> After: Java ActivityMainBinding binding =   DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setMatchVM(matchViewModel); binding.setHandler(this);
  32. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout> After: Java ActivityMainBinding binding =   DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setMatchVM(matchViewModel); binding.setHandler(this);
  33. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout> After: Java ActivityMainBinding binding =   DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setMatchVM(matchViewModel); binding.setHandler(this);
  34. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout> After: Java ActivityMainBinding binding =   DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setMatchVM(matchViewModel); binding.setHandler(this);
  35. After: XML <layout> <data> <variable name="team" type="class.path.Team" /> </data> ...

    </layout> team.xml <layout> … <include layout="@layout/team" app:team="@{matchVM.match.homeTeam}" /> </layout> match.xml
  36. After: XML <layout> <data> <variable name="team" type="class.path.Team" /> </data> ...

    </layout> team.xml <layout> … <include layout="@layout/team" app:team="@{matchVM.match.homeTeam}" /> </layout> match.xml
  37. After: XML <layout> <data> <variable name="team" type="class.path.Team" /> </data> ...

    </layout> team.xml <layout> … <include layout="@layout/team" app:team="@{matchVM.match.homeTeam}" /> </layout> match.xml
  38. ViewModel public class MatchViewModel { private Match match; public Match

    getMatch() { return match; } public void setMatch(Match match) { this.match = match; } }
  39. ViewModel public class MatchViewModel extends BaseObservable { private Match match;

    @Bindable public Match getMatch() { return match; } public void setMatch(Match match) { this.match = match; notifyPropertyChanged(BR.match); } }
  40. ViewModel public class MatchViewModel extends BaseObservable { private Match match;

    @Bindable public Match getMatch() { return match; } public void setMatch(Match match) { this.match = match; notifyPropertyChanged(BR.match); } }
  41. ViewModel public class MatchViewModel extends BaseObservable { private Match match;

    @Bindable public Match getMatch() { return match; } public void setMatch(Match match) { this.match = match; notifyPropertyChanged(BR.match); } }
  42. ViewModel public class MatchViewModel extends BaseObservable { private Match match;

    @Bindable public Match getMatch() { return match; } public void setMatch(Match match) { this.match = match; notifyPropertyChanged(BR.match); } }
  43. Two way Binding: Model ↔ View ModelView changes ⇒ UI

    updates ModelView updates ⇐ UI changes
  44. Two way Binding: Model ↔ View ModelView changes ⇒ UI

    updates ModelView updates ⇐ UI changes
  45. Data Binding Library: How ? • Zero reflection • 100%

    generated Java code • Bitwise flags to make views dirty
  46. Data Binding Library: How ? • Zero reflection • 100%

    generated Java code • Bitwise flags to make views dirty
  47. Data Binding Library: How ? • Zero reflection • 100%

    generated Java code • Bitwise flags to make views dirty
  48. Data Binding Library: How ? • Zero reflection • 100%

    generated Java code • Bitwise flags to make views dirty
  49. Data Binding Library: Generated Code • from layout/match.xml • MatchBinding.java

    • matchBinding.score ◦ MatchBinding.date ◦ MatchBinding.team ◦ MatchBinding.showScore ◦ MatchBinding.bottomNavigationView
  50. Attributes for every java setter class Team { private int

    drawableId; public int getDrawableId() {} } <ImageView app:imageResource="@{team.drawableId}" /> class ImageView > void setImageResource (int resId)
  51. Attributes for every java setter class Team { private int

    drawableId; public int getDrawableId() {} } <ImageView app:imageResource="@{team.drawableId}" /> class ImageView > void setImageResource (int resId)
  52. Custom XML Attributes <ImageView app:imageUrl="@{team.imageUrl}" /> @BindingAdapter("imageUrl") public static void

    loadImage(ImageView view, String url) { Picasso.with(view.getContext()) .load(url) .into(view); }
  53. Custom XML Attributes <ImageView app:imageUrl="@{team.imageUrl}" /> @BindingAdapter("imageUrl") public static void

    loadImage(ImageView view, String url) { Picasso.with(view.getContext()) .load(url) .into(view); }
  54. Java Code Evaluation android:visibility="@{showScore.checked ? View.VISIBLE : View.GONE}" android:padding="@{@dimen/activity_vertical_margin /

    2}" android:drawableLeft="@{isCat ? @drawable.cat : @drawable.dog}" android:onClick="@{() -> presenter.onSaveClick(task)}"
  55. Null Pointer Exception さようなら if (match != null && match.getHomeTeam()

    != null) { homeTeamNameView.setText(match.getHomeTeam().getFullname()); }
  56. Null Pointer Exception さようなら if (match != null && match.getHomeTeam()

    != null) { homeTeamNameView.setText(match.getHomeTeam().getFullname()); } android:text="@{match.team.fullname}"
  57. Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  58. Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  59. Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  60. Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  61. Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  62. Android Studio Tips • Put namespaces in <layout> <layout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <LinearLayout> <!----> </LinearLayout> </layout>
  63. Android Studio Tips • Use tools: for preview <TextView tools:text="甲子園"

    /> <ImageView tools:src="@drawable/image" /> <View tools:visibility="visible" />
  64. Easy to start • Can be applied to a unique

    part of your app • Don’t have to use every feature ◦ Only view binding, ◦ One way data binding, ◦ Custom XML attributes...
  65. Want some code? Demo App • https://github.com/oldergod/DataBindingDemo ◦ 0-no-databinding ◦

    1-minimal-setup ◦ 2-viewbinding ◦ 3-oneway-data-binding ◦ 4-view-binding-expression ◦ 5-binding-adapters ◦ 6-persistent-data-binding ◦ 7-event-listeners ◦ 8-twoway-data-binding
  66. References • DataBinding Demo App ◦ https://github.com/oldergod/DataBindingDemo • Data Bindling

    Library ◦ https://developer.android.com/topic/libraries/data-binding/index.html • talk:title=”@{databinding}” ◦ https://www.youtube.com/watch?v=zYGVsTE_scI • 057: Data Binding with GDE Lisa Wray ◦ http://fragmentedpodcast.com/episodes/057/ • George Mount’s blog posts ◦ https://medium.com/@georgemount007