CoordinatorLayout.Behaviorを独自実装する上での前提知識

`CoordinatorLayout` で `AppBarLayout` を使うSampleはよく見かけますが、 `CoordinatorLayout.Behavior` を独自実装して制御しているサンプルはあまり見なかったので、すごくざっくりですが動かし方を書いてみようと思います。

# CoordinatorLayoutとAttachedBehavior

スクロールできる `DependedView` と、それに伴って動く `ComsumedScrollView` が存在するViewがあるものとします。 `AppBarLayout` の代わりが `ComsumedScrollView` で、 xmlで `layout_behavior` を設定していたものが `DependedView` だと思ってください。この `ComsumedScrollView` と `DependedView` は `CoordinatorLayout.AttachedBehavior` を実装していて、 `CoordinatorLayout` は実装された `behavior` をスクロール検知時にいい感じに叩いてくれます。

```xml
<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

```kotlin:ConsumedScrollView.kt
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

```kotlin:DependedView.kt
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
        }
    }
}
```

１にはスクロールによって動いたviewが通知されます。通知されたviewに付随してなにかを動かす必要がある場合は、ここでtrueを返答します。1でtrueを返答した場合に2が呼び出されます。ここで、スクロールしたやつの真下についていくとか、一緒に回転するとか独自の動きを実装します。

# まとめ

AppBarLayoutの中を見るともっと詳細なことはわかると思いますが、scrollを察知して奪い取って動く側と、奪い取られた動きを元に動くやつの実装をすればいい感じに動かせるっていう大前提がわかっているとちょっとは捗るんじゃないかと思います。具体的なサンプルは時間があれば書きます。。。