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

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

      ContextCompat.getDrawable(this, R.drawable.ic_giants), null, null, null);
  2. 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() {} });
  3. 6.

    Problem with Android • Not all setters in Java have

    XML equivalent textView.setTypeface(); drawerLayout.setScrimColor();
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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>
  10. 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>
  11. 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>
  12. 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>
  13. 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>
  14. 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>
  15. 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>
  16. 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>
  17. 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
  18. 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}" />
  19. 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}" />
  20. 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}" />
  21. 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)}" />
  22. 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)}" />
  23. 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)}" />
  24. 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}" />
  25. 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}" />
  26. 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" />
  27. 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" />
  28. 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}" />
  29. 53.
  30. 54.

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

    After: XML <BottomNavigationView app:onNavigationItemSelectedListener="@{handler::onNavigationClick}" />
  31. 55.

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

    After: XML <BottomNavigationView app:onNavigationItemSelectedListener="@{handler::onNavigationClick}" />
  32. 58.
  33. 59.
  34. 60.
  35. 61.
  36. 62.
  37. 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);
  38. 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);
  39. 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);
  40. 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);
  41. 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
  42. 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
  43. 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
  44. 72.

    ViewModel public class MatchViewModel { private Match match; public Match

    getMatch() { return match; } public void setMatch(Match match) { this.match = match; } }
  45. 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); } }
  46. 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); } }
  47. 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); } }
  48. 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); } }
  49. 77.

    Two way Binding: Model ↔ View ModelView changes ⇒ UI

    updates ModelView updates ⇐ UI changes
  50. 78.

    Two way Binding: Model ↔ View ModelView changes ⇒ UI

    updates ModelView updates ⇐ UI changes
  51. 82.

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

    generated Java code • Bitwise flags to make views dirty
  52. 83.

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

    generated Java code • Bitwise flags to make views dirty
  53. 84.

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

    generated Java code • Bitwise flags to make views dirty
  54. 85.

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

    generated Java code • Bitwise flags to make views dirty
  55. 86.

    Data Binding Library: Generated Code • from layout/match.xml • MatchBinding.java

    • matchBinding.score ◦ MatchBinding.date ◦ MatchBinding.team ◦ MatchBinding.showScore ◦ MatchBinding.bottomNavigationView
  56. 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)
  57. 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)
  58. 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); }
  59. 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); }
  60. 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)}"
  61. 94.

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

    != null) { homeTeamNameView.setText(match.getHomeTeam().getFullname()); }
  62. 96.

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

    != null) { homeTeamNameView.setText(match.getHomeTeam().getFullname()); } android:text="@{match.team.fullname}"
  63. 98.

    Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  64. 99.

    Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  65. 100.

    Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  66. 101.

    Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  67. 102.

    Android Studio • Gradle integration • Syntax highlighting • Code

    completion • Can view/debug generated code
  68. 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>
  69. 105.

    Android Studio Tips • Use tools: for preview <TextView tools:text="甲子園"

    /> <ImageView tools:src="@drawable/image" /> <View tools:visibility="visible" />
  70. 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...
  71. 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
  72. 109.
  73. 110.
  74. 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