위 화면 중에서 상단 Toolbar 구현 방법에 대해 알아보겠습니다.
Material
저는 google의 Material Components에서 제공하는 MaterialToolbar를 사용했습니다. 우선, build.gradle 파일에 관련 라이브러리를 추가해줘야겠죠? 바로 project 단위의 build.gradle 파일에 들어가서 Maven library dependency를 추가해줍니다. 참고링크
buildscript{
...
repositories {
google()
mavenCentral()
}
...
}
그 다음, app 단위의 build.gradle로 들어가서 dependencies 항목에 material 관련 라이브러리를 추가합니다.
dependencies {
...
implementation 'com.google.android.material:material:1.6.1'
...
}
toolbar에 넣을 메뉴를 정의합니다.
우선 res 폴더 하위에 "menu" 폴더를 생성합니다. 그리고 생성한 menu 폴더 안에 toolbar에 넣을 item을 생성합니다.
저는 toolbar에서 우측에 보이는 편집
버튼만 아이템으로 필요하므로, 하나만 정의했습니다. 좌측의 burger 아이콘은 navigationIcon이라는 속성으로 추가할 것이므로 menu 아이템에는 추가하지 않았습니다.
<!-- [menu] > menu_timeline.xml-->
<menu 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">
<item
android:id="@+id/action_refactor"
android:title="@string/action_refactor"
app:showAsAction="always"/>
</menu>
✨ 고려사항 ✨
app:actionViewClass
와 같이 사용되어 연결된 액션뷰를 접었다 펼 수 있다.카카오버스 앱에서 "편집" 아이템은 항상 Toolbar에 표시되기 때문에 always
로 설정했습니다.
이제 Toolbar가 있는 뷰의 xml에 Toolbar 관련 코드를 추가할 차례입니다.
뷰의 전체 레이아웃을 분석하자면, 상단의 Toolbar
, 그리고 그 밑의 Slide View
와 즐겨찾기 Recycler view
가 Linear하게 구성되어 있으므로 가장 밖에 layout은 LinearLayout으로 감싸줍니다. 그래서 전체 코드를 보면 다음과 같이 됩니다.
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/Theme.KakaoBus_Clone.AppBarOverlay">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Widget.MaterialComponents.Toolbar.Primary"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#FFFFFF"
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
app:navigationIcon="@drawable/ic_menu_burger">
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="36dp"
android:background="@drawable/btn_toolbar_search"
android:gravity="start|center_vertical"
android:minHeight="0dp"
android:paddingHorizontal="10dp"
android:text="@string/toolbar_search"
android:textColor="@color/gray_300" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
...
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
AppBarLayout의 theme 속성의 경우, style.xml에 따로 정의해둔 스타일을 입혀줍니다. 별거 없습니다. 그냥 Material에서 제공하는 Appbar 스타일을 상속해줍니다.
<style name="Theme.KakaoBus_Clone.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
그 다음 MaterialToolbar에서 contentInsetStart
관련 속성값을 모두 0dp
로 설정해서 Toolbar의 navigation 아이콘과 검색 버튼 사이의 간격을 조절합니다.
그리고 검색 버튼을 생성해주면 됩니다. 카카오버스의 상단 검색 버튼의 경우 height 값이 일반 버튼보다 작은 것을 알 수 있고, 그냥 text를 넣는다면 버튼의 기본 padding에 의해 텍스트가 잘리는 버그가 나타납니다. 그래서 minHeight
속성값을 0dp로 설정해서 버튼에 기본적으로 존재하는 padding을 없애줍니다.
xml 파일을 만들었으니 이제 뷰를 보여줄 Kotlin 파일을 업데이트 해줄 차례입니다.
메뉴바를 보여줄 때 사용하던 setHasOptions가 deprecated 되었습니다. 그 대신 MenuHost와 MenuProvider를 사용합니다.
MenuProvider를 호스팅하고 추적할 수 있는 클래스입니다. Toolbar를 보여줄 view에 대한 Fragment 파일에서 MenuHost
를 선언합니다.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
...
val menuHost: MenuHost = requireActivity()
...
}
...
}
requireActivity()
는 Fragment에서 제공하는 메서드로 현재 화면에 보여지는 Fragment를 나타냅니다.
MenuProvider
는 app bar에 메뉴 아이템을 보여주기 위해 필요한 인터페이스입니다. Toolbar에 기존에 menu
xml파일에 만들어 둔 아이템을 보여주기 위해, 위에서 선언한 MenuHost에 MenuProvider를 추가해줘야 합니다. 저는 onCreateView 메서드 밖에 따로 함수를 정의했습니다.
private fun initMenu(menuHost: MenuHost) {
menuHost.addMenuProvider(object: MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_timeline, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_refactor -> {
Log.d("DEBUG","press 편집")
true
}
else -> false
}
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
view를 만들 때와 비슷하게 MenuProvider
안의 onCreateMenu 메서드에서 기존에 만들어 둔 menu xml 파일을 inflate 해줍니다. 그 다음 onMenuItemSelected 메서드에서 각 아이템이 클릭되었을 때의 이벤트를 정의해줍니다. 현재는 화면 이동을 구현하는 단계가 아니므로 디버그용으로 Log만 찍어보게 했습니다.
이렇게 함수까지 정의했습니다. 이제 이 함수를 onCreateView 메서드에 사용해야겠죠?
initMenu(menuHost)
를 onCreateView 메서드 안에 적어줍니다.
이제 코드를 실행하면 화면 상단에 Toolbar가 보여질 것입니다.