Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Intermediate Level Data Binding

Intermediate Level Data Binding

shibuya.apk #13

Keisuke Kobayashi

March 24, 2017
Tweet

More Decks by Keisuke Kobayashi

Other Decks in Programming

Transcript

  1. About me • Keisuke Kobayashi • GitHub, Qiita: @kobakei •

    Twitter: @ksk_kbys • Android, Rails, React
  2. Data: ObservableField public class MainViewModel { public ObservableField<String> name =

    new ObservableField<>(); public ObservableBoolean visible = new ObservableBoolean(true); }
  3. 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); } }
  4. 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); } }
  5. 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); } }
  6. Data -> UI <layout> <data> <import type=“android.view.View” /> <variable name="viewModel"

    type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout>
  7. Data -> UI <layout> <data> <import type=“android.view.View” /> <variable name="viewModel"

    type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout>
  8. <layout> <data> <import type=“android.view.View” /> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout

    android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout> Data -> UI <data></data>
  9. <layout> <data> <import type=“android.view.View” /> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout

    android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout> Data -> UI <variable> ࢀর͢ΔΦϒδΣΫτ
  10. <layout> <data> <import type=“android.view.View” /> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout

    android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout> Data -> UI
  11. <layout> <data> <import type=“android.view.View” /> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout

    android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout> Data -> UI ࢖༻͢ΔΫϥεΛΠϯϙʔτ
 ʢjava.lang.* ͸ෆཁʣ
  12. <layout> <data> <import type=“android.view.View” /> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout

    android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.name}" android:visibility="@{viewModel.visible ? View.VISIBLE : View.GONE}"/> </RelativeLayout> </layout> Data -> UI
  13. 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); }
  14. UI -> Data <data> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout android:layout_width="match_parent"

    android:layout_height="match_parent"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@={viewModel.name}"/> </RelativeLayout>
  15. UI -> Data <data> <variable name="viewModel" type="io.github.kobakei.dbsample.MainViewModel"/> </data> <RelativeLayout android:layout_width="match_parent"

    android:layout_height="match_parent"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@={viewModel.name}"/> </RelativeLayout>
  16. BindingAdapter public class BindingAdapterUtil { @BindingAdapter(value = {"imageUrl"}) public static

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

    void setImageUrl(ImageView imageView, String url) { Picasso.with(context).load(url).into(imageView); } } XMLଐੑ໊
  18. 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); } } }
  19. 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 } });
  20. 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
  21. 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); } } }); }
  22. 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); } } }); }
  23. 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ʣ
  24. InverseBindingMethod @InverseBindingMethods({ @InverseBindingMethod( type = ListView.class, attribute = "checkedItemPositions", event

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

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

    = “checkedItemPositionsAttrChanged", method = "getCheckedItemPositions" ) }) public class BindingAdapterUtil { // ... } σʔλऔಘͰݺ͹ΕΔϝιου
  27. 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)); } } }
  28. 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ʹҰக
  29. 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(); } } }); }
  30. 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ʹҰக
  31. 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Λ ݺͼग़͢
  32. Data class public class ListChoiceActivityViewModel { public ObservableList<Item> items; public

    ObservableField<SparseBooleanArray> checkedItemPositions; public ListChoiceActivityViewModel() { this.items = new ObservableArrayList<>(); this.checkedItemPositions = new ObservableField<>(); } }
  33. class ObservableRecyclerAdapter extends RecyclerView.Adapter<ViewHolder> { private final Context context; private

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

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