33
Speed Up Application Development with Data Binding Anokhin Mikle / Android Developer / MWDN Ltd.

SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Embed Size (px)

Citation preview

Page 1: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Speed Up Application Development with Data Binding

Anokhin Mikle / Android Developer / MWDN Ltd.

Page 2: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

First Part: Introduction

Page 3: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

How to start (Java)

android { buildToolsVersion "24.0.1" dataBinding { enabled = true }}

buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.2' }}

3

Page 4: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

How to start (Kotlin)

apply plugin: 'kotlin-android'

android { buildToolsVersion "24.0.1" dataBinding { enabled = true } }

dependencies { kapt 'com.android.databinding:compiler:2.1.2'}

kapt { generateStubs = true}

buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.2' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.3' }}

4

Page 5: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Binding (Model)

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data> <variable name="user" type="com.anokmik.databinding.model.User" /> </data>

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{user.firstName}"/>

<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{user.lastName}"/>

</LinearLayout>

</layout>

public class BindingModelFragment extends Fragment {

@Override public View onCreateView(LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(

R.layout.fragment_binding_model, container, false); FragmentBindingModelBinding binding

= FragmentBindingModelBinding.bind(view); binding.setUser(User.getDefault()); return view; }

}

5

Page 6: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Data Binding doesn’t handle view state

so you should specify id for such views as usual

Page 7: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Binding (Ids)

public class BindingIdsFragment extends Fragment { private TextView firstName; private TextView lastName;

@Override public View onCreateView(LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(

R.layout.fragment_binding_ids, container, false); FragmentBindingIdsBinding binding

= FragmentBindingIdsBinding.bind(view); firstName = binding.firstName; lastName = binding.lastName; return view; }

@Override public void onViewCreated(View view,

Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); User user = User.getDefault(); firstName.setText(user.firstName); lastName.setText(user.lastName); }

}

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

<TextView android:id="@+id/first_name" android:layout_width="match_parent" android:layout_height="wrap_content"/>

<TextView android:id="@+id/last_name" android:layout_width="match_parent" android:layout_height="wrap_content"/>

</LinearLayout>

</layout>

7

Page 8: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Generated Binding

8

public class FragmentBindingModelBinding extends android.databinding.ViewDataBinding {

@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } java.lang.String firstNameUser = null; java.lang.String lastNameUser = null; com.anokmik.databinding.model.User user = mUser; if ((dirtyFlags & 0x3L) != 0) { user = user; if (user != null) { firstNameUser = user.firstName; lastNameUser = user.lastName; } } if ((dirtyFlags & 0x3L) != 0) { this.mboundView1.setText(firstNameUser); this.mboundView2.setText(lastNameUser); } }

}

Page 9: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Bindings are generated at compile-time

Page 10: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Variables and Imports

<data>

<import type="android.graphics.drawable.Drawable" /> <import type="com.anokmik.databinding.model.User" /> <import type="android.util.SparseArray"/> <import type="java.util.Map"/> <import type="java.util.List"/>

<variable name="user" type="User" /> <variable name="image" type="Drawable" /> <variable name="text" type="String" /> <variable name="array" type="String[]" /> <variable name="list" type="List&lt;String>"/> <variable name="sparse" type="SparseArray&lt;String>"/> <variable name="map" type="Map&lt;String, String>"/> <variable name="index" type="int"/> <variable name="key" type="String"/>

</data>

<data>

<import type="android.app.Fragment"/> <import type="android.support.v4.app.Fragment" alias="SupportFragment"/>

</data>

10

Page 11: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Simple Data Model

public class User { private final String firstName; private final String lastName;

public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; }

public String getFirstName() { return firstName; }

public String getLastName() { return lastName; } }

public class User { public final String firstName; public final String lastName;

public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; }

}

11

Page 12: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Observable Data Model

public class NotifyGreeting extends BaseObservable {

private String name;

@Bindable public String getName() { return name; }

public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); }

}

public class ObservableGreeting {

public ObservableString name = new ObservableString();

}

12

Page 13: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Include and Merge

<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto">

<data> <variable name="user" type="com.example.User" /> </data>

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

<include layout="@layout/name" bind:user="@{user}" /> </LinearLayout> </layout>

<layout xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <merge> <include layout="@layout/name" bind:user="@{user}"/> </merge> </layout>

13

Page 14: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Expressions

● mathematical +, -, /, *, %● string concatenation +● logical &&, ||● binary &, |, ^● unary +, -, !, ~● shift >>, >>>, <<● comparison ==, >, <, >=, <=● instanceof

● grouping ()● literals - character, String, numeric, null● cast● method calls● field access● array access []● ternary operator ?:

android:enabled="@{communicator.isLoginValid &amp; communicator.isPasswordValid}"

14

Page 15: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Binding Providers

15

@BindingMethods({ @BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"), @BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"), @BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps")})public class TextViewBindingAdapter {

@BindingAdapter("android:text") public static void setText(TextView view, CharSequence text) { ... }

}

public class Converters {

@BindingConversion public static String convertObservableToString(ObservableString observableString) { return observableString.get(); }

}

Page 16: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Data Binding Component

16

public class MainDataBindingComponent {

@BindingAdapter(value = {"defaultColor", "pressedColor"}) public void setButtonStateListBackground(Button button, int defaultColor, int pressedColor) { button.setBackground(getButtonStateListDrawable(defaultColor, pressedColor)); }

}

public class DataBindingComponentProvider implements DataBindingComponent {

@Override public MainDataBindingComponent getMainDataBindingComponent() { return new MainDataBindingComponent(); }

}

DataBindingUtil.setDefaultComponent(new DataBindingComponentProvider());

DataBindingUtil.setContentView(this, R.layout.activity_main, new DataBindingComponentProvider());

DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false, new DataBindingComponentProvider());

Page 17: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Two-way Binding

17

android:text="@={communicator.editTextValue}"

AdapterView android:selectedItemPosition

CalendarView android:date

CompoundButton android:checked

DatePicker android:year / android:month / android:day

NumberPicker android:value

RadioGroup android:checkedButton

RatingBar android:rating

SeekBar android:progress

TabHost android:currentTab

TextView android:text

TimePicker android:hour / android:minute

app:color="@={communicator.color}"

Page 18: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Two-way binding requires getters and setters for fields

Page 19: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Second Part: Typical Use Cases

Page 20: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

User Flow

20

Login Profile

Page 21: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

User Layout Custom Items

21

Toolbar

<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ToolbarTheme" app:onNavigationClick="@{navigationClickListener}" app:popupTheme="@style/PopupTheme" />

Page 22: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Support Toolbar Component

22

@BindingMethods({ @BindingMethod(type = Toolbar.class, attribute = "onMenuItemClick", method = "setOnMenuItemClickListener"), @BindingMethod(type = Toolbar.class, attribute = "onNavigationClick", method = "setNavigationOnClickListener")})public final class SupportToolbarComponent {

}

@BindingMethods( BindingMethod(type = Toolbar::class, attribute = "onMenuItemClick", method = "setOnMenuItemClickListener"), BindingMethod(type = Toolbar::class, attribute = "onNavigationClick", method = "setNavigationOnClickListener"))class SupportToolbarComponent

Page 23: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

User Layout Custom Items

23

Text Input Layout Button

<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{() -> presenter.login()}" android:text="@string/login" />

<android.support.design.widget.TextInputLayout style="@style/TextInputLayoutStyle" android:layout_width="match_parent" android:layout_height="wrap_content" app:editable="@{presenter.isEditing}" app:error="@{@string/error_name_not_valid}" app:showError="@{!presenter.firstNameValid}">

<android.support.design.widget.TextInputEditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_first_name" android:text="@={presenter.observableUser.firstName}" />

</android.support.design.widget.TextInputLayout>

Page 24: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Text Input Layout Component

24

public final class TextInputLayoutComponent {

@BindingAdapter({"error", "showError"}) public void setError( TextInputLayout view, String error, boolean showError ) { view.setError(showError ? error : null); }

@BindingAdapter("editable") public void setEditable( TextInputLayout view, boolean isEditable ) { view.setFocusable(isEditable); EditText editText = view.getEditText(); if (editText != null) { editText.setCursorVisible(isEditable); editText.setFocusable(isEditable); editText.setFocusableInTouchMode(isEditable); Drawable background = editText.getBackground(); if (background != null) { background.setAlpha(isEditable ? 255 : 0); } if (isEditable) { Editable editableText = editText.getText(); int selectionStart = editText.getSelectionStart(); int selectionEnd = editText.getSelectionEnd(); if (!TextUtils.isEmpty(editableText) && (selectionStart == 0) && (selectionEnd == 0)) { editText.setSelection(editableText.length()); } } } }

}

class TextInputLayoutComponent {

@BindingAdapter("error", "showError") fun setError( view: TextInputLayout, error: String, showError: Boolean ) { view.error = if (showError) error else null }

@BindingAdapter("editable") fun setEditable( view: TextInputLayout, isEditable: Boolean ) { view.isFocusable = isEditable view.editText?.apply { isCursorVisible = isEditable isFocusable = isEditable isFocusableInTouchMode = isEditable background?.apply { alpha = if (isEditable) 255 else 0 } if (isEditable) { val editableText = text if (!TextUtils.isEmpty(editableText) && (selectionStart == 0) && (selectionEnd == 0)) { setSelection(editableText.length) } } } }

}

Page 25: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Trips Flow

25

Trips List

Trip Event

Trip Details

Page 26: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Trips Layout Custom Items

26

Recycler View

<android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:padding="@dimen/fragment_padding" app:decoration="@{decoration}" app:items="@{presenter.trips}" app:layoutManager="@{layoutManager}" app:viewHolderPresenter="@{presenter.viewHolderPresenter}" />

<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@{trip.title}" android:textColor="@color/material_dark_primary_text_color" android:textSize="@dimen/title_text_size" android:textStyle="bold" />

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{DateUtils.format(trip.startDate, trip.finishDate)}" android:textColor="@color/material_dark_secondary_text_color" android:textSize="@dimen/caption_text_size" android:textStyle="bold" />

TextView (Title) TextView (Dates)

Page 27: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Recycler View Component

27

public final class RecyclerViewComponent {

@BindingAdapter({"items", "viewHolderPresenter"}) public void setContent( final RecyclerView view, List<?> list, ViewHolderPresenter<?> viewHolderPresenter ) { BinderRecyclerViewAdapter adapter = new BinderRecyclerViewAdapter( LayoutInflater.from(view.getContext()), list, viewHolderPresenter ); view.setAdapter(adapter); if (list instanceof ObservableList) { view.addOnAttachStateChangeListener( new ObservableListAttachStateChangeListener( (ObservableList) list, new RecyclerViewListChangedCallback(adapter) ) ); } }

@BindingAdapter("decoration") public void setDecoration(RecyclerView view, RecyclerView.ItemDecoration decoration) { view.addItemDecoration(decoration); }

@BindingAdapter("decoration") public void setDecoration(RecyclerView view, RecyclerView.ItemDecoration[] decorations) { for (RecyclerView.ItemDecoration decoration : decorations) { view.addItemDecoration(decoration); } }

}

class RecyclerViewComponent {

@BindingAdapter("items", "viewHolderPresenter") fun <T> setContent( view: RecyclerView, list: List<T>, viewHolderPresenter: ViewHolderPresenter<T> ) { val adapter = BinderRecyclerViewAdapter<T, ViewDataBinding>( LayoutInflater.from(view.context), list, viewHolderPresenter ) view.adapter = adapter if (list is ObservableList<T>) { view.addOnAttachStateChangeListener( ObservableListAttachStateChangeListener( list, RecyclerViewListChangedCallback(adapter) ) ) } }

@BindingAdapter("decoration") fun setDecoration(view: RecyclerView, decoration: RecyclerView.ItemDecoration) { view.addItemDecoration(decoration) }

@BindingAdapter("decoration") fun setDecoration(view: RecyclerView, decorations: Array<RecyclerView.ItemDecoration>) { for (decoration in decorations) { view.addItemDecoration(decoration) } }

}

Page 28: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Trips Layout Custom Items

28

View Pager

<android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" app:items="@{presenter.photoAttachments}" app:viewHolderPresenter="@{presenter.viewHolderPresenter}" />

Page 29: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

View Pager Component

29

class ViewPagerComponent {

@BindingAdapter("items", "viewHolderPresenter") fun <T> setContent(view: ViewPager, list: List<T>, viewHolderPresenter: ViewHolderPresenter<T>) { val adapter = BinderViewPagerAdapter( LayoutInflater.from(view.context), list, viewHolderPresenter ) view.adapter = adapter if (list is ObservableList<T>) { view.addOnAttachStateChangeListener( ObservableListAttachStateChangeListener( list, ViewPagerListChangedCallback(adapter) ) ) } }

}

public final class ViewPagerComponent {

@BindingAdapter({"items", "viewHolderPresenter"}) public void setContent(ViewPager view, List<?> list, ViewHolderPresenter<?> viewHolderPresenter) { BinderViewPagerAdapter adapter = new BinderViewPagerAdapter( LayoutInflater.from(view.getContext()), list, viewHolderPresenter ); view.setAdapter(adapter); if (list instanceof ObservableList) { view.addOnAttachStateChangeListener(new ObservableListAttachStateChangeListener( (ObservableList) list, new ViewPagerListChangedCallback(adapter)) ); } }

}

Page 30: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Image View Component

30

class ImageViewComponent {

@BindingAdapter("android:src") fun loadImage(view: ImageView, imageUrl: String) { Picasso.with(view.context).load(imageUrl).into(view) }

}

public final class ImageViewComponent {

@BindingAdapter("android:src") public void loadImage(ImageView view, String imageUrl) { Picasso.with(view.getContext()).load(imageUrl).into(view); }

}

Page 31: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Thanks for your attention!

sources:

first part: https://github.com/anokmik/data-binding

second part: https://github.com/anokmik/trip-assistant

mail: [email protected]

skype: anokmik

Page 32: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

References

● Data Binding Guide from Google (https://goo.gl/1yFPtt)

● No More findViewById (https://goo.gl/ugV86t)

● Android Data Binding: Adding Some Variability (https://goo.gl/aDTMmp)

● Android Data Binding: Express Yourself (https://goo.gl/NZ15Tz)

● Android Data Binding: Custom Setters (http://goo.gl/DnKgON)

● Android Data Binding: The Big Event (https://goo.gl/qcpdpU)

● Android Data Binding: That <include> Thing (https://goo.gl/QiqpQT)

● Android Data Binding: Let’s Flip This Thing (https://goo.gl/Gok1gv)

● Descent into Data Binding (https://goo.gl/1PTE7F)

● Two-way Data Binding (https://goo.gl/DQtRrh | https://goo.gl/KjY2Ze)

● Porting to Data Binding (https://goo.gl/7CA7vX)

● Advanced Data Binding (https://www.youtube.com/watch?v=DAmMN7m3wLU)

● Data Binding Techniques (https://www.youtube.com/watch?v=WdUbXWztKNY)

32

Page 33: SE2016 Android Mikle Anokhin "Speed up application development with data binding"

Q&A