StatusBar と ToolBar のあれこれ
今回の目標はコレです。 グラデーションのかかった Toolbar が StatusBar の下に潜り込んで表示されています。 そして、キーボードが表示された際の Activity のリサイズにも対応するようにします。
StatusBar と ActionBar
まずは、何も設定していない時はどうなるか見ていきます。
分かりやすいように EditText に赤い枠線を付けてます。 キーボードが EditText の上に覆いかぶさってしまっています。
これは、AndroidManifest の方で、android:windowSoftInputMode="adjustResize"
を設定すると、キーボードが出現した際に Activity がリサイズされるようになります。
さらに、ActionBar を非表示にします。
Theme.AppCompat.Light.NoActionBar
を Activity の Theme に指定すると非表示にできます。
次に、StatusBar の見た目をカスタマイズしていきたいと思います。 まずは、背景を透明にしていきます。
window.statusBarColor = Color.TRANSPARENT
をすることで実現できます。
これで背景を透明にできました。しかし、これでは StatusBar の背景が白で、アイコンの色と被ってしまい見にくいので、LIGHT_STATUS_BAR にして、アイコンを黒にします。
window.statusBarColor = Color.TRANSPARENT if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val option = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR window.decorView.systemUiVisibility = option }
StatusBar の下に Activity の View を食い込ませる
Activity の表示領域を StatusBar の範囲まで拡大させるためには、systemUiVisibility に対して View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
を設定します。
これは、以下のコードで実現できます。 こちらのサイトがとても参考になりました。
window.statusBarColor = Color.TRANSPARENT if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val option = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE window.decorView.systemUiVisibility = option }
しかし、これだとキーボードが表示された時、 android:windowSoftInputMode="adjustResize"
を設定しているのにも関わらず、
Activity のリサイズが効かなくなってしまいます。
そこで、Activity のルートタグに fitsSystemWindows="true"
を指定することで、キーボードが出現した時に、Activity が必要に応じてリサイズされるようになります。
Toolbar を設置する
ここまで出来たら、次は Toolbar を設置していきます。
<androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@drawable/gradation" app:title="Toolbar" app:titleTextColor="#FFF" />
これを Activity に追加しておきます。
<?xml version="1.0" encoding="utf-8"?> <layout> <LinearLayout 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:background="#00F" android:fitsSystemWindows="true" android:orientation="vertical" tools:context=".MainActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@drawable/gradation" app:title="Toolbar" app:titleTextColor="#FFF" /> <androidx.appcompat.widget.AppCompatEditText android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" android:background="@drawable/frame" android:gravity="top" android:hint="sample strings sample strings sample strings sample strings" android:padding="8dp" tools:ignore="HardcodedText" /> </LinearLayout> </layout>
ちなみに、背景の @drawable/gradation
はこんな感じです。
gradient タグを使ってグラデーションを描きます。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="rectangle"> <gradient android:angle="45" android:centerColor="#FF8C00" android:endColor="#FFEE00" android:startColor="#F60000" /> </shape> </item> </layer-list>
この状態で実行すると、Toolbar にグラデーションが描かれていると思います。 しかし、StatusBar にグラデーションがかかっていません。
これは、fitsSystemWindows="true"
を設定した際に、StatusBar の高さ分だけ Padding が自動的に付与されるからです。
なので、以下の方法でステータスバーの領域までグラデーションをかけようと考えました。
- StatusBar の高さ分だけ Toolbar の高さを伸ばす
- StatusBar の高さ分だけ Toolbar に対してマイナスマージンをかける
StatusBar の高さは ViewCompat.setOnApplyWindowInsetsListener
を使います。今回は汎用性をもたせて拡張関数として定義しました。
fun View.getStatusBarHeightPx(f: ((Int) -> Unit)) { ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> v.setOnApplyWindowInsetsListener(null) f(insets.systemWindowInsetTop) ViewCompat.onApplyWindowInsets(v, insets) } }
private fun resizeToolbar() = with(binding) { root.getStatusBarHeightPx { statusBarHeightPx -> toolbar.updatePadding(top = statusBarHeightPx) toolbar.layoutParams = (toolbar.layoutParams as ViewGroup.MarginLayoutParams).apply { height += statusBarHeightPx updateMargins(top = -statusBarHeightPx) } } }
また、android:clipToPadding="false"
を Activity のレイアウトに追加するのを忘れないでください。マイナスマージンをかけているので、Toolbar を親の View からはみ出して描画されるようにします。これでうまく動くはずです。
最後に、最低限必要なソースコードを載せておきます。
class MainActivity : AppCompatActivity() { private val binding by lazy { DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initStatusBar() setContentView(R.layout.activity_main) resizeToolbar() } private fun initStatusBar() { window.statusBarColor = Color.TRANSPARENT if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val uiOption = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE window.decorView.systemUiVisibility = uiOption } } private fun resizeToolbar() = with(binding) { root.getStatusBarHeightPx { statusBarHeightPx -> toolbar.updatePadding(top = statusBarHeightPx) toolbar.layoutParams = (toolbar.layoutParams as ViewGroup.MarginLayoutParams).apply { height += statusBarHeightPx updateMargins(top = -statusBarHeightPx) } } } } fun View.getStatusBarHeightPx(f: ((Int) -> Unit)) { ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> v.setOnApplyWindowInsetsListener(null) f(insets.systemWindowInsetTop) ViewCompat.onApplyWindowInsets(v, insets) } }
<?xml version="1.0" encoding="utf-8"?> <layout> <LinearLayout 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:background="#00F" android:clipToPadding="false" android:fitsSystemWindows="true" android:orientation="vertical" tools:context=".MainActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@drawable/gradation" app:title="Toolbar" app:titleTextColor="#FFF" /> <androidx.appcompat.widget.AppCompatEditText android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" android:background="@drawable/frame" android:gravity="top" android:hint="sample strings sample strings sample strings sample strings" android:padding="8dp" tools:ignore="HardcodedText" /> </LinearLayout> </layout>
何か、気づいたことや、懸念点あればコメント頂ければと思います。 参考になれば幸いです。
おしまい