Pro Yearly is on sale from $80 to $50! »

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

05162bc961c3654218bf1839974a4f35?s=128

Benoît Quenaudon

November 27, 2016
Tweet

Transcript

  1. The Data Binding Library Benoît Quenaudon @oldergod Slides: https://goo.gl/Pr4kjj

  2. Problem with Android

  3. Problem with Android • findViewById, Cast textView = (TextView) findViewById(R.id.text_view);

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

      ContextCompat.getDrawable(this, R.drawable.ic_giants), null, null, null);
  5. 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() {} });
  6. Problem with Android • Not all setters in Java have

    XML equivalent textView.setTypeface(); drawerLayout.setScrimColor();
  7. Data Binding to the rescue

  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. 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
  10. 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
  11. 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
  12. 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
  13. Before / After github.com/oldergod/DataBindingDemo

  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. 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>
  16. 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>
  17. 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>
  18. 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>
  19. 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>
  20. team.xml <android.support.constraint.ConstraintLayout> <ImageView android:id="@+id/teamicon" /> <TextView android:id="@+id/teamName" /> </android.support.constraint.ConstraintLayout>

  21. <android.support.constraint.ConstraintLayout> <ImageView android:id="@+id/teamicon" /> <TextView android:id="@+id/teamName" /> </android.support.constraint.ConstraintLayout> team.xml

  22. <android.support.constraint.ConstraintLayout> <ImageView android:id="@+id/teamicon" /> <TextView android:id="@+id/teamName" /> </android.support.constraint.ConstraintLayout> team.xml

  23. team.xml <android.support.constraint.ConstraintLayout> <ImageView android:id="@+id/teamicon" /> <TextView android:id="@+id/teamName" /> </android.support.constraint.ConstraintLayout>

  24. Setup Data Binding

  25. After: build.gradle android { dataBinding { enabled true } }

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

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

    <?xml version="1.0" encoding="utf-8"?> <layout> <LinearLayout> <!----> </LinearLayout> </layout>
  28. Before: Java setContentView(R.layout.activity_main); After: Java DataBindingUtil.setContentView(this, R.layout.activity_main);

  29. Data Binding is ON

  30. Now, what can I do?

  31. View Binding

  32. 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
  33. After ActivityMainBinding binding =   DataBindingUtil.setContentView(this, R.layout.activity_main);

  34. After ActivityMainBinding binding =   DataBindingUtil.setContentView(this, R.layout.activity_main);

  35. One way data binding Model → View

  36. Before: Java Match match = matchViewModel.getMatch(); scoreView.setText(match.getScore()); After: XML <TextView

    android:text="@{matchVM.match.score}" />
  37. Before: Java Match match = matchViewModel.getMatch(); scoreView.setText(match.getScore()); After: XML <TextView

    android:text="@{matchVM.match.score}" />
  38. Before: Java Match match = matchViewModel.getMatch(); scoreView.setText(match.getScore()); After: XML <TextView

    android:text="@{matchVM.match.score}" />
  39. 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}" />
  40. 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}" />
  41. 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}" />
  42. 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)}" />
  43. 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)}" />
  44. 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)}" />
  45. Before: Java showScoreView.setChecked(match.isShowScore()); After: XML <CheckBox android:checked="@{matchVM.match.showScore}" />

  46. 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}" />
  47. 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}" />
  48. 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" />
  49. 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" />
  50. 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}" />
  51. Before: Java homeTeamIconView.setImageResource(match.getHomeTeam().getDrawableId()); After: XML <ImageView app:imageResource="@{match.homeTeam.drawableId}" />

  52. Before: Java homeTeamIconView.setImageResource(match.getHomeTeam().getDrawableId()); After: XML <ImageView app:imageResource="@{match.homeTeam.drawableId}" />

  53. Callbacks

  54. Before: Java bottomNavigationView.setOnNavigationItemSelectedListener(this); @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) {}

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

    After: XML <BottomNavigationView app:onNavigationItemSelectedListener="@{handler::onNavigationClick}" />
  56. Before: Java showScoreView.setOnCheckedChangeListener(this); @Override public void onCheckChanged(CompoundButton compoundButton, boolean b)

    {} After: XML <CheckBox app:onCheckChangeListener="@{handler::onCheckChanged}" />
  57. Model - UI Connection Setup

  58. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout>
  59. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout>
  60. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout>
  61. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout>
  62. After: XML <layout> <data> <variable name="matchVM" type="class.path.MatchViewModel" /> <variable name="handler"

    type="class.path.ListActivity" /> </data> <!--old layout--> </layout>
  63. 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);
  64. 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);
  65. 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);
  66. 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);
  67. <include />

  68. 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
  69. 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
  70. 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
  71. Persistent Binding ModelView changes ⇒ UI updates

  72. ViewModel public class MatchViewModel { private Match match; public Match

    getMatch() { return match; } public void setMatch(Match match) { this.match = match; } }
  73. 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); } }
  74. 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); } }
  75. 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); } }
  76. 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); } }
  77. Two way Binding: Model ↔ View ModelView changes ⇒ UI

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

    updates ModelView updates ⇐ UI changes
  79. Before <EditText android:text="@{team.fullname}" /> <CheckBox android:checked="@{match.showScore}" />

  80. After <EditText android:text="@={team.fullname}" /> <CheckBox android:checked="@={match.showScore}" />

  81. After <EditText android:text="@={team.fullname}" /> <CheckBox android:checked="@={match.showScore}" />

  82. Data Binding Library: How ? • Zero reflection • 100%

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

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

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

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

    • matchBinding.score ◦ MatchBinding.date ◦ MatchBinding.team ◦ MatchBinding.showScore ◦ MatchBinding.bottomNavigationView
  87. Data Binding in detail

  88. <TextView android:text="@{matchVM.match.stadium}" /> <TextView android:text="@{matchVM.getMatch().getStadium()}" /> Readibility obj.getAttr() obj.isAttr() obj.hasAttr()

    ⇒ obj.attr
  89. Attributes for every java setter class Team { private int

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

    drawableId; public int getDrawableId() {} } <ImageView app:imageResource="@{team.drawableId}" /> class ImageView > void setImageResource (int resId)
  91. 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); }
  92. 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); }
  93. 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)}"
  94. Null Pointer Exception さようなら if (match != null && match.getHomeTeam()

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

    }
  96. Null Pointer Exception さようなら if (match != null && match.getHomeTeam()

    != null) { homeTeamNameView.setText(match.getHomeTeam().getFullname()); } android:text="@{match.team.fullname}"
  97. Data Binding と Android Studio

  98. Android Studio • Gradle integration • Syntax highlighting • Code

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

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

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

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

    completion • Can view/debug generated code
  103. Android Studio Tips

  104. 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>
  105. Android Studio Tips • Use tools: for preview <TextView tools:text="甲子園"

    /> <ImageView tools:src="@drawable/image" /> <View tools:visibility="visible" />
  106. 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...
  107. Data Binding For The Win

  108. 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
  109. None
  110. None
  111. Fin Slides: https://goo.gl/Pr4kjj Benoît Quenaudon @oldergod

  112. 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