 
                Swift로 MacOS 애플리케이션 만들기 #1 Swift 기초
FE 개발자를 위한 안드로이드 후려치기 #4 바텀시트 만들기
2023-08-06
Explanation
  오늘은 다시 찾아온 주말을 맞이하여, 요즘 적고있는 안드로이드 시리즈 제 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 에서 확인하실 수 있습니다!
| 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로 주어서 가운데 위치하게 하였습니다!
| 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하고 거의 같아요!
| 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
  이라고 하기엔 내용이 너무 적어서.. 바텀시트에 줄수 있는 몇가지 옵션에 대해 적어볼게요!
우선 테마 스타일을 적용하고 수정해줄게요.
우선 스타일 테마의 스타일을 주어볼게요.
| 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’의 세부 스타일에 대해서는 이따가 한번에 이야기할게요.
이제 매니페스트에서 테마를 수정해줄게요.
| 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> | 
여기에서 바로 요부분 입니다.
| 1 2 3 4 5 6 | <!-- AndroidManifest.xml --> <application     android:theme="@style/Theme.Frontend"     ...>     ...   </application> | 
짜잔 이렇게 선언해주면 이제, ‘Theme.MaterialComponents.DayNight.NoActionBar’ 테마를 기반으로 하는 ‘@style/Theme.Frontend’ 스타일이 적용이 되었어요.
이제 마지막으로, 아까 이야기 하려다 말았던 바텀시트 스타일에 대해 적어볼게요.
| 1 2 3 4 5 6 | <!-- 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

첫 번째가 실행한, 두 번째가 ‘behavior_peekHeight’ 높이, 세 번째가 ‘behavior_halfExpandedRatio’ 높이 마지막이 전체 화면으로 늘렸을 때입니다.