Slide 1

Slide 1 text

Intermediate Level 
 Data Binding Keisuke Kobayashi / @kobakei shibuya.apk #13

Slide 2

Slide 2 text

About me • Keisuke Kobayashi • GitHub, Qiita: @kobakei • Twitter: @ksk_kbys • Android, Rails, React

Slide 3

Slide 3 text

ຊൃදʹ͍ͭͯ • σʔλόΠϯσΟϯάॳڃऀ޲͚ʹɺҰาઌ΁ߦ͘࢖͍ํΛ ঺հ͠·͢ • ެࣜΨΠυʹࡌͬͯͳ͍಺༰΋ؚΈ·͢ • https://developer.android.com/topic/libraries/data- binding/index.html • ιʔείʔυ • https://github.com/kobakei/AndroidDataBindingSample

Slide 4

Slide 4 text

ΞδΣϯμ • σʔλόΠϯσΟϯάͱ͸ • BindingAdapterͷ࢖͍ํ • Ϧετͱͷ࿈ܞ • ٧·Γͦ͏ͳϙΠϯτ

Slide 5

Slide 5 text

Ξϯέʔτ

Slide 6

Slide 6 text

σʔλόΠϯσΟϯάΛ
 ۀ຿Ͱ࢖ͬͯΔਓ✋

Slide 7

Slide 7 text

@BindingAdapterΛ
 ࢖ͬͨ͜ͱ͕͋Δਓ✋

Slide 8

Slide 8 text

@InverseBinding***Λ
 ࢖ͬͨ͜ͱ͕͋Δਓ✋

Slide 9

Slide 9 text

ObservableListΛ
 ࢖ͬͨ͜ͱ͕͋Δਓ✋

Slide 10

Slide 10 text

σʔλόΠϯσΟϯά

Slide 11

Slide 11 text

Android data binding • σʔλΦϒδΣΫτͱUIΛඥ෇͚ɺ
 σʔλͷ஋ΛࣗಈతʹUIʹ൓өͨ͠Γɺ
 ϢʔβʔͷೖྗΛࣗಈతʹσʔλʹ൓ө͢Δ
 GoogleެࣜϥΠϒϥϦ

Slide 12

Slide 12 text

app/build.gradle android { //... dataBinding { enabled true } }

Slide 13

Slide 13 text

Data: ObservableField public class MainViewModel { public ObservableField name = new ObservableField<>(); public ObservableBoolean visible = new ObservableBoolean(true); }

Slide 14

Slide 14 text

Data: BaseObservable public class MainViewModel extends BaseObservable { private String name; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } }

Slide 15

Slide 15 text

Data: BaseObservable public class MainViewModel extends BaseObservable { private String name; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } }

Slide 16

Slide 16 text

Data: BaseObservable public class MainViewModel extends BaseObservable { private String name; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } }

Slide 17

Slide 17 text

Data -> UI

Slide 18

Slide 18 text

Data -> UI

Slide 19

Slide 19 text

Data -> UI

Slide 20

Slide 20 text

Data -> UI ࢀর͢ΔΦϒδΣΫτ

Slide 21

Slide 21 text

Data -> UI

Slide 22

Slide 22 text

Data -> UI ࢖༻͢ΔΫϥεΛΠϯϙʔτ
 ʢjava.lang.* ͸ෆཁʣ

Slide 23

Slide 23 text

Data -> UI

Slide 24

Slide 24 text

Inflate layout @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Before // setContentView(R.layout.main_activity); // After MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity); MainViewModel viewModel = new MainViewModel(); binding.setViewModel(viewModel); }

Slide 25

Slide 25 text

૒ํ޲
 σʔλόΠϯσΟϯά

Slide 26

Slide 26 text

UI -> Data

Slide 27

Slide 27 text

UI -> Data

Slide 28

Slide 28 text

࢖͑ΔଐੑϦετ • ओͳ΋ͷ͸େମఆٛ͞Ε͍ͯΔ • http://qiita.com/kobakei/items/ 5e902dd24005e6768ac4 • setHoge͸ɺapp:hoge=“@{…}” ͱॻ͚Δ • SwipeRefreshLayout#setRefreshing
 -> app:refreshing=“@{viewModel.refreshing}”

Slide 29

Slide 29 text

BindingAdapterͷ
 ࢖͍ํ

Slide 30

Slide 30 text

BindingAdapter • σʔλόΠϯσΟϯάͰ࢖͑ΔଐੑΛࣗ෼Ͱ ఆٛͰ͖Δ • set, get྆ํՄೳ

Slide 31

Slide 31 text

Lv1. ImageViewʹ
 αʔόʔ্ͷը૾Λදࣔ͢Δ

Slide 32

Slide 32 text

JavaͰී௨ʹॻ͘ͱ ImageView imageView = (ImageView) findViewById(R.id.imageView); Picasso.with(context).load(url).into(imageView);

Slide 33

Slide 33 text

BindingAdapter public class BindingAdapterUtil { @BindingAdapter(value = {"imageUrl"}) public static void setImageUrl(ImageView imageView, String url) { Picasso.with(context).load(url).into(imageView); } }

Slide 34

Slide 34 text

BindingAdapter public class BindingAdapterUtil { @BindingAdapter(value = {"imageUrl"}) public static void setImageUrl(ImageView imageView, String url) { Picasso.with(context).load(url).into(imageView); } } XMLଐੑ໊

Slide 35

Slide 35 text

XML

Slide 36

Slide 36 text

Lv2: TextViewʹଧͪফ͠ઢ Λ෇͚Δ

Slide 37

Slide 37 text

JavaͰී௨ʹॻ͘ͱ TextView textView = (TextView) findViewById(R.id.textView); TextPaint paint = textView.getPaint(); paint.setFlags(TextPaint.STRIKE_THRU_TEXT_FLAG); paint.setAntiAlias(true);

Slide 38

Slide 38 text

BindingAdapter public class BindingAdapterUtil { @BindingAdapter(value = {"strike"}) public static void setTextStrike(TextView textView, boolean strike) { if (strike) { TextPaint paint = textView.getPaint(); paint.setFlags(TextPaint.STRIKE_THRU_TEXT_FLAG); paint.setAntiAlias(true); } } }

Slide 39

Slide 39 text

XML

Slide 40

Slide 40 text

XML @{}ͰғΉඞཁ͋Γ

Slide 41

Slide 41 text

͋ΕΕʔʁ σʔλΛόΠϯσΟϯάͯ͠ ͳ͍ͧʔʁ

Slide 42

Slide 42 text

ϝϞ • BindingAdapter͸ɺ
 ୯ʹXMLͷଐੑͷ௥Ճʹ΋࢖͑Δ • ࠓ·ͰJavaͰॻ͍ͯͨϏϡʔૢ࡞Λɺ
 ڞ௨Խͯ͠XMLʹԡ͠ग़ͤΔ • @{}ͰғΉͷΛ͓๨Εͳ͘

Slide 43

Slide 43 text

Lv3: RecyclerViewͷ
 εΫϩʔϧΠϕϯτ

Slide 44

Slide 44 text

JavaͰී௨ʹॻ͘ͱ RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { // do something } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // do something } });

Slide 45

Slide 45 text

BindingAdapter (1) public interface OnScrollStateChanged { void onScrollStateChanged(RecyclerView recyclerView, int newState); } public interface OnScrolled { void onScrolled(RecyclerView recyclerView, int dx, int dy); } 1 method 1 interface

Slide 46

Slide 46 text

BindingAdapter (2) @BindingAdapter(value = {"onScrollStateChanged", "onScrolled"}, requireAll = false) public static void setRecyclerViewScroll(RecyclerView recyclerView, final OnScrollStateChanged onScrollStateChanged, final OnScrolled onScrolled) { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (onScrollStateChanged != null) { onScrollStateChanged.onScrollStateChanged(recyclerView, newState); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (onScrolled != null) { onScrolled.onScrolled(recyclerView, dx, dy); } } }); }

Slide 47

Slide 47 text

BindingAdapter (2) @BindingAdapter(value = {"onScrollStateChanged", "onScrolled"}, requireAll = false) public static void setRecyclerViewScroll(RecyclerView recyclerView, final OnScrollStateChanged onScrollStateChanged, final OnScrolled onScrolled) { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (onScrollStateChanged != null) { onScrollStateChanged.onScrollStateChanged(recyclerView, newState); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (onScrolled != null) { onScrolled.onScrolled(recyclerView, dx, dy); } } }); }

Slide 48

Slide 48 text

BindingAdapter (2) @BindingAdapter(value = {"onScrollStateChanged", "onScrolled"}, requireAll = false) public static void setRecyclerViewScroll(RecyclerView recyclerView, final OnScrollStateChanged onScrollStateChanged, final OnScrolled onScrolled) { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (onScrollStateChanged != null) { onScrollStateChanged.onScrollStateChanged(recyclerView, newState); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (onScrolled != null) { onScrolled.onScrolled(recyclerView, dx, dy); } } }); } ݸผʹ࢖༻ՄೳʢσϑΥϧτ͸trueʣ

Slide 49

Slide 49 text

XML

Slide 50

Slide 50 text

Data class public class MainViewModel { public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // do something } }

Slide 51

Slide 51 text

Lv4: Multichoice ListViewͷ νΣοΫҐஔΛऔಘ

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

JavaͰී௨ʹॻ͘ͱ ListView listView = (ListView) findViewById(R.id.listView); SparseBooleanArray positions = listView.getCheckedItemPositions();

Slide 54

Slide 54 text

InverseBindingMethod @InverseBindingMethods({ @InverseBindingMethod( type = ListView.class, attribute = "checkedItemPositions", event = “checkedItemPositionsAttrChanged", method = "getCheckedItemPositions" ) }) public class BindingAdapterUtil { // ... }

Slide 55

Slide 55 text

InverseBindingMethod @InverseBindingMethods({ @InverseBindingMethod( type = ListView.class, attribute = "checkedItemPositions", event = “checkedItemPositionsAttrChanged", method = "getCheckedItemPositions" ) }) public class BindingAdapterUtil { // ... } σʔλ͕ߋ৽͞ΕΔΠϕϯτ໊

Slide 56

Slide 56 text

InverseBindingMethod @InverseBindingMethods({ @InverseBindingMethod( type = ListView.class, attribute = "checkedItemPositions", event = “checkedItemPositionsAttrChanged", method = "getCheckedItemPositions" ) }) public class BindingAdapterUtil { // ... } σʔλऔಘͰݺ͹ΕΔϝιου

Slide 57

Slide 57 text

InverseBindingMethod @InverseBindingMethods({ @InverseBindingMethod( type = ListView.class, attribute = "checkedItemPositions" ) }) public class BindingAdapterUtil { // ... } event, method͸লུՄೳ

Slide 58

Slide 58 text

BindingAdapter: setter @BindingAdapter(value = "checkedItemPositions") public static void setListViewCheckedItemPositions(ListView listView, SparseBooleanArray positions) { if (positions != null) { for (int i = 0; i < positions.size(); i++) { listView.setItemChecked(positions.keyAt(i), positions.valueAt(i)); } } }

Slide 59

Slide 59 text

BindingAdapter: setter @BindingAdapter(value = "checkedItemPositions") public static void setListViewCheckedItemPositions(ListView listView, SparseBooleanArray positions) { if (positions != null) { for (int i = 0; i < positions.size(); i++) { listView.setItemChecked(positions.keyAt(i), positions.valueAt(i)); } } } InverseBindingMethodʹҰக

Slide 60

Slide 60 text

BindingAdapter: event @BindingAdapter(value = "checkedItemPositionsAttrChanged") public static void setCheckedItemPositionsAttrChanged(ListView listView, final InverseBindingListener listener) { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView> parent, View view, int position, long id) { if (listener != null) { listener.onChange(); } } }); }

Slide 61

Slide 61 text

BindingAdapter: event @BindingAdapter(value = "checkedItemPositionsAttrChanged") public static void setCheckedItemPositionsAttrChanged(ListView listView, final InverseBindingListener listener) { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView> parent, View view, int position, long id) { if (listener != null) { listener.onChange(); } } }); } InverseBindingMethodʹҰக

Slide 62

Slide 62 text

BindingAdapter: event @BindingAdapter(value = "checkedItemPositionsAttrChanged") public static void setCheckedItemPositionsAttrChanged(ListView listView, final InverseBindingListener listener) { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView> parent, View view, int position, long id) { if (listener != null) { listener.onChange(); } } }); } ߲໨ΫϦοΫ࣌ʹ InverseBindingListenerΛ ݺͼग़͢

Slide 63

Slide 63 text

XML

Slide 64

Slide 64 text

Data class public class ListChoiceActivityViewModel { public ObservableList items; public ObservableField checkedItemPositions; public ListChoiceActivityViewModel() { this.items = new ObservableArrayList<>(); this.checkedItemPositions = new ObservableField<>(); } }

Slide 65

Slide 65 text

Data bindingͱListͷ࿈ܞ

Slide 66

Slide 66 text

΍Γ͍ͨ͜ͱ • Ϧετͷཁૉ͕૿ݮͨ͠ΒɺListView / RecyclerViewͷཁૉ਺΋࿈ಈͯ͠ཉ͍͠

Slide 67

Slide 67 text

ObservableList • ObservableFieldͷϦετ൛ • ཁૉ਺มԽͳͲͷΠϕϯτΛड͚औΔϦεφ Λొ࿥Ͱ͖Δ • Πϯελϯε࡞੒࣌ʹ͸ɺ ObservableArrayListΛ࢖͏

Slide 68

Slide 68 text

ListActivityViewModel public class ListActivityViewModel { public ObservableList items = new ObservableArrayList(); }

Slide 69

Slide 69 text

class ObservableRecyclerAdapter extends RecyclerView.Adapter { private final Context context; private final ObservableList items; ObservableRecyclerAdapter(Context context, ObservableList items) { this.context = context; this.items = items; // Add listener to ObservableList items.addOnListChangedCallback(new ObservableList.OnListChangedCallback>() { @Override public void onChanged(ObservableList items) { notifyDataSetChanged(); } @Override public void onItemRangeChanged(ObservableList items, int i, int i1) { notifyItemRangeChanged(i, i1); } @Override public void onItemRangeInserted(ObservableList items, int i, int i1) { notifyItemRangeInserted(i, i1); } @Override public void onItemRangeMoved(ObservableList items, int i, int i1, int i2) { notifyItemMoved(i, i1) } @Override public void onItemRangeRemoved(ObservableList items, int i, int i1) { notifyItemRangeRemoved(i, i1); } }); } //... }

Slide 70

Slide 70 text

class ObservableRecyclerAdapter extends RecyclerView.Adapter { private final Context context; private final ObservableList items; ObservableRecyclerAdapter(Context context, ObservableList items) { this.context = context; this.items = items; // Add listener to ObservableList items.addOnListChangedCallback(new ObservableList.OnListChangedCallback>() { @Override public void onChanged(ObservableList items) { notifyDataSetChanged(); } @Override public void onItemRangeChanged(ObservableList items, int i, int i1) { notifyItemRangeChanged(i, i1); } @Override public void onItemRangeInserted(ObservableList items, int i, int i1) { notifyItemRangeInserted(i, i1); } @Override public void onItemRangeMoved(ObservableList items, int i, int i1, int i2) { notifyItemMoved(i, i1) } @Override public void onItemRangeRemoved(ObservableList items, int i, int i1) { notifyItemRangeRemoved(i, i1); } }); } //... } มߋΠϕϯτൃੜ࣌ʹɺAdapterͷnotifyΛݺͿ

Slide 71

Slide 71 text

ObservableMap (ObservableArrayMap) • Ϛοϓ൛ • ࢖ͬͨ͜ͱͳ͍ͷͰ঺հ͸ׂѪ

Slide 72

Slide 72 text

ͦͷଞॳ৺ऀ͕٧·Γͦ͏ͳ ϙΠϯτ

Slide 73

Slide 73 text

UIͷߋ৽λΠϛϯά • ஋ͷมߋͷ࣍ͷϑϨʔϜʹͳΔ • ଈ࣌Ͱߋ৽͍ͨ͠৔߹ɺ ViewDataBinding#executePendingBindings() ΛݺͿ

Slide 74

Slide 74 text

εϨου • ίϨΫγϣϯҎ֎͸ɺσʔλϞσϧΛόοΫ άϥ΢ϯυεϨουͰมߋՄೳ • ObservableList, ObservableMap͸஫ҙ

Slide 75

Slide 75 text

·ͱΊ

Slide 76

Slide 76 text

·ͱΊ • BindingAdapterͷ࢖͍ํΛ঺հͨ͠ • RecyclerViewͱObservableListͷ૊Έ߹Θͤ ํΛ঺հͨ͠ • Let's try data binding

Slide 77

Slide 77 text

Thanks!