Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Coordinator Layout Behavior

2,530 views

Published on

kyobashi.dex #2の資料です。(Android向け)

Published in: Engineering
  • Be the first to comment

Coordinator Layout Behavior

  1. 1. Coordinator Layout Behavior kyobashi.dex #2
  2. 2. 自己紹介 釘宮 愼之介 / @kgmyshin ・ Androidエンジニア ・ Androidエンジニア
  3. 3. 今回お話すること CoordinatorLayoutのBehaviorについて 本当はCoordinatorLayoutについて隅から隅まで話そうと思ったけど 時間がなさそうだったので、今回はBehaviorに焦点を当てます。
  4. 4. この発表で達成したいこと 聴いてくれた方が、聴き終わったあとに Behaviorの仕組みを理解し、 右のようなカスタムBehaviorを 作れるようになっている状態にすること。
  5. 5. 目次 ・ CoordinatorLayoutとは ・ Behaviorとは ・ Behaviorの仕組み ・ すでにあるBehaviorたち ・ カスタムBehaviorを作ってみる
  6. 6. CoordinatorLayoutとは CoordinatorLayoutというのは子ビュー同士が 相互に動くようなインタラクションをする場合に使われるViewGroupです。 とくにMaterial DesignガイドラインのScrolling techniquesを実現するときに使う印象。
  7. 7. Behaviorとは CoordinatorLayoutの子ビューの動きのプラグインです。
  8. 8. だいたいがこの二つのメソッドをOverrideして使っているみたい • layoutDependsOn • onDependentViewChanged
  9. 9. Behaviorの仕組み(簡易版)
  10. 10. これ以外にも TouchEventやScrollのイベントが取れたりもする ・onNestedFling ・onNestedPreFling ・onNestedPreScroll ・onNestedScroll ・onTouchEvent :
  11. 11. 実際のBehaviorを見てみましょう
  12. 12. FloatingActionButton.Behavior
  13. 13. FloatingActionButton.Behaviorの各メソッドはこのようになってます。(※1 ) public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { return dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean onDependentViewChanged( CoordinatorLayout parent, FloatingActionButton child, View dependency ) { if (dependency instanceof Snackbar.SnackbarLayout) { updateFabTranslationForSnackbar(parent, child, dependency); } return false; } ※1 説明のために一部ソースを削除してます
  14. 14. FloatingActionButton.Behaviorの各メソッドはこのようになってます。(※1 ) public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { return dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean onDependentViewChanged( CoordinatorLayout parent, FloatingActionButton child, View dependency ) { if (dependency instanceof Snackbar.SnackbarLayout) { updateFabTranslationForSnackbar(parent, child, dependency); } return false; } ※1 説明のために一部ソースを削除してます
  15. 15. FloatingActionButton.Behaviorの各メソッドはこのようになってます。(※1 ) public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) { return dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean onDependentViewChanged( CoordinatorLayout parent, FloatingActionButton child, View dependency ) { if (dependency instanceof Snackbar.SnackbarLayout) { updateFabTranslationForSnackbar(parent, child, dependency); } return false; } ※1 説明のために一部ソースを削除してます
  16. 16. onPreDrawをトリガーにしている
  17. 17. AppBarLayout.ScrollingViewBehavior @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof AppBarLayout; }
  18. 18. @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { final CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); if (behavior instanceof Behavior) { // Offset the child so that it is below the app-bar (with any overlap) final int appBarOffset = ((Behavior) behavior) .getTopBottomOffsetForScrollingSibling(); final int expandedMax = dependency.getHeight() - mOverlayTop; final int collapsedMin = parent.getHeight() - child.getHeight(); if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { // If we have an overlap top, and the dependency is an AppBarLayout, we control // the offset ourselves based on the appbar's scroll progress. This is so that // the scroll happens sequentially rather than linearly final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, Math.abs(appBarOffset) / (float) scrollRange)); } else { setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset); } } return false; }
  19. 19. @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { final CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); if (behavior instanceof Behavior) { // Offset the child so that it is below the app-bar (with any overlap) final int appBarOffset = ((Behavior) behavior) .getTopBottomOffsetForScrollingSibling(); final int expandedMax = dependency.getHeight() - mOverlayTop; final int collapsedMin = parent.getHeight() - child.getHeight(); if (mOverlayTop != 0 && dependency instanceof AppBarLayout) { // If we have an overlap top, and the dependency is an AppBarLayout, we control // the offset ourselves based on the appbar's scroll progress. This is so that // the scroll happens sequentially rather than linearly final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange(); setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin, Math.abs(appBarOffset) / (float) scrollRange)); } else { setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset); } } return false; }
  20. 20. SwipeDismissBehavior 下記二つは実装していない。 • layoutDependsOn • onDependentViewChanged タッチイベントでごりごりやってる。
  21. 21. カスタムBehaviorを作ってみる すごく簡単なやつ
  22. 22. • layoutDependsOn • onDependentViewChanged 下記を実装するだけ
  23. 23. public class CustomBehavior extends CoordinatorLayout.Behavior<View> { public CustomBehavior(Context context, AttributeSet attrs) { } @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof AppBarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { if (dependency instanceof AppBarLayout) { AppBarLayout appBarLayout = (AppBarLayout) dependency; int totalScrollRange = appBarLayout.getTotalScrollRange(); int scrollY = appBarLayout.getTop(); float ratio = -scrollY / (float) totalScrollRange; child.setAlpha(1.f - ratio); } return true; } }
  24. 24. public class CustomBehavior extends CoordinatorLayout.Behavior<View> { public CustomBehavior(Context context, AttributeSet attrs) { } @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof AppBarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { if (dependency instanceof AppBarLayout) { AppBarLayout appBarLayout = (AppBarLayout) dependency; int totalScrollRange = appBarLayout.getTotalScrollRange(); int scrollY = appBarLayout.getTop(); float ratio = -scrollY / (float) totalScrollRange; child.setAlpha(1.f - ratio); } return true; } }
  25. 25. public class CustomBehavior extends CoordinatorLayout.Behavior<View> { public CustomBehavior(Context context, AttributeSet attrs) { } @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof AppBarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { if (dependency instanceof AppBarLayout) { AppBarLayout appBarLayout = (AppBarLayout) dependency; int totalScrollRange = appBarLayout.getTotalScrollRange(); int scrollY = appBarLayout.getTop(); float ratio = -scrollY / (float) totalScrollRange; child.setAlpha(1.f - ratio); } return true; } }
  26. 26. 注意 コンストラクタも実装しよう public CustomBehavior(Context context, AttributeSet attrs) { }
  27. 27. リフレクションに失敗して落ちます
  28. 28. まとめ ・ Behaviorを使うと他の子ビューに依存した動きを定義しやすくなります。 ・ 使う場合は大抵 layoutDependsOn とonDependentViewChanged を Overrideすればなんとかなります。
  29. 29. 宣伝 ・ shinobu.apkってのやります! http://shinobu-apk.connpass.com/event/24921/ shinobu.apkとは 「Shinobu Okanoと愉快な仲間たちが繰り広げるファンタジーな勉強会」 です。
  30. 30. ご静聴ありがとうございました。

×