2016年3月5日土曜日

CollapsingToolbarLayout で status bar を透明にする方法

注意:以下は support library v23.2.0 での動作をもとにしています。

追記
AppBarLayout と CollapsingToolbarLayout にも android:fitsSystemWindows="true" の指定を追加するようにしました。 これにより、v25.0.1, v25.0,0, v24.2.1, v24.2.0, v24.1.1, v24.1.0, v24.0.0, v23.4.0, v23.3.0, v23.2.1 でも動作することを確認してあります。



わかりやすいように

colorPrimary : #ff0000(赤)
colorPrimaryDark : #99ff00ff(マゼンダ)

contentScrim : #990000ff(青)
statusBarScrim : #9900ffff(シアン)

Toolbar の background : #9900ff00(緑)

にしてあります。

普通に実装するとこんな感じになります。 <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" app:contentScrim="#990000ff" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:statusBarScrim="#9900ffff"> <ImageView android:layout_width="match_parent" android:layout_height="360dp" android:scaleType="centerCrop" android:src="@drawable/sample" tools:ignore="ContentDescription" /> <android.support.v7.widget.Toolbar android:id="@id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" tools:background="#9900ff00" /> </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" android:layout_gravity="fill_vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> ... </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout> これを実行するとこうなります。



マゼンダ色の status bar が表示されています。この status bar を透明にして ImageView をその分上にあげるにはどうすればいいか。

そのためにまずテーマに <item name="android:statusBarColor">@android:color/transparent</item> を指定して、Activity の onCreate() に以下の処理を追加します。 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { findViewById(android.R.id.content).setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); } すると次のようになります。



マゼンダから赤色に変わりました。これはステータスバーの色が見えているのではなく CollapsingToolbar の領域が見えています。その証拠にスクロールするとこの領域も移動します。

この領域はどこで確保されているかというと CollapsingToolbarLayout の onLayout() です。 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); ... // Update our child view offset helpers for (int i = 0, z = getChildCount(); i < z; i++) { final View child = getChildAt(i); if (mLastInsets != null && !ViewCompat.getFitsSystemWindows(child)) { final int insetTop = mLastInsets.getSystemWindowInsetTop(); if (child.getTop() < insetTop) { // If the child isn't set to fit system windows but is drawing within the inset // offset it down ViewCompat.offsetTopAndBottom(child, insetTop); } } getViewOffsetHelper(child).onViewLayout(); } ... } CollapsingToolbarLayout の直接の子ビューで fitsSystemWindows が false のものは、ビューの配置位置を insetTop(ステータスバーの高さ)分だけ下にずらすようになっています。
つまり、fitsSystemWindows = true を子ビューにセットすれば、この処理が行われないということです。

そこで ImageView に android:fitsSystemWindows="true" を追加すると <android.support.design.widget.CollapsingToolbarLayout ... > <ImageView ... android:fitsSystemWindows="true" /> <android.support.v7.widget.Toolbar ... /> </android.support.design.widget.CollapsingToolbarLayout> 次のようになります。




この方法を取る場合、4.4 で注意が必要です。 「 StatusBar 透明化の正しい方法」で書いているように、v19 では android:windowTranslucentStatus をセットして、v21 では android:statusBarColor をセットしている場合、上記の対応を入れると次のように 4.4 で余分な領域が確保されてしまいます。



そのため、v21以降だけ android:fitsSystemWindows="true" が指定されるようにします。

values/bools.xml <?xml version="1.0" encoding="utf-8"?> <resources> <bool name="fitsSystemWindowForImage">false</bool> </resources> values-v21/bools.xml <?xml version="1.0" encoding="utf-8"?> <resources> <bool name="fitsSystemWindowForImage">true</bool> </resources> <android.support.design.widget.CollapsingToolbarLayout ... > <ImageView ... android:fitsSystemWindows="@bool/fitsSystemWindowForImage" /> <android.support.v7.widget.Toolbar ... /> </android.support.design.widget.CollapsingToolbarLayout> こうすると、4.4 でも次のような結果になります。




4.4 での実行結果をよく見ると、閉じたときに status bar 分の領域が確保されずに Toolbar が status bar の下にきてしまうことがわかります。



残念ながら 4.4 で閉じた時に status bar 分を確保するシンプルな方法はありません。 以下のように inset を margin に付け替える Toolbar を用意するとこれが解決できます。 public class CustomToolbar extends Toolbar { public CustomToolbar(Context context) { super(context); } public CustomToolbar(Context context, AttributeSet attrs) { super(context, attrs); } public CustomToolbar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected boolean fitSystemWindows(Rect insets) { final ViewGroup.LayoutParams params = getLayoutParams(); if (params instanceof MarginLayoutParams) { ((MarginLayoutParams) params).topMargin = insets.top; } return true; } }





0 件のコメント:

コメントを投稿