본문 바로가기
깡샘 코틀린

12-1 앱바 사용하기

by 농농씨 2023. 6. 30.

머티리얼 디자인은 구글의 디자인 지침이다. 그리고 이 디자인 지침에 맞게 앱을 만드는 여러가지 뷰를 라이브러리로 제공한다. 12장에서는 머티리얼 라이브러리에서 자주 사용하는 앱바 레이아웃, 탭 레이아웃, 내비게이션 뷰, 확장된 플로팅 액션 버튼 등을 소개한다. 상용 수준의 앱을 개발하고자 한다면 11장에서 살펴본 제트팩의 androidx와 마찬가지로 머티리얼 라이브러리도 잘 정리해둬야 한다.

 

 

머티리얼 라이브러리란?

구글의 머티리얼 디자인(material design)은 모바일과 데스크톱, 그리고 그 밖에 다양한 장치를 아우르는 일관된 애플리케이션 디자인 지침이다.(androidx는 기능 위주고 머티리얼은 디자인 중점인듯) 그림자 효과나 물결 모양 효과 등 다양한 뷰를 필요로 하는 효과들을 구현할 수 있도록 구글이 라이브러리를 지원한다.

 

머티리얼 라이브러리를 사용하려면 빌드 그래들 파일의 dependencies 항목에 선언해줘야 한다. 그런데 안드로이드 스튜디오 4.1 버전부터는 프로젝트나 모듈을 만들면 자동으로 머티리얼 라이브러리가 선언된다. 그만큼 많이 이용한다는 뜻이다.

// 머티리얼 라이브러리 선언
implementation 'com.google.android.material:material:1.4.0'

 

 

앱바 레이아웃

앱바(AppBar)란 화면 위쪽의 꾸밀 수 있는 영역을 의미한다. ex. 최근 통화목록에서 위에 '전화' 라고 뜨는 영역.

이 영역은 단순 액션바나 툴바가 아니라 이미지와 문자열로 다양하게 꾸밀 수 있다.

 

앱바를 이용해 화면 위쪽 영역의 크기만 늘릴 수도 있지만 메뉴를 출력하는 툴바를 포함할 수도 있다. 그리고 툴바 이외에 이미지나 문자열을 함께 출력하는 등 앱바를 이용하면 화면의 위쪽영역을 다양하게 꾸밀 수 있다.

머티리얼 라이브러리는 이러한 앱바를 위해 앱바 레이아웃을 제공한다. 앱바 레이아웃도 하나의 뷰이므로 레이아웃 XML 파일에 등록한다.

 

툴바 포함하기

다음은 앱바 레이아웃(AppBarLayout)에 툴바를 포함한 예이다. 이처럼 앱바를 사용할 때는 대부분 앱바 레이아웃 안에 툴바를 포함한다.

// 앱바 레이아웃에 툴바 포함
<com.google.ndroid.material.appbar.appBarLayout // 앱바 영역에
	android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    <androidx.appcompat.widget.Toolbar // 툴바 포함
    	android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="@attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>

실행결과는 상단에 제목이랑 돋보기랑 물음표(아마도 도움말)랑 오버플로우 버튼 등이 뜬다.

 

크기 확장하기

앱바 레이아웃을 사용하면 바뿐만 아니라 여러가지 콘텐츠를 함께 표시할 수도 있다.

예를 들어 다음처럼 layout_height 속성을 이용해 위쪽영역을 크게 만들 수도 있다.

// 위쪽 영역 확장하기
<com.google.android.material.appbar.AppBarLayout
	(...생략...)
    android:layout_height="242dp"> // 앱바 영역 높이 설정
</com.google.android.material.appbar.AppBarLayout>

실행결과는 앱바 부분이 늘어나서 위아래로 넓어진다.

 

이미지 넣기

이처럼 크기만 조절할 수도 있고 다양한 콘텐츠를 함께 출력할 수도 있다. 대표적인 예가 앱바에 이미지를 표시하는 것이다.

// 앱바에 이미지 표시
<com.google.android.material.appbar.AppBarLayout ... 생략 ... >
	<anndroidx.appcompat.widget.Toolbar ... 생략 ... />
    <ImageView ... 생략 ... /> // 이미지 뷰 삽입
</com.google.android.material.appbar.AppBarLayout>

실행해보면 툴바 아래에 이미지가 뜬다.

 

 

코디네이터 레이아웃 - 뷰끼리 상호 작용하기

코디네이터 레이아웃(CoordinatorLayout)은 머티리얼이 아닌 제트팩의 androidx 라이브러리에서 제공하지만, 앱바 레이아웃에서 가장 많이 이용하므로 이곳에서 함께 설명한다.

 

스크롤 연동하기

코디네이터 레이아웃은 뷰끼리 상호 작용해야 할 때 사용한다. 예를 들어 뷰가 스크롤되는 상황을 살펴보자.

한 화면에 위아래로 뷰가 두개 있을때 두개가 동시에 스크롤되게 하고 싶다. 코디네이터 레이아웃을 이용하면 가능하다.

 

뷰 2개를 코디네이터 레이아웃에 넣으면 1️⃣뷰에서 발생한 스크롤 정보를 2️⃣코디네이터 레이아웃이 받아서 3️⃣다른 뷰에 전달해 준다. 이로써 뷰 2개가 함께 스크롤된다. 그렇지만 코디네이터 레이아웃에 뷰를 추가만 한다고 해서 모든 뷰의 스크롤 정보를 공유할 수 있는 것은 아니다. 즉, 코디네이터 레이아웃으로 감싼 모든 자식 뷰끼리 상호작용을 지원하지는 않는다. 자식 뷰끼리 상호작용 하려면 누군가는 코디네이터 레이아웃에 정보를 전달해야 하고, 또 다른 누군가는 그 정보를 받을 수 있어야 한다. 이 부분을 비헤이비어(behavior)라고 하는데 이 비헤비어를 구현해야 뷰끼리 상호작용 할 수 있다

코디네이터 레이아웃으로 스크롤을 연동할 수 있는 뷰는 목록을 표현하는 리사이클러 뷰와 앞에서 소개한 앱바 레이아웃이 대표적이다. 앱바 레이아웃으로 화면 위쪽 영역을 꾸미면 메인콘텐츠 자리가 적어지므로 메인콘텐츠 부분을 스크롤할때 앱바레이아웃도 함께 스크롤되게 만든다. 이때 코디네이터 레이아웃을 사용한다.

 

중첩 스크롤 뷰 사용하기

그런데 코디네이터 레이아웃을 사용하더라도 텍스트 뷰, 이미지 뷰 등은 스크롤을 연동할 수 없다. 만약 이런 뷰에서 발생하는 스크롤을 연동하려면 androidx.core.widget.NestedScrollView를 이용한다. 즉, 코디네이터 레이아웃에 중첩 스크롤 뷰(NestedScrollView)를 포함하고 여기에 텍스트 뷰나 이미지 뷰를 넣으면 해당 뷰에서 발생하는 스크롤 정보를 코디네이터 레이아웃에 전달하여 앱바 레이아웃이 함께 스크롤되게 할 수 있다.

다음 코드는 텍스트 뷰에서 스크롤이 발생할 때 화면 위쪽의 앱바 레이아웃도 함께 스크롤 되게 작성한 예이다. 이를 위해 코디네이터 레이아웃을 선언하고 그 하위에 앱바 레이아웃과 중첩 스크롤 뷰를 배치했다. 텍스트 뷰 자체로는 스크롤 정보를 코디네이터 레이아웃에 전달할 수 없어 중첩 스크롤 뷰로 텍스트 뷰를 감쌌다.

// 중첩 스크롤 뷰
<androidx.coordinatorlayout.widget.CoordinatorLayout ... 생략 ... > // 코디네이터 레이아웃 선언하고
	<com.google.android.material.appbar.AppBarLayout ... 생략 ... > // 하위에 앱바 레이아웃 배치
    	<androidx.appcompat.widget.Toolbar ... 생략 ...
        	app:layout_scrollFlags="scroll|enterAlways" /> // 앱바의 툴바
        <ImageView ... 생략 ...
        	app:layout_scrollFlags="scroll|enterAlways" /> // 앱바의 이미지
    </com.google.android.material.appbar.AppBarLayout>
    <androidx.core.widget.NestedScrollView ... 생략 ... // 중첩 스크롤 뷰도 배치
    	app:layout_behavior_="@string/appbar_scrolling_view_behavior">
        // layout_behavior 속성은 자신의 스크롤 정보를 어느 비헤비어 클래스가 받아서 처리해야 하는지 명시함
        // 여기서는 문자열 리소스를 지정했는데 그 문자열이 클래스명이어서
        // 코디네이터 레이아웃이 중첩 스크롤 뷰 정보를 '앱바 레이아웃의 ScrollingViewBehavior' 클래스에 전달함
        <TextView ... 생략 ... > // 중첩 스크롤 뷰로 텍스트 뷰 감쌈
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

실행 결과는 텍스트 뷰를 위로 스크롤하면 이미지 뷰도 같이 스크롤되면서 영역이 줄어든다.

여기서 app:layout_behaviorapp:layout_scrollFlags가 중요한 설정이다. 중첩 스크롤 뷰의 app:layout_behavior 속성은 자신의 스크롤 정보를 어느 비헤비어 클래스가 받아서 처리해야 하는지를 의미한다. 예에서는 app:layout_behavior 설정값을 문자열 리소스로 지정했는데 이 문자열은 com.google.android.material.appbar.AppBarlayout$ScrollingViewBehavior라는 클래스명이다. 결국 이 설정으로 코디네이터 레이아웃이 중첩 스크롤 뷰 정보를 앱바 레이아웃의 ScrollingViewBehavior 클래스에 전달한다.

 

그리고 app:layout_scrollFlags 속성이 설정된 뷰가 스크롤 정보를 수신해서 함께 스크롤된다. 이 속성은 예에서처럼 앱바 레이아웃에 추가한 개별 뷰에 설정할 수도 있지만 대부분은 다음에 소개하는 컬랩싱 툴바 레이아웃에 추가한다.

 

 

컬랩싱 툴바 레이아웃 - 앱바 접히는 형태 설정하기

컬랩싱 툴바 레이아웃(CollapsingToolbarLayout)은 앱바 레이아웃 하위에 선언하여 앱바가 접힐 때 다양한 설정을 할 수 있는 뷰이다. 에를 들어 앱바가 접힐 때 표시할 제목이나 색상(scrimColor) 등을 지정할 수 있다.

그런데 가장 중요한 설정은 앱바를 어떻게 접을 것인가이다. 앞에서 살펴보았듯이 컬랩싱 툴바 레이아웃을 사용하지 않고도 앱바 레이아웃에 추가한 툴바나 이미지 뷰 등을 접을 수 있다. 그런데 앱바 레이아웃에 여러 개의 뷰를 추가했다면 개별 뷰에 app:layout_scrollFlags 속성을 지정하는 것은 효율적이지 않다.

따라서 보통은 앱바 레이아웃 하위에 CollapsingToolbarLayout을 추가하여 앱바가 스크롤되어 접히거나 나타날 때 어떻게 동작해야 하는지를 설정한다.

// 컬랩싱 툴바 등록
<androidx.coordinatorlayout.widget.coordinatorLayout ... 생략 ... > // 전체를 코디네이터레이아웃으로 감쌈
    <com.google.android.material.appbar.AppBarLayout ... 생략 ...  > // 앱바 레이아웃 하위에
        <com.google.android.material.appbar.CollapsingToolbarLayout ... 생략 ... // 컬랩싱툴바레이아웃 추가함
        // 컬랩싱 툴바 레이아웃 영역 모두 주목!
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginBottom="50dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:title="AppBar Title">
            <ImageView ... 생략 ... // 컬랩싱 하위의 이미지 뷰
                app:layout_collapseMode="parallax" />
            <androidx.appcompat.widget.Toolbar ... 생략 ... // 컬랩싱 하위의 툴바	
                app:layout_collapseMode="pin" />
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    <androidx.recyclerview.widget.RecyclerView ... 생략 ... // 코디네이터레이아웃 하위에 리사이클러 뷰도 추가
    	app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorlLayout>

실행결과: 처음에는 앱바 영역의 이미지 전체가 보이고 타이틀 문자열도 지정한 크기와 위치에 따라 이미지의 왼쪽 아래에 크게 출력된다.

그런데 리사이클러뷰(목록)을 위로 스크롤해서 앱바 영역이 작아지면 툴바만 출력할 정도의 크기로 앱바가 줄어들고 타이틀문자열도 그 크기만큼으로 줄어든다.

전체를 CoordinatorLayout으로 감싸고 그 하위에 앱바 레이아웃과 리사이클러 뷰를 등록했다. 앱바 레이아웃과 리사이클러 뷰의 스크롤을 연동하겠다는 의도이다. 앱바 레이아웃 하위에는 CollapsingToolbarLayout을 선언했고 다시 그 하위에 앱바의 구성 요소인 툴바와 이미지 뷰를 등록한 구조이다.

컬랩싱 툴바 레이아웃의 title 속성으로 앱바의 제목을 설정했으며 expandedTitleMarginStart, expandedTitleMarginBottom 속성으로 앱바가 접히지 않았을 때 제목의 위치를 설정했다. 그리고 앱바가 접히면서 내용이 정상적으로 출력되지 못하는 상황이라면 contentScrim 속성에 지정한 색상으로 앱바를 출력한다.

컬랩싱 툴바 레이아웃 속성으로 앱바의 제목을 지정하면 expandedTitleMarginStart, expandedTitleMarginBottom 속성에 따라 해당 위치에 출력되다가 앱바가 스크롤되면 타이틀 문자열이 함께 스크롤되어 오른쪽 그림(책)처럼 나온다.

 

 

스크롤 설정하기

컬랩싱 툴바 레이아웃에서 가장 중요한 속성은 앱바가 스크롤될지를 설정하는 layout_scrollFlags이다. 스크롤돼야 한다면 scroll값을 지정하고,  | 연산자를 추가해 스크롤 시 어떻게 움직여야 하는지를 설정한다.

  • scroll | enterAlways: 스크롤 시 완전히 사라졌다가 거꾸로 스크롤 시 처음부터 다시 나타난다.
  • scroll | enterAlwaysCollapsed: 스크롤 시 완전히 사라졌다가 거꾸로 스크롤 시 처음부터 나타나지 않고 메인 콘텐츠 부분이 끝까지 스크롤된 다음에 나타난다.
  • scroll | exitUntilCollapsed: 스크롤 시 모두 사라지지 않고 툴바를 출력할 정도의 한 줄만 남을 때까지 스크롤된다.

각 속성값이 어떻게 동작하는지 좀 더 살펴보자.

기본구성은 앱바에 이미지랑 타이틀 문자열 들어가있고 리사이클러 뷰가 Item 1부터 있다고 한다.

// scroll | enterAlways 속성값(완전히 사라졌다가 거꾸로 스크롤시 처음부터 다시 나타남)
app:layout_scrollFlags="scroll|enterAlways"

처음 화면은 다 똑같다. 앱바 이미지 전체 보이고 리사이클러뷰는 그 밑부터 시작한다.

이 화면을 스크롤하면 가운데 그림처럼 앱바는 모두 사라진다.

그리고 다시 거꾸로 스크롤하면 그 즉시 앱바가 함께 스크롤되어 나타난다. 오른쪽 그림을 보면 거꾸로 스크롤할 때 목록에서 item3이 앱바 아래에 있으므로 리사이클러 뷰가 스크롤되지 않고 앱바가 먼저 스크롤된다는 것을 알 수 있다. 앱바의 스크롤이 끝나면 그 다음에 리사이클러 뷰가 스크롤된다.

 

// scroll | enterAlwaysCollapsed 속성(완전히 사라졌다가 거꾸로 스크롤시 메인콘텐츠 부분이 끝까지 스크롤된 다음 나타남)
app:layout_scrollFlags="scroll|enterAlwaysCollapsed"

기본 동작은 enterAlways 속성과 같은데 거꾸로 스크롤할 때만 차이가 있다. 앱바가 화면에서 모두 사라진 후 거꾸로 스크롤할 때 처음에는 앱바가 나타나지 않는다. 즉, 리사이클러 뷰만 먼저 스크롤되고 리사이클러 뷰의 스크롤이 끝난 다음에(Item1까지 모두 출력된 다음에) 앱바가 스크롤되어 나타난다. 

 

// scroll | exitUntilCollapsed 속성값(툴바 영역은 남기고 사라짐)
app:layout_scrollFlags="scroll|exitUntilCollapsed"

오른쪽 그림을 보면 앱바를 끝까지 스크롤하더라도 모두 사라지지 않고 툴바 정도의 크기로 남아서 고정된다는 것을 알 수 있다.

(거꾸로 스크롤하면...?)

 

 

개별 뷰의 스크롤 설정하기

이번에는 layout_collapseMode 속성을 살펴보자. 앞선 예를 보면 툴바와 이미지 뷰에 layout_collapseMode 속성을 지정했다. 이 속성은 앱바를 스크롤할 때 앱바에 포함한 개별 뷰가 어떻게 움직여야 하는지를 설정한다. 앱바 전체의 스크롤 설정은 컬랩싱 툴바 레이아웃의 layout_scrollFlags 속성으로 하고, 그 하위 뷰마다 스크롤 설정은 layout_collapseMode 속성으로 한다. layout_collapseMode 속성에는 pin이나 parallax 값을 지정한다.

  • pin: 고정되어 스크롤되지 않는다.
  • parallax: 함께 스크롤된다.

개별 뷰의 layout_collapseMode 속성값을 pin으로 하면 앱바는 스크롤되지만 뷰는 스크롤되지 않는다. 반면에 parallax로 지정하면 앱바를 스크롤할 때 처음부터 함께 스크롤된다.

// 개별 뷰의 스크롤 설정
<com.google.android.material.appbar.CollapsingToolbarLayout ... 생략 ... 
    app.layout_scrollFlags="scroll|exitUntilCollapsed"> // 앱바 전체 스크롤 설정(툴바영역 남기고 사라짐)
    <ImageView ... 생략 ...
    	app:layout_collapseMode="parallax" /> // 앱바를 스크롤할 때 처음부터 함께 스크롤
    <androidx.appcompat.widget.Toolbar ... 생략 ... 
    	app:layout_collapseMode="pin" /> // 앱바는 스크롤되지만 뷰는 스크롤되지 않음
</com.google.android.material.appbar.CollapsingToolbarLayout>

위 코드는 컬랩싱 툴바 레이아웃의 layout_scrollFlags 속성에 scroll|exitUntilCollapsed를 설정했고, 그 하위 이미지 뷰의 layout_collapseMode 속성은 parallax로, 툴바의 layout_collapseMode 속성은 pin으로 설정했다.

이렇게 하면 앱바를 스크롤 할 때 이미지 뷰는 함께 스크롤되어 사라지지만, 툴바는 전혀 움직이지 않고 고정되며 계속 스크롤해도 사라지지 않는다.

(그럼 툴바영역을 남기도록 앱바 전체 스크롤 설정을 scroll|exitUntilCollapsed로 지정해야만 pin을 쓸수 있는건가....?)

(툴바가 사라지는거랑 앱바가 사라지는거랑 별개인가...?)