CoordinatorLayout
で AppBarLayout
を使うSampleはよく見かけますが、 CoordinatorLayout.Behavior
を独自実装して制御しているサンプルはあまり見なかったので、すごくざっくりですが動かし方を書いてみようと思います。
CoordinatorLayoutとAttachedBehavior
スクロールできる DependedView
と、それに伴って動く ComsumedScrollView
が存在するViewがあるものとします。 AppBarLayout
の代わりが ComsumedScrollView
で、 xmlで layout_behavior
を設定していたものが DependedView
だと思ってください。この ComsumedScrollView
と DependedView
は CoordinatorLayout.AttachedBehavior
を実装していて、 CoordinatorLayout
は実装された behavior
をスクロール検知時にいい感じに叩いてくれます。
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ComsumedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<DependedView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
スクロールを奪い取るView
class ComsumedScrollView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), CoordinatorLayout.AttachedBehavior {
override fun getBehavior(): CoordinatorLayout.Behavior<*> = Behavior(context)
class Behavior(context: Context, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<ComsumedScrollView>(context, attrs) {
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: ComsumedScrollView, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
// 1
return true
}
override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: ComsumedScrollView, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
// 2
}
}
}
1でtrueを返答することで、そのScrollを奪い取る可能性があることを教えます。trueを返答した場合に、2が呼び出され、ここにスクロール時にカスタマイズしたい動作を記載します。スクロールでViewを円形に動かしたりとか、ある程度進んだ後に画面上部に固定するとか、動きを実装します。もし、このViewがある一定の動きをするまで他のスクロールを止めたいというのであれば、consumedでどれだけスクロールを消費したか?を教えます。
付随して動くView
class DependedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : ViewPager(context, attrs), CoordinatorLayout.AttachedBehavior {
override fun getBehavior(): CoordinatorLayout.Behavior<*> = Behavior(context)
class Behavior(context: Context, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<DependedView>(context, attrs) {
override fun layoutDependsOn(parent: CoordinatorLayout, child: DependedView, dependency: View): Boolean {
// 1
return true
}
override fun onDependentViewChanged(parent: CoordinatorLayout, child: DependedView, dependency: View): Boolean {
// 2
return true
}
}
}
1にはスクロールによって動いたviewが通知されます。通知されたviewに付随してなにかを動かす必要がある場合は、ここでtrueを返答します。1でtrueを返答した場合に2が呼び出されます。ここで、スクロールしたやつの真下についていくとか、一緒に回転するとか独自の動きを実装します。
まとめ
AppBarLayoutの中を見るともっと詳細なことはわかると思いますが、scrollを察知して奪い取って動く側と、奪い取られた動きを元に動くやつの実装をすればいい感じに動かせるっていう大前提がわかっているとちょっとは捗るんじゃないかと思います。具体的なサンプルは時間があれば書きます。。。