프로젝트를 진행하면서 투명 상태 바(transparent status bar)를 적용해야 하는 화면이 있었습니다.
구글링을 해봐도 코드가 deprecated 돼있거나, 적용한 결과물이 생각한 것과 다른 부분이 있어서
삽질을 반복하다가 겨우 알아낸 방법을 공유해 드리고자 합니다!
제가 원하는 것은 위 화면 처럼 지도나 사진이 있는 화면에서 투명한 상태 바를 가지는 것이었습니다.
이를 위해서는 Style XML 파일과 Activity 혹은 Fragment에서 코드를 추가해야 합니다.
프로젝트 생성
Empty Acticity로 프로젝트를 생성 한 뒤 Navigation Component를 이용해 Fragment 두 개를 추가하였습니다.
첫 번째 Fragment에는 두 번째 Fragment로 이동하는 버튼을 추가하였고,
두 번째 Fragment에는 간단한 사진과 상단에 뒤로 가기 아이콘, 하단에는 뒤로 가기 버튼을 추가하였습니다.
앱 화면은 위와 같습니다.액션바와 상태 바의 색깔때문에 앱 화면이 작아 보이고 깔끔해 보이지 않습니다.
상태 바를 흰색으로 변경하고, 두번째 화면에는 투명 상태 바를 적용해 보겠습니다.
Style 수정
투명 상태 바를 적용하기 위해서는 먼저 Style을 수정해야 합니다.
<!-- Base application theme. -->
<style name="Theme.TransparentStatusBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
res -> values -> themes -> themes.xml 파일을 열어 위와 같이
NoActionBar로 수정합니다.
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">@color/white</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
다음으로 statusBarColor는 @color/white로 바꿔주고
windowLightStatusBar를 true로 설정하는 옵션을 추가해줍니다.
위 옵션의 기능은 상태 바 색상이 밝은색 일 때 상태 바에 있는 아이콘들이 잘 보이지 않기 때문에
상태 바 아이콘들의 색을 어둡게 바꿔주는 기능입니다.
* API 30 (Android 11) 추가 대응
API 레벨 30에서 투명 상태 바 적용이 안되는 경우가 발생해서 Style 코드를 추가합니다.
<!-- Status bar color. -->
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast" tools:targetApi="q">false</item>
<item name="android:enforceStatusBarContrast" tools:targetApi="q">false</item>
Style을 수정하고 컴파일 후 앱을 실행하면 위와 같이 변합니다.
액션바를 없애고 상태 바의 색깔을 화면의 색과 일치시켜주기만 해도 굉장히 깔끔해 보이고
화면이 넓어진 느낌입니다.
Kotlin 코드 추가
두 번째 화면에서 상태 바를 투명으로 만들기 위해서 해당 Fragment에서 window 객체를 이용하여 앱을 전체 화면에 그린 후 상태 바 높이와 하단 네비게이션 버튼 높이만큼 padding을 주는 방식을 사용했습니다.
후에 다른 Activity나 Fragment에서 재사용하기 간편하도록 Util 클래스를 생성 후 코틀린 확장 함수 기능을 이용하여 함수를 만들었습니다.
fun Activity.setStatusBarTransparent() {
window.apply {
setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)
}
}
* API 30 (Android 11) 추가 대응
API 레벨 30에서 투명 상태 바 적용이 안되는 경우가 발생해서 kotlin 코드를 추가합니다.
fun Activity.setStatusBarTransparent() {
window.apply {
setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)
}
if(Build.VERSION.SDK_INT >= 30) { // API 30 에 적용
WindowCompat.setDecorFitsSystemWindows(window, false)
}
}
위 함수는 window에 setFlags 함수를 이용하여 전체 화면에 뷰가 그려지게 합니다.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().setStatusBarTransparent() // 상태 바 투명으로 설정(전체화면)
...
Fragment의 onViewCreated( )함수에서 setStatusBarTransparent( ) 함수를 호출합니다.
컴파일 후 앱을 실행하면 위와 같이 뷰가 전체화면에 그려 지게 되므로 상태 바가 투명하게 되었습니다.
그러나 뒤로가기 아이콘이 상태 바와 겹쳐있고 하단 버튼은 네비게이션 버튼 영역과 겹쳐있는 상태입니다.
따라서 각 상태 바와 네비게이션 버튼의 높이만큼 padding을 주도록 하겠습니다.
먼저 상태 바와 네비게이션 버튼의 높이를 알아내기 위한 코드는 앞 코드처럼 Util 클래스에 확장 함수로
작성하였습니다.
fun Context.statusBarHeight(): Int {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return if (resourceId > 0) resources.getDimensionPixelSize(resourceId)
else 0
}
fun Context.navigationHeight(): Int {
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) resources.getDimensionPixelSize(resourceId)
else 0
}
위 코드는 resource에 식별자로 status_bar_height와 navigation_bar_height를 전달하여 해당 리소스의
아이디를 얻은 뒤 높이를 픽셀 크기로 받아와 리턴하는 함수입니다.
<androidx.constraintlayout.widget.ConstraintLayout ...>
<!-- 사진 이미지 -->
<ImageView ... />
<!-- 내부에 constraintlayout을 중첩하여 구성 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/inner_container" ... >
<!-- 뒤로가기 아이콘 -->
<ImageView
android:id="@+id/imageview_back" ... />
<!-- 뒤로가기 버튼 -->
<Button
android:id="@+id/button_back" ... />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
padding을 적용하기 전에 먼저 xml 파일을 수정합니다.
제가 원하는 것은 투명 상태 바가 사진 위에 있고, 나머지 버튼들은 상태 바와 네비게이션 바 사이에
배치하는 것입니다.
이를 위해서 constraintlayout을 중첩으로 구성하여 사진은 바깥 constraintlayout에 배치하고,
나머지 뒤로 가기 아이콘과 뒤로가기 버튼은 안쪽 constraintlayout에 배치하였습니다.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().setStatusBarTransparent()
// 상태 바, 네비게이션 높이 만큼 padding 주기
binding.innerContainer.setPadding(
0,
requireContext().statusBarHeight(),
0,
requireContext().navigationHeight()
)
...
onViewCreated( ) 함수 내에서 setStatusBarTransparent( ) 함수 바로 밑에
padding을 설정하는 함수를 호출합니다.
안쪽에 있는 constraintlayout만 padding을 주면 제가 원하는 대로 사진은 상태 바 뒤에,
나머지 요소들은 상태 바와 네비게이션 바 사이에 위치하게 됩니다.
컴파일한 후 앱을 실행하면 위와 같은 화면이 나옵니다.
상태 바는 투명으로 설정되었고, 뒤로 가기 아이콘과 버튼은 겹치지 않게 잘 배치되었습니다.
여기에서 만약 다른 Fragment로 넘어가면 전체 화면이 설정된 상태에서 넘어가는 것이기 때문에
위에서 발생했던 문제처럼 다른 화면 요소들이 상태 바와 네비게이션 바와 겹치는 현상이 발생합니다.
따라서 Fragment가 종료될 때 전체 화면을 취소해주는 코드를 추가로 넣어줍니다.
Util 클래스에 전체화면 취소를 위해 Flag를 clear 해주는 함수를 작성합니다.
fun Activity.setStatusBarOrigin() {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
}
}
* API 30 (Android 11) 추가 대응
API 레벨 30에서 투명 상태 바 적용이 안되는 경우가 발생해서 kotlin 코드를 추가합니다.
fun Activity.setStatusBarOrigin() {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
}
if(Build.VERSION.SDK_INT >= 30) { // API 30 에 적용
WindowCompat.setDecorFitsSystemWindows(window, true)
}
}
Fragment는 종료 시 onDestroyView( ) 함수를 호출하기 때문에 여기에 전체 화면을 취소하는 함수를 호출합니다.
override fun onDestroyView() {
super.onDestroyView()
// 전체화면 취소
requireActivity().setStatusBarOrigin()
...
결과
컴파일 후 실행하면 위와 같습니다.
저의 목적대로 두 번째 화면에서 투명 상태 바를 적용하였고, 다른 화면에서는 흰색 상태 바를 적용하여
앱이 전반적으로 깔끔해졌고 더 넓어 보이는 효과를 얻었습니다.
전체 코드는 여기에서 보실 수 있습니다.
이것으로 투명 상태 바 및 흰색 상태 바를 적용해보는 것을 마치겠습니다.
안드로이드에서 기본 style 설정만으로도 간단하게 투명 상태 바를 적용할 수 있는 날이 왔으면 좋겠습니다..ㅎㅎ
참고
https://medium.com/androiddevelopers/gesture-navigation-going-edge-to-edge-812f62e4e83e
https://proandroiddev.com/android-full-screen-ui-with-transparent-status-bar-ef52f3adde63
'Android > UI' 카테고리의 다른 글
[Android] 리사이클러뷰(RecyclerView) 사용하기 (with ViewBinding) (3) | 2022.04.19 |
---|