FloatingActionButtonをスクロール中に消す

NestedScrollViewを下にスクロールしている間は, FloatingActionButtonを表示させないようにする. 少し変えれば, RecyclerView等の他のScrollViewにも使えそう(未確認). 下に実際の挙動を示した.



目次


方法

  1. 縦スクロールする場合に対してのみ有効にする.
  2. 現在のFloatingActionButtonのアニメーション状態を4つ, スクロールの状態を5つで管理する.
  3. スクロール時のアニメーションを設定する
  4. 停止時のアニメーションを設定する
  5. FloatingActionButtonのbehaviorを設定する

コード全文

参考にしたサイト


方法


  1. 縦スクロールする場合に対してのみ有効にする.

  2. 1
    2
    3
    4
    5
    6
    7
    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull FloatingActionButton child, @NonNull View directTargetChild,
                                       @NonNull View target, int axes, int type) {
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
                super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
    }

  3. 現在のFloatingActionButtonのアニメーション状態を4つ, スクロールの状態を5つで管理する.

  4. FloatingActionButtonが消えている間は, 押しても反応しないようにする. ここのアニメーションはListenerに, AnimatorListenerAdapterAnimator.AnimatorListenerのインスタンスを代入できればよく, もっと複雑にできるかもしれない. また, アニメーションの時間はそこまで長くしないほうが良い.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    private static final int duration = 200;
     
    ...
     
    // 消失アニメーション
    private void shrinkOut(final FloatingActionButton child) {
     
        child.animate()
                .setDuration(duration)
                .scaleX(0.0f)
                .scaleY(0.0f)
                .setInterpolator(new AccelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        state = STATE.VANISHED;
                        child.setEnabled(false);
                    }
     
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        state = STATE.VANISHING;
                        child.setEnabled(false);
                    }
                });
    }
     
    // 表示アニメーション
    private void expandIn(final FloatingActionButton child) {
     
        child.animate()
                .setDuration(duration)
                .scaleX(1.0f)
                .scaleY(1.0f)
                .setInterpolator(new AccelerateDecelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        state = STATE.APPEARED;
                        child.setEnabled(true);
                    }
     
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        state = STATE.APPEARING;
                        child.setEnabled(false);
                    }
                });
    }

    現在のスクロール状態(scrollingState)と, (状態が変わる)1つ前のスクロール状態(preScrollingState)を保持しておく. 実際の状態管理については, 最後のコード全文を参照
    1
    2
    3
    4
    if (scrollingState != SCROLLING_STATE.STATE) {
        preScrollingState = scrollingState;
    }
    scrollingState = SCROLLING_STATE.STATE;

  5. スクロール時のアニメーションを設定する
  6. 下にスクロールまたは, フリック(以降, スクロールに含める)したときに, FloatingActionButtonを消す. また, FloatingActionButtonが消失している場合, 上にスクロールしたとき再表示する. 4行目から7行目, 23行目から26行目では, 一番下までスクロールしたFloatingActionButtonが消えないようにしている(下のコードでは, 見やすさのためにスクロール状態の管理を省いている).
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
     
        NestedScrollView nestedScrollView = (NestedScrollView) target;
        if (nestedScrollView.getChildAt(0).getMeasuredHeight() - nestedScrollView.getHeight() == nestedScrollView.getScrollY()) {
            return;
        }
     
        if (dy > 0) {
            if (state == STATE.APPEARED) {
                shrinkOut(child);
            }
        } else if (dy < 0){
            if (state == STATE.VANISHED) {
                expandIn(child);
            }
        }
    }
     
    @Override
    public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, float velocityX, float velocityY) {
     
        NestedScrollView nestedScrollView = (NestedScrollView) target;
        if (nestedScrollView.getChildAt(0).getMeasuredHeight() - nestedScrollView.getHeight() == nestedScrollView.getScrollY()) {
            return false;
        }
     
        if (velocityY > 0) {
            if (state == STATE.APPEARED) {
                shrinkOut(child);
            }
        } else if (velocityY < 0) {
            if (state == STATE.VANISHED){
                expandIn(child);
            }
        }
     
        return false;
    }

  7. 停止時のアニメーションを設定する
  8. FloatingActionButtonが消失している場合, 放置していると再表示する.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    private static final int delayDuration = 400;
     
    ...
     
    @Override
    public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull final FloatingActionButton child, @NonNull View target, int type) {
     
        if (state == STATE.VANISHED) {
            if (scrollingState != SCROLLING_STATE.FLUNG_DOWN) {
                expandIn(child);
            } else if (preScrollingState != SCROLLING_STATE.SCROLLED_DOWN){
                expandIn(child);
            }
        } else if (state == STATE.VANISHING) {
            coordinatorLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (state == STATE.VANISHED && scrollingState == SCROLLING_STATE.STOP) {
                        expandIn(child);
                    }
                }
            }, delayDuration);
        }
    }

    上のコードで, 9行目から13行目までは, 以下のような挙動を抑えるために記述している.


    すなわち, スクロール(フリックは含めない)後, フリックすると, イベントとしては, onNestedPreScroll -> onStopNestedScroll -> onNestedPreFlingScroll (コードに関係しないイベントは除く)と, onStopNestedScrollが途中で呼ばれてしまい, 何もしないとここで, 一回FloatingActionButtonが消える.

    また, 15行目から22行目では, 200(=duration)ms未満の下スクロールを行うと, onStopNestedScrollが呼ばれたときに, stateSTATE.VANISHINGのままで, 該当箇所を抜くと, FloatingActionButtonが消えたままとなる(下のgif参照)ので, これを回避するために, そうなったときに, 400(=delayDuration)ms後, FloatingActionButtonを再表示させている.

    放置しているときに, FloatingActionButtonを再表示させたくない場合は, onStopNestedScrollをオーバーライドしなければよい.

  9. FloatingActionButtonのbehaviorを設定する
  10. MainActivityOnCreateで次のようにすればよい.
    1
    2
    3
    CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) floatingActionButton.getLayoutParams();
    p.setBehavior(new VanishFloatingActionButtonBehavior());
    floatingActionButton.setLayoutParams(p);


コード全文


コード全文を掲載する

VanishFloatingActionButtonBehavior.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.NestedScrollView;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
 
public class VanishFloatingActionButtonBehavior extends FloatingActionButton.Behavior {
 
    private static final int duration = 200;
    private static final int delayDuration = 400;
 
    private STATE state = STATE.APPEARED;
    private SCROLLING_STATE preScrollingState = SCROLLING_STATE.STOP, scrollingState = SCROLLING_STATE.STOP;
 
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull FloatingActionButton child, @NonNull View dependency) {
        return super.layoutDependsOn(parent, child, dependency) || dependency instanceof NestedScrollingChild;
    }
 
    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull FloatingActionButton child, @NonNull View directTargetChild,
                                       @NonNull View target, int axes, int type) {
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
                super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
    }
 
    @Override
    public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull final FloatingActionButton child, @NonNull View target, int type) {
 
        if (state == STATE.VANISHED) {
            if (scrollingState != SCROLLING_STATE.FLUNG_DOWN) {
                expandIn(child);
            } else if (preScrollingState != SCROLLING_STATE.SCROLLED_DOWN){
                expandIn(child);
            }
        } else if (state == STATE.VANISHING) {
            coordinatorLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (state == STATE.VANISHED && scrollingState == SCROLLING_STATE.STOP) {
                        expandIn(child);
                    }
                }
            }, delayDuration);
        }
 
        if (scrollingState != SCROLLING_STATE.STOP) {
            preScrollingState = scrollingState;
        }
        scrollingState = SCROLLING_STATE.STOP;
    }
 
    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
 
        NestedScrollView nestedScrollView = (NestedScrollView) target;
        if (nestedScrollView.getChildAt(0).getMeasuredHeight() - nestedScrollView.getHeight() == nestedScrollView.getScrollY()) {
            return;
        }
 
        if (dy > 0) {
            if (scrollingState != SCROLLING_STATE.SCROLLED_DOWN) {
                preScrollingState = scrollingState;
            }
            scrollingState = SCROLLING_STATE.SCROLLED_DOWN;
            if (state == STATE.APPEARED) {
                shrinkOut(child);
            }
        } else if (dy < 0){
            if (scrollingState != SCROLLING_STATE.SCROLLED_UP) {
                preScrollingState = scrollingState;
            }
            scrollingState = SCROLLING_STATE.SCROLLED_UP;
            if (state == STATE.VANISHED) {
                expandIn(child);
            }
        }
    }
 
    @Override
    public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, float velocityX, float velocityY) {
 
        NestedScrollView nestedScrollView = (NestedScrollView) target;
        if (nestedScrollView.getChildAt(0).getMeasuredHeight() - nestedScrollView.getHeight() == nestedScrollView.getScrollY()) {
            return false;
        }
 
        if (velocityY > 0) {
            if (scrollingState != SCROLLING_STATE.FLUNG_DOWN) {
                preScrollingState = scrollingState;
            }
            scrollingState = SCROLLING_STATE.FLUNG_DOWN;
            if (state == STATE.APPEARED) {
                shrinkOut(child);
            }
        } else if (velocityY < 0) {
            if (scrollingState != SCROLLING_STATE.FLUNG_UP) {
                preScrollingState = scrollingState;
            }
            scrollingState = SCROLLING_STATE.FLUNG_UP;
            if (state == STATE.VANISHED){
                expandIn(child);
            }
        }
 
        return false;
    }
 
 
 
    private void shrinkOut(final FloatingActionButton child) {
 
        child.animate()
                .setDuration(duration)
                .scaleX(0.0f)
                .scaleY(0.0f)
                .setInterpolator(new AccelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        state = STATE.VANISHED;
                        child.setEnabled(false);
                    }
 
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        state = STATE.VANISHING;
                        child.setEnabled(false);
                    }
                });
    }
 
    private void expandIn(final FloatingActionButton child) {
 
        child.animate()
                .setDuration(duration)
                .scaleX(1.0f)
                .scaleY(1.0f)
                .setInterpolator(new AccelerateDecelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        state = STATE.APPEARED;
                        child.setEnabled(true);
                    }
 
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        state = STATE.APPEARING;
                        child.setEnabled(false);
                    }
                });
    }
 
    private enum STATE {
        VANISHING,
        VANISHED,
        APPEARING,
        APPEARED
    }
 
    private enum SCROLLING_STATE {
        SCROLLED_DOWN,
        SCROLLED_UP,
        FLUNG_DOWN,
        FLUNG_UP,
        STOP
    }
}

MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        FloatingActionButton floatingActionButton = findViewById(R.id.floatingActionButton);
        CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) floatingActionButton.getLayoutParams();
        p.setBehavior(new VanishFloatingActionButtonBehavior());
        floatingActionButton.setLayoutParams(p);
    }
}


参考にしたサイト




NestedScrollViewで最下部にスクロールしたかを判定する

まず, NestedScrollViewのサイズを取得する方法 :
1
height = nestedScrollView.getChildAt(0).getMeasuredHeight() - nestedScrollView.getHeight();

よって, これとNestedScrollView.getScrollY()を比較すればよい:
1
2
3
if(nestedScrollView.getScrollY() == nestedScrollView.getChildAt(0).getMeasuredHeight() - nestedScrollView.getHeight()) {
    // 最下部にスクロールした時に実行するコード
}


参考にしたサイト



Toolbarの挙動をコードで変える

例えば, 最初の画面ではToolbarを動かさないようにしたいが,次の画面ではToolbarをcollapseさせたい時の方法

方法


android.support.design.widget.CollapsingToolbarLayoutapp:layout_scrollFlags属性をJavaコード中で変更する.
実際には、以下のように行う.
1
2
3
4
5
6
7
8
9
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
 
...
 
CollapsingToolbarLayout collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
AppBarLayout.LayoutParams appBarLayoutParams = (AppBarLayout.LayoutParams)collapsingToolbarLayout.getLayoutParams();
appBarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
toolbarLayout.setLayoutParams(appBarLayoutParams);

setScrollFlagsメソッドに代入する定数は以下の通りで, 複数指定したい場合は, 上のコードのようにビット演算子OR|をはさんでやる.

定数 対応するapp:layout_scrollFlagsの値
SCROLL_FLAG_ENTER_ALWAYS enterAlways
SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED enterAlwaysCollapsed
SCROLL_FLAG_SCROLL scroll
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED exitUntilCollapsed
SCROLL_FLAG_SNAP snap

上の定数は, android.support.design.widget.AppBarLayout.LayoutParamsのフィールドであるが, ToolBarの挙動については, こちらが詳しい.

実装例


FloatingActionButtonを押すことで, Toolbarの挙動をenterAlways <-> scroll|exitUntilCollapsedと相互に変える. 以下のような感じ.

styles.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<resources>
 
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
 
    <style name="CollapsedTextAppearance" parent="@android:style/TextAppearance.Medium">
        <item name="android:textColor">#ffffff</item>
    </style>
 
    <style name="ExpandTextAppearance" parent="@android:style/TextAppearance.Large">
        <item name="android:textColor">#ffffff</item>
        <item name="android:textSize">56sp</item>
    </style>
</resources>

ToolBarをつかうので, アプリケーションテーマとしてNoActionBarを採用している.
また, 11行目から13行目, 15行目から18行目は, ToolBarのタイトル文字がデフォルトでは黒色なので, 見やすい白色にするために追加している(後に, activity_main.xmlのCollaspingToolbarLayoutapp:collapsedTitleTextAppearance, app:expandedTitleTextAppearanceで指定してやる).

activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbarLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:collapsedTitleTextAppearance="@style/CollapsedTextAppearance"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleTextAppearance="@style/ExpandTextAppearance"
            app:layout_scrollFlags="enterAlways"
            app:title="Hello!"
            app:toolbarId="@+id/toolbar">
 
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
 
    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="android.support.design.widget.AppBarLayout$ScrollingViewBehavior">
 
        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="WORLD!"
            android:textAlignment="inherit"
            android:textSize="200sp" />
 
    </android.support.v4.widget.NestedScrollView>
 
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/floatingActionButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:clickable="true"
        android:focusable="true"
        app:srcCompat="@drawable/ic_refresh_white_24dp" />
 
</android.support.design.widget.CoordinatorLayout>

styles.xmlで述べたが, 17行目と19行目でToolBarのタイトルのテキストスタイルを変更している
54行目に出てくる, FloatingActionButtonに使うdrawableリソースはここには載せないが, 適当な画像を使えばよい.

MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.blogspot.ko_ika.changeappbarlayout;
 
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
 
public class MainActivity extends AppCompatActivity {
 
    // status : Toolbarの状態, 0の時, enterAlwaysで, 1の時, scroll|exitUntilCollapsed
    private int status;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        status = 0;
 
        // floatingActionButtonを押したときのイベントを登録
        FloatingActionButton floatingActionButton = findViewById(R.id.floatingActionButton);
        floatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                CollapsingToolbarLayout collapsingToolbarLayout;
                AppBarLayout.LayoutParams appBarLayoutParams;
                AppBarLayout appBarLayout;
                ViewGroup.LayoutParams layoutParams;
                switch (status) {
                    case 0:
                        collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
                        appBarLayoutParams = (AppBarLayout.LayoutParams)collapsingToolbarLayout.getLayoutParams();
                        appBarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
                        collapsingToolbarLayout.setLayoutParams(appBarLayoutParams);
 
                        // AppBar(Toolbar)の高さを192dpにする.
                        appBarLayout = findViewById(R.id.appbar);
                        layoutParams = appBarLayout.getLayoutParams();
                        layoutParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 192, getResources().getDisplayMetrics());
                        appBarLayout.setLayoutParams(layoutParams);
                        status = 1;
                        break;
                    case 1:
                        collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
                        appBarLayoutParams = (AppBarLayout.LayoutParams)collapsingToolbarLayout.getLayoutParams();
                        appBarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
                        collapsingToolbarLayout.setLayoutParams(appBarLayoutParams);
 
                        // AppBar(Toolbar)の高さをwrap_contentにする.
                        appBarLayout = findViewById(R.id.appbar);
                        layoutParams = appBarLayout.getLayoutParams();
                        layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                        appBarLayout.setLayoutParams(layoutParams);
                        status = 0;
                        break;
                }
            }
        });
    }
}

ハイライトしたところで, Toolbarの挙動を変更している.
基本的に, statusで現在の状態を管理している. 詳しい説明は, コード中のコメントを参照.

実装例の最初に挙げたgifを見て気づいた方もいるかもしれないが, このコードの問題として, AppBarLayouheightwrap_contentか, あまり大きくない値を指定するとタイトルが表示されないことが挙げられる(Toolbarapp:titleを設定してないことは関係ない!). この問題は, CollapsingToolbarLayoutを使わないようにすれば回避できる:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="Hello!"
            app:layout_scrollFlags="enterAlways"/>
    </android.support.design.widget.AppBarLayout>
 
    ...
 
</android.support.design.widget.CoordinatorLayout>

しかし, この方法では, Toolbarの挙動をJavaコード中では変えられないっぽく, 自作のカスタムViewを作る等面倒なことをしないといけないみたい(? 現在模索中)

参考にしたサイト




FloatingActionButtonをスクロール中に消す

NestedScrollView を下にスクロールしている間は, FloatingActionButton を表示させないようにする. 少し変えれば, RecyclerView 等の他のScrollViewにも使えそう(未確認). 下に実際の挙動を示した. 目次 ...