오늘은 다시 찾아온 주말을 맞이하여, 요즘 적고있는 안드로이드 시리즈 제 4탄! 바텀시트 만들기 입니다!
모든 코드는 지난 코드를 기반으로 추가되어서 헷갈리는 부분이 있다면 지난글을 봐주시면 좋을 것 같아요.
3탄: (https://falsy.me/fe-개발자를-위한-안드로이드-후려치기-3-자바스크립트/ )
2탄: (https://falsy.me/fe-개발자를-위한-안드로이드-후려치기-2-자바스크립트/ )
1탄: (https://falsy.me/fe-개발자를-위한-안드로이드-후려치기-1-웹뷰-만들기/ )
작성된 코드는 https://github.com/falsy/blog-post-example/tree/master/android-for-frontend/bottom-sheet/android 에서 확인하실 수 있습니다!
참고: https://medium.com/over-engineering/hands-on-with-material-components-for-android-bottom-sheet-970c5f0f1840
1. 바텀시트 레이아웃(xml) 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- res/lauout/fragment_bottomsheet.xml -->
<? xml version ="1.0" encoding ="utf-8" ?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns: android ="http://schemas.android.com/apk/res/android"
xmlns: app ="http://schemas.android.com/apk/res-auto"
android: layout_width ="match_parent"
android: layout_height ="match_parent"
app: layout_behavior ="com.google.android.material.bottomsheet.BottomSheetBehavior" >
<TextView
android: id ="@+id/textView"
android: layout_width ="match_parent"
android: layout_height ="400dp"
android: gravity ="center"
android: text ="Hello World"
android: textSize ="18sp"
app: layout_constraintBottom_toBottomOf ="parent"
app: layout_constraintEnd_toEndOf ="parent"
app: layout_constraintHorizontal_bias ="0.0"
app: layout_constraintStart_toStartOf ="parent"
app: layout_constraintTop_toTopOf ="parent"
app: layout_constraintVertical_bias ="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
layout_behavior 속성을 ‘com.google.android.material.bottomsheet.BottomSheetBehavior’로 선언해 주었어요! 그리고 간단하게 텍스트 뷰를 하나 넣어줬어요. height는 400dp에 gravity를 center로 주어서 가운데 위치하게 하였습니다!
2. 메인 액티비티 레이아웃(xml) 수정하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- res/lauout/fragment_bottomsheet.xml -->
<? xml version ="1.0" encoding ="utf-8" ?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools: context =".MainActivity" >
<Button
android: id ="@+id/button"
android: layout_width ="wrap_content"
android: layout_height ="wrap_content"
android: text ="open bottom sheet"
app: layout_constraintBottom_toBottomOf ="parent"
app: layout_constraintEnd_toEndOf ="parent"
app: layout_constraintHorizontal_bias ="0.5"
app: layout_constraintStart_toStartOf ="parent"
app: layout_constraintTop_toTopOf ="parent"
app: layout_constraintVertical_bias ="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
이전 글들의 메인 xml하고 거의 같아요!
3. 메인 액티비티에 바텀시트 프래그먼트 추가하기
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
// java/com.example.frontend/MainActivity
package com . example . frontend
import androidx . appcompat . app . AppCompatActivity
import android . os . Bundle
import android . view . LayoutInflater
import android . view . View
import android . view . ViewGroup
import android . widget . Button
import com . google . android . material . bottomsheet . BottomSheetDialogFragment
class ModalBottomSheet : BottomSheetDialogFragment ( ) {
override fun onCreateView (
inflater : LayoutInflater ,
container : ViewGroup ? ,
savedInstanceState : Bundle ?
) : View ? {
val view : View = inflater . inflate ( R . layout . fragment_bottomsheet , container , false )
return view
}
}
class MainActivity : AppCompatActivity ( ) {
override fun onCreate ( savedInstanceState : Bundle ? ) {
super . onCreate ( savedInstanceState )
setContentView ( R . layout . activity_main )
val button : Button = findViewById ( R . id . button )
val modalBottomSheet = ModalBottomSheet ( )
button . setOnClickListener {
modalBottomSheet . show ( supportFragmentManager , "Open Bottom Sheet" )
}
}
}
딱 봐도 코드가 엄청 간단하죠!
ModalBottomSheet 라는 프래그먼트를 만들어주고, 버튼에 클릭이벤트를 추가해서 실행시켜주었어요.
여기서 inflater는 xml로 정의된 View를 실제 객체화 시켜주는 거라고 해요!
참고: https://dev-cini.tistory.com/29
끝
이라고 하기엔 내용이 너무 적어서.. 바텀시트에 줄수 있는 몇가지 옵션에 대해 적어볼게요!
우선 테마 스타일을 적용하고 수정해줄게요.
5. 바텀시트 옵션
우선 스타일 테마의 스타일을 주어볼게요.
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
<!-- res/lauout/values/themes.xml -->
<resources xmlns: tools ="http://schemas.android.com/tools" >
<!-- Base application theme. -->
<style name ="Theme.Frontend" parent ="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name ="colorPrimary">@color/purple_500</item>
<item name ="colorPrimaryVariant">@color/purple_700</item>
<item name ="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name ="colorSecondary">@color/teal_200</item>
<item name ="colorSecondaryVariant">@color/teal_700</item>
<item name ="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name ="android : statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name ="bottomSheetDialogTheme">@style/ModalBottomSheetDialog</item>
</style>
<style name ="ModalBottomSheetDialog" parent ="ThemeOverlay.MaterialComponents.DayNight.BottomSheetDialog">
<item name ="bottomSheetStyle">@style/ModalBottomSheet</item>
</style>
<style name ="ModalBottomSheet" parent ="Widget.MaterialComponents.BottomSheet.Modal">
<item name ="behavior_peekHeight">100dp</item>
<item name ="behavior_halfExpandedRatio">0.4</item>
<item name ="behavior_fitToContents">false</item>
</style>
</resources>
간단하게, 테마 이름을 정해주고 ‘Theme.Frontend’ 부모 태마를 ‘Theme.MaterialComponents.DayNight.NoActionBar’로 정해줬어요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- res/lauout/values/themes.xml -->
<resources xmlns: tools ="http://schemas.android.com/tools" >
<style name ="Theme.Frontend" parent ="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name ="bottomSheetDialogTheme">@style/ModalBottomSheetDialog</item>
</style>
...
<style name ="ModalBottomSheetDialog" parent ="ThemeOverlay.MaterialComponents.DayNight.BottomSheetDialog">
<item name ="bottomSheetStyle">@style/ModalBottomSheet</item>
</style>
<style name ="ModalBottomSheet" parent ="Widget.MaterialComponents.BottomSheet.Modal">
<item name ="behavior_peekHeight">100dp</item>
<item name ="behavior_halfExpandedRatio">0.4</item>
<item name ="behavior_fitToContents">false</item>
</style>
</resources>
대략 ‘bottomSheetDialogTheme > ModalBottomSheetDialog > ModalBottomSheet’ 순서로 이어져있죠??
‘ModalBottomSheet’의 세부 스타일에 대해서는 이따가 한번에 이야기할게요.
6. 테마 스타일 적용하기
이제 매니페스트에서 테마를 수정해줄게요.
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
<!-- AndroidManifest.xml -->
<? xml version ="1.0" encoding ="utf-8" ?>
<manifest xmlns: android ="http://schemas.android.com/apk/res/android"
xmlns: tools ="http://schemas.android.com/tools" >
<application
android: allowBackup ="true"
android: dataExtractionRules ="@xml/data_extraction_rules"
android: fullBackupContent ="@xml/backup_rules"
android: icon ="@mipmap/ic_launcher"
android: label ="@string/app_name"
android: roundIcon ="@mipmap/ic_launcher_round"
android: supportsRtl ="true"
android: theme ="@style/Theme.Frontend"
tools: targetApi ="31" >
<activity
android: name =".MainActivity"
android: exported ="true" >
<intent-filter>
<action android: name ="android.intent.action.MAIN" />
<category android: name ="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android: name ="android.app.lib_name"
android: value ="" />
</activity>
</application>
</manifest>
여기에서 바로 요부분 입니다.
<!-- AndroidManifest.xml -->
<application
android: theme ="@style/Theme.Frontend"
. . . >
...
</application>
짜잔 이렇게 선언해주면 이제, ‘Theme.MaterialComponents.DayNight.NoActionBar’ 테마를 기반으로 하는 ‘@style/Theme.Frontend’ 스타일이 적용이 되었어요.
7. 바텀시트 스타일
이제 마지막으로, 아까 이야기 하려다 말았던 바텀시트 스타일에 대해 적어볼게요.
<!-- res/lauout/values/themes.xml -->
<style name ="ModalBottomSheet" parent ="Widget.MaterialComponents.BottomSheet.Modal">
<item name ="behavior_peekHeight">100dp</item>
<item name ="behavior_halfExpandedRatio">0.4</item>
<item name ="behavior_fitToContents">false</item>
</style>
‘behavior_peekHeight’: 뷰가 접힌 상태의 높이 값입니다. 음.. 제가 적은 샘플에서는 조금 헷갈릴 수도 있는데, 저는 버튼을 클릭했을때 바텀싯이 활성화 되도록 했으니까. behavior_peekHeight 값이 초기 높이값? 정도로도 생각할 수 있을 거 같아요.
‘behavior_fitToContents’: 기본값을 true이고 뷰 영역이 컨텐츠의 영역으로 정해지게 할지?! 정하는 설정으로 보여요. false로 설정하게 되면, 이후 설명할 ‘behavior_halfExpandedRatio’ 에 설정한 값으로 중간 단계의 뷰 영역 단계를 가집니다.
(true로 설정할 경우 ‘behavior_halfExpandedRatio’ 속성이 무시됩니다.)
‘behavior_halfExpandedRatio’: 앞서 설명한 ‘behavior_fitToContents’이 false 일때, 중간 단계의 뷰 높이값을 설정합니다. 기본값은 0.5로 화면의 절반입니다.
사실 몇가지 옵션이 더 있는데, 제가 다 써보지 못해서… 그 밖의 옵션들은 아래에서 확인 할 수 있어요~
https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetBehavior
8. 실행
첫 번째가 실행한, 두 번째가 ‘behavior_peekHeight’ 높이, 세 번째가 ‘behavior_halfExpandedRatio’ 높이 마지막이 전체 화면으로 늘렸을 때입니다.
끝