CoordinatorLayoutでヘッダー隠す時の知見を得た話


画面をスクロールした際にヘッダーを隠すような挙動を`CoordinatorLayout`で実装できます。
最近、とあるアプリの画面にこの挙動を実装した際にいろいろ試行錯誤したので、｢こうやったらヘッダーがうまく隠れてくれたぜイェーイ｣ってなった例や、｢ここでこうやったらレイアウトも崩れなかったぜイェーイ｣ってなったテンションのままで書いてます。

## ヘッダーを隠すために

スクロールしたらヘッダーを隠せるようにするUIを作るための基礎知識とかです。
とりあえずこの内容が把握出来てると、あれこれしてるうちになんとなくできる。

### スクロールするレイアウト

`CoordinatorLayout`でスクロール時にヘッダーを隠す挙動を行いたい際は、スクロールする部分が`NestedScrollView`か`RecyclerView`である必要があります。
ここを`ScrollView`とか`ListView`の状態で｢ヘッダーが・・・隠れない・・・！｣ってなった人もいるかもしれないですね。

とりあえず`CoordinatorLayout`のリファレンスを見に行ってもこの辺りは記載されておらず、`AppBarLayout`の方に書かれているので、初めてする人だと引っかかる人もいるかもしれません。

あと、｢ひとまずこのレイアウトをスクロールしたらヘッダーを隠したいぜ｣という場合ですが、そのときは｢スクロールしたいレイアウト｣を子にもっている、｢隠したいヘッダーを子レイアウトに持つ`CoordinatorLayout`｣の子レイアウトにつけておきましょう。 ~~自分で書いておいてあれだけどめっちゃわかりにくいな・・・。~~

### 隠すヘッダー

隠したいヘッダー部分は、`AppBarLayout`の要素内に配置する必要があります。
あと、この`AppBarLayout`は`CoordinatorLayout`の直下になければいけません。

`AppBarLayout`内にヘッダーを配置し、次のように`app:layout_scrollFlags`属性を設定すると、設定した値に従ってスクロール時に`AppBarLayout`が隠れてくれるナイスなレイアウトになってくれます。

```xml
<AppBarLayout ...>
	<ToolBar ...
		app:layout_scrollFlags="scroll|enterAlways"
	/>
</AppBarLayout>

```

上のサンプルだと、下方向にスクロールするとAppBarLayoutが隠れて、上方向にスクロールすることでAppBarLayoutが表示されるようになります。

`layout_scrollFlags`に指定できるフラグについては[こちら](https://developer.android.com/reference/android/support/design/widget/AppBarLayout.LayoutParams.html#setScrollFlags(int))をどうぞ。

### 基本を抑えたうえで

スクロール時にヘッダーを隠すレイアウトを作成するための基礎知識はこんなところです。
ただ、スクロールするレイアウトの階層が深いところにあったり、別のレイアウトファイルをincludeしていたり、フラグメントであった場合にちゃんと隠れてくれるのか？というところは一度試してみないとわからないところです。

次の画像のようなレイアウトで、Toolbarを隠すようなアプリを目標にして、
そういった場合についていくつか試してみたので、参考例として一覧をあげていきます。

![behavior概要_skiched.png](https://qiita-image-store.s3.amazonaws.com/0/66681/1600713e-fddc-509f-5c3d-ddb33de273c9.png)

## 成功例

### スクロールするレイアウトxmlがヘッダーのあるレイアウトxmlと別のとき

// TODO それっぽいレイアウト(隠すヘッダーを示す)

```xml:res/layout/activity_main.xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                ...
                app:layout_scrollFlags="scroll|enterAlways"/>  

        </android.support.design.widget.AppBarLayout>

        <include layout="@layout/view_pager" />

    </android.support.design.widget.CoordinatorLayout>

</LinearLayout>

```

```xml:res/layout/view_pager.xml
<android.support.v4.widget.NestedScrollView
    android:id="@+id/pager"
    android:layout_below="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
    <!-- 中略 -->

</android.support.v4.widget.NestedScrollView>
```

![behavior_success01_480.gif](https://qiita-image-store.s3.amazonaws.com/0/66681/a16c110a-c244-af4b-4667-d2005adaa27d.gif)


### AppBarLayoutにスクロール時に隠したいものと隠したくないものを含みたい

```xml:res/layout/activity_main.xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                ...
                app:layout_scrollFlags="scroll|enterAlways"/>  

            <android.support.design.widget.TabLayout
                android:id="@+id/tab"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            </android.support.design.widget.TabLayout>

        </android.support.design.widget.AppBarLayout>

        <include layout="@layout/view_pager" 
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    </android.support.design.widget.CoordinatorLayout>

</LinearLayout>

```

```xml:res/layout/view_pager.xml
<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_below="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

</android.support.v4.view.ViewPager>
```

![behavior_success02_480.gif](https://qiita-image-store.s3.amazonaws.com/0/66681/2d297323-d79b-b057-ef88-28bf2cccc5df.gif)

## 失敗例

### AppBarLayoutが｢1. 隠したくないもの、2. 隠したいもの｣で構成されてる

```xml:res/layout/activity_main.xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                .../>

            <android.support.design.widget.TabLayout
                android:id="@+id/tab"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|enterAlways">

            </android.support.design.widget.TabLayout>

        </android.support.design.widget.AppBarLayout>

        <include layout="@layout/view_pager" />

    </android.support.design.widget.CoordinatorLayout>

</LinearLayout>

```

```xml:res/layout/view_pager.xml
<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_below="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

</android.support.v4.view.ViewPager>
```

![behavior_fail_480.gif](https://qiita-image-store.s3.amazonaws.com/0/66681/c0799471-7677-1b0c-3c88-8221277cdfcc.gif)

上記のような例だと、スクロール時にヘッダーを隠す動作が実行されません。
この場合、AppBarLayout内の一番上にある隠したくないViewを、さらに上位の階層に移すことで解決するほかないと思います。

(※まあこんなUIになる場合に、一番上の隠したくないViewはそれ以下のLayoutとは別の責務を持つことが多いと思うので同じレイアウトファイルに収まってるのがおかしいのかな、とも思います)


## しめ

という感じで成功例ふたつ失敗例ひとつという感じで紹介しました。
この先すすめていくとBehaviorを自作する話になってくるのですが、これは後日で。