Android Design Support Library를 소개합니다
Cover image by Manual
Material Design
머티리얼 디자인은 Google I/O 2014에서 안드로이드 5.0(롤리팝)과 함께 처음 소개된 구글의 디자인 철학이자 가이드라인입니다. 모바일 뿐만 아니라 데스크탑을 아우르는 새로운 디자인 가이드라인을 제시하므로써 이전까지 구글이 내놓은 여러가지 제품에서 다소 제각각이었던 디자인들을 일관된 디자인으로 재편함으로써 통일된 UI/UX를 제공하겠다는 것이 목적입니다.
머티리얼 디자인에서 표면과 그림자는 물리적인 구조를 형성하여, 사용자들이 화면 상의 어떤 부분을 터치할 수 있고 움직일 수 있는지 쉽게 이해할 수 있도록 돕습니다. 현대적인 출판물 디자인 원칙이 반영되어 다른 부가 요소보다 컨텐츠 자체가 강조됩니다. 모든 움직임에는 의미가 있으며, 화면 요소들 간의 관계를 명확히 하고 세세한 디테일을 통해 사용자에게 이러한 관계를 알려줍니다.
머티리얼 디자인에 대한 간단한 설명은 "구글 디벨로퍼 코리아 블로그 - 머티리얼 디자인 (Material Design) 이란?"을, 머티리얼 디자인을 적용하기 위한 가이드라인은 Google Design을 참고하세요.
안드로이드와 머티리얼 디자인
I/O 2014에서 머티리얼 디자인과 함께 공개된 롤리팝에서는 기본적으로 이 새로운 머티리얼 디자인이 적용되어 있습니다.
Android 5.0 Lollipop Showcase from Android Central.
하지만 문제는 이 머티리얼 디자인을 직접 안드로이드 앱 개발에 구현하는 것이 매우 어렵다는 점이었습니다. v7-appcompat, cardview, recyclerview 등 일부 머티리얼 디자인을 적용하기 위한 서포트 라이브러리가 제공되었지만 그 기능이나 요소들이 매우 제한적이어서 별도의 커스텀 라이브러리를 사용해야 했습니다. 심지어 구글에서 만든 앱들조차도 제각각 다른 형태로 머티리얼 디자인을 적용하는 모습을 보여주었습니다.
안드로이드 디자인 서포트 라이브러리
개발자들의 이런 불만을 들어준 것일까요? 구글은 이번 Google I/O 2015에서 머티리얼 디자인을 더욱 쉽게 적용할 수 있는 **'Android Design Support Library'**를 공개했습니다. 덕분에 개발자들은 자신들의 앱에 머티리얼 디자인을 통일된 방식으로 좀 더 쉽게 적용할 수 있게 되었습니다. 이렇게 된 이상, 머티리얼 디자인을 적용하지 않을 이유는 없겠죠?
이제 빨리 이번 안드로이드 디자인 서포트 라이브러리에 포함된 요소들을 차례로 살펴보고, 이들을 어떻게 구현할지 간단히 소개해 보겠습니다.
시작하기
간단합니다. 다른 라이브러리들처럼 gradle
에 dependency를 등록해 줍니다.
compile 'com.android.support:design:22.2.0'
물론 그 전에 SDK Manager를 통해서 Android Support Repository를 꼭 업데이트해야 합니다.
아직도 이클립스를 쓰시는 분들, 이제는 정말 안드로이드 스튜디오, 혹은 안드로이드 스튜디오의 기반이 되는 Intellij로 옮겨가실 때가 되었습니다. 이미 이클립스에 대한 SDK의 공식 지원이 끊긴지도 오래 되었습니다. 여기에서부터 천천히 시작해 보세요.
Snackbar
Snackbar
는 Toast
와 비슷하게 화면 하단에 간단한 메시지를 통해 어떤 동작에 대한 피드백을 줄 수 있는 요소입니다. 대부분의 동작와 사용법은 Toast
와 비슷하지만, 화면 하단에 붙어 표시된다는 점과 메시지와 함께 액션(Action)을 포함할 수 있다는 점에서 차이가 있습니다.
표시되고 나면 사용자들은 (액션이 있는 경우) 액션을 취하거나, 좌우로 스와이프(swipe-to-dismiss)하여 스낵바를 숨길 수 있습니다. 물론 일정 시간이 지나면 스낵바는 자동으로 사라집니다.
구현은 매우 간단합니다.
Snackbar.make(parentLayout, R.string.snackbar_text, Snackbar.LENGTH_LONG)
.setAction(R.string.snackbar_action, myOnClickListener)
.show(); // show 까먹지 마세요!
Toast
랑 무지 비슷하죠? 다만 차이점은 첫번째 인자로 view를 넘겨준다는 점입니다. 화면에 표시되고 있는 view중 아무거나 넣어 주면 Snackbar
가 알아서 적당한 parent view를 찾아 Snackbar를 표시해줍니다.
혹시 기존의 Toast
를 Snackbar
로 옮기는 과정에서 view가 생성되기 전에 Snackbar
를 보여주려고 한다면 당연히 문제가 생기게 됩니다. 이미 Activity
의 View는 만들어졌지만, 내부 Fragment
의 View가 아직 생성되지 않은 경우(ex. onCreate()
) 다음과 같이 content view를 사용할 수 있습니다만, 추천하지는 않습니다. (급한 불을 꺼야 하는 경우에서만 사용하시고, 이 같은 상황에서는 Toast
를 사용하는 것이 더 적합한 것일지도 모릅니다)
Snackbar.make(getActivity().findViewById(android.R.id.content), ...);
만약 Snackbar
에서 좌우로 밀어 숨기기(swipe-to-dismiss)나 위젯 자동 정렬(FloatingActionButton
자동 이동 등)의 기능을 사용하기 위해서는 아래에서 설명할 CoordinatorLayout
을 최상위 레이아웃으로 사용하여야 합니다. 이 부분은 잠시 후 CoordinatorLayout
을 설명하는 부분에서 좀 더 자세히 다루도록 하겠습니다.
TextInputLayout
TextInputLayout
은 기존의 EditText
를 감싸 그 기능을 확장해 한층 더 업그레이드 해 주는 레이아웃입니다. 기존의 EditText
는 글자를 입력하면 Hint Text가 사라지는 것과는 달리, TextInputLayout
을 사용하면 EditText
가 focus 될 때 Hint Text Text가 플로팅 레이블 형태로 좌측상단에 나타나 사용자가 입력하고 있는 내용이 뭔지 알 수 있게 됩니다.
역시나 구현은 매우 간단합니다. 다음과 같이 레이아웃에서 EditText
를 TextInputLayout
으로 감싸줍니다.
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_text_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:hint="@string/hint_email" />
</android.support.design.widget.TextInputLayout>
힌트를 표시하는 것 외에도 setError()
를 호출하여 EditText
아래에 오류 메시지를 표시할 수 있습니다.
setErrorEnabled()
를 호출하면 EditText
아래에 에러 메시지를 표시하기 위한 공간이 할당됩니다. 에러 메시지가 나올 때 갑자기 레이아웃이 바뀌는 것을 방지하기 위해 뷰가 생성될 때 미리 함수를 호출해 놓는 것을 추천합니다.
setErrorEnabled(true);
setError(getString(R.string.text_error_message));
※주의: 라이브러리 버전 22.2.1 이하에서 TextInputLayout
안의 EditText
에 OnFocusChangeListener
를 새로 할당하면 애니메이션이 제대로 되지 않는 문제가 발생하고 있습니다. 다음 버전에서 고쳐진다고 합니다만, 그 때까지는 다음과 같이 기존에 있던 OnFocusChangeListener
를 보존하면서 새로운 OnFocusChangeListener
를 붙여주어야 합니다.
TextInputLayout inputLayout = (TextInputLayout)findViewById(R.id.input_layout);
EditText editText = inputLayout.getEditText();
final OnFocusChangeListener existing = editText.getOnFocusChangeListener();
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View view, boolean focused) {
existing.onFocusChange(view, focused);
// Your custom logic
}
});
Floating Action Button (FAB)
Floating Action Button은 인터페이스의 가능한 동작(ex. 새로운 아이템 목록에 추가)을 나타내는 동그란 버튼 컴포넌트입니다. 기본 색상은 별도의 설정이 없다면 역시 colorAccent
로 적용되고, 2가지 크기(Normal, Mini)를 지원합니다.
2가지 크기의 FAB. Normal(왼쪽)과 Mini(오른쪽). (from Google Material Design Guideline)
- Normal (56dp): 가장 일반적으로 사용되는 크기입니다. 주로 화면 우측 하단이나
AppBar
에 붙어서 사용됩니다. - Mini (40dp): 소형. 화면에 보여지고 있는 다른 구성 요소들과의 시각적 조화(visual continuity)가 중요할 경우 이상적입니다.
뿐만 아니라 FloatingActionButton
은 ImageView
를 확장하기 때문에 XML의 android:src
속성이나 setImageDrawable()
과 같은 메서드를 이용하여 FloatingActionButton
에 표시될 아이콘을 설정할 수 있습니다.
- fabSize: FAB의 크기 설정(
normal
혹은mini
) - backgroundTint: 배경(background)에 적용할 tint의 색상을 설정합니다
- rippleColor: 버튼을 눌렀을 때 나타나는 ripple effect의 색상을 설정합니다
- src: 버튼 안에 사용할 아이콘을 설정합니다
FAB를 레이아웃에 추가하는 것 역시 매우 쉽습니다.
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_plus"
app:fabSize="normal" />
Navigation View
네비게이션 드로워(Navigation Drawer)는 사용자들이 앱 내에서 각기 다른 섹션들을 쉡게 오갈 수 있도록 해 주는 중요한 요소입니다. 이 네비게이션 드로워가 얼마나 일관성 있게 잘 디자인 되어 있는지의 여부가 앱의 편의성을 판가름하는 중요한 요소가 됩니다. 이는 특히 처음 사용하는 사용자에게 매우 크게 작용합니다.
새로 추가된 NavigationView
는 기존의 DrawerLayout
내부에 위치하면서 이런 네비게이션 드로워를 구현하는데 필요한 프레임워크와 함께 메뉴 리소스를 통해 쉽게 네비게이션 메뉴를 설정할 수 있도록 해 줍니다.
우선 다음과 같이 레이아웃을 구성해 줍니다.
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- your content layout -->
<FrameLayout
android:id="@+id/main_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/navigation_header"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>
Navigation View에서 살펴볼 핵심 요소는 2가지가 있습니다.
Header Layout
app:headerLayout
속성은 드로워(Drawer)의 헤더 색션의 레이아웃을 정의하는 데 쓰입니다. 이 부분은 네비게이션 아이템들의 바로 위에 위치하게 되는데요, 다음과 같이 주로 프로필 섹션으로 사용됩니다.
Menu
app:menu
속성은 드로워 내부에 사용될 네비게이션 메뉴를 지정하는 데 사용됩니다. 물론 inflateMenu()
를 통해 코드를 이용해 메뉴를 수동으로 구성할 수도 있습니다.
네비게이션 메뉴를 구성할 때 서브 헤더를 사용해 항목들을 구분할 수도 있습니다. 위 Gmail 앱의 네비게이션을 보면 윗 부분과 달리 아래 All labels 부분에는 서브 헤더가 달려 있는 것을 볼 수 있습니다.
서브 헤더 없이 단순한 형태의 드로어 메뉴를 구성하는 방법은 다음과 같습니다.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<group android:checkableBehavior="single">
<item
android:id="@+id/navigation_item_1"
android:checked="true"
android:icon="@drawable/ic_android"
android:title="@string/navigation_item_1" />
<item
android:id="@+id/navigation_item_2"
android:icon="@drawable/ic_android"
android:title="@string/navigation_item_2" />
</group>
</menu>
이렇게 하면 네비게이션 메뉴들이 단순히 아래로 쭉 나열되는 형태로 보여지게 됩니다. 현재 선택되어 있는 항목은 자동으로 네비게이션 드로어에 강조 표시되므로 사용자가 현재 선택된 탐색 항목이 무엇인지 알 수 있게 됩니다.
반면 서브 헤더를 사용해 네비게이션 항목들을 구분하는 방법은 다음과 같습니다.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<group android:checkableBehavior="single">
<item
android:id="@+id/navigation_subheader"
android:title="@string/nav_header">
<menu>
<!-- Menu items go here -->
<item
android:id="@+id/navigation_sub_item_1"
android:icon="@drawable/ic_android"
android:title="@string/navigation_sub_item_1"/>
<item
android:id="@+id/navigation_sub_item_2"
android:icon="@drawable/ic_android"
android:title="@string/navigation_sub_item_2"/>
</menu>
</item>
</group>
</menu>
네비게이션 드로워 안에 들어가는 메뉴들을 런타임에서 코드를 이용해 수정하는 것도 가능합니다. getMenu()
를 이용해 현재의 Menu
인스턴스를 가져와 수정해 주면 됩니다.
뿐만 아니라 다음 속성들을 이용해 여러가지 추가적인 설정을 할 수 있습니다.
- itemBackground — 메뉴 아이템의 background resource 설정
- itemIconTint — 메뉴 아이템의 아이콘 틴트 색상 설정
- itemTextColor — 메뉴 아이템의 텍스트 색상 설정
네비게이션 드로워의 선택한 항목에 대한 콜백을 받으려면 setNavigationItemSelectedListener()
를 이용해 OnNavigationItemSelectedListener
를 설정해 주면 됩니다. 이를 이용해 사용자가 네비게이션 메뉴를 선택했을 때 원하는 작업을 수행할 수 있습니다.
Note: API21
이상의 기기에서는 NavigationView
가 자동으로 상태 표시줄의 scrim protection 문제를 자동으로 처리하여 네비게이션 드로워 위로 scrim이 오버레이 될 수 있도록 해 줍니다.
TabLayout
TabLayout
은 탭을 기존보다 보다 쉽고 간편하게 구현할 수 있게 도와주는 컴포넌트입니다. 기본적으로 *고정 탭(Fixed tabs)*과 스크롤 탭(Scrollable tabs) 2가지를 모두 지원합니다. 고정 탭에서는 모든 탭이 보기의 너비를 골고루 똑같이 나눠 쓰게 하거나 탭을 가운데 정렬 할 수 있는 반면, 스크롤 탭에서는 탭의 크기가 균일하지 않고 가로 방향으로 스크롤할 수 있습니다.
가장 먼저 레이아웃에 TabLayout
을 추가해 줍니다.
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill" />
여기에는 입맛에 따라 설정해 줘야 할 몇 가지 속성들이 있습니다.
- tabMode: TabLayout의 모드를 설정합니다.
fixed
(모든 탭을 한번에 표시)와scrollable
(일부 탭만 표시하고 나머지는 스크롤)중에서 선택할 수 있습니다. - tabGravity: 탭의 정렬 방식을 선택합니다.
fill
(너비를 모두 같게 나누어 표시)와center
(가운데 정렬)을 사용할 수 있습니다.
또한, TabLayout
에서는 다음과 같은 3가지의 리스너가 사용됩니다. OnTabSelectedListener
를 제외한 나머지 2개는 탭과 함께 ViewPager
를 사용하는 경우에만 필요합니다.
TabLayout.OnTabSelectedListener
: 탭의 선택 상태가 변경될 때 호출되는 리스너입니다.TabLayout.OnPageChangeListener
:ViewPager
에서 페이지의 상태가 변경될 때(스크롤) 이에 따라 탭의 상태도 변경되어야 하기 때문에 필요한 리스너입니다. 이 리스너가ViewPager
에 붙어서 페이지 변경 이벤트를TabLayout
에 전달해 탭의 선택 상태를 동기화 해 주는 역할을 합니다.ViewPager.OnTabSelectedListener
: 위와는 반대로TabLayout
에서 탭을 선택할 때 이에 따라 페이지의 선택 상태도 변경되어야 하기 때문에 필요한 리스너입니다. 이 리스너가 탭의 선택 이벤트를ViewPager
에 전달해 탭과 페이지의 선택 상태가 동기화 될 수 있도록 합니다.
TabLayout
이 레이아웃에 추가되었다면, 탭을 추가하는 것은 이전에 액션바에 탭을 추가하는 방법과 동일합니다. 아래와 같이 코드에서 새 탭을 만들고, setText()
와 setIcon()
메소드를 이용해 탭의 이름과 아이콘을 설정해 줍니다.
물론, 페이지를 넘기는 데 ViewPager
를 사용하는 경우, setupWithViewPager()
를 사용해 이 둘을 간단하게 연결할 수 있습니다.
고맙게도, setupWithViewPager()
는 다음과 같은 일들을 자동으로 수행해 줍니다.
ViewPager.OnPageChangeListener
를 자동으로 등록해ViewPager
의 페이지 변경 이벤트가TabLayout
에 전달될 수 있도록 합니다.- 코드로 일일이 탭을 생성해 주지 않아도
ViewPager
의PagerAdapter
로부터 자동으로 탭을 만들어 줍니다. 만세! (참고:setTabsFromPagerAdapter()
) TabLayout.OnTabSelectedListener
를 자동으로 등록해TabLayout
의 탭 변경 이벤트가ViewPager
에 전달될 수 있도록 합니다.
그래서 다음과 같이 간단한 코드로 TabLayout
와 ViewPager
를 구현할 수 있게 됩니다.
ViewPager pager = (ViewPager)
rootView.findViewById(R.id.viewPager);
pager.setAdapter(new MyPagerAdapter(getActivity().getSupportFragmentManager()));
TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs);
tabLayout.addTab(tabLayout.newTab().setText("Tab One"));
tabLayout.addTab(tabLayout.newTab().setText("Tab Two"));
tabLayout.addTab(tabLayout.newTab().setText("Tab Three"));
tabLayout.setupWithViewPager(pager);
Coordinator Layout
CoordinatorLayout
은 터치나 드래그와 같은 이벤트가 일어날 때 여러 개의 하위(child) view들이 서로 상호 작용을 할 수 있도록 해 주는 레이아웃입니다.
Note: 레이아웃들이 정상적으로 작동할 수 있도록 support library dependency를 최신 버전(22.2.0
이상)으로 업데이트 해 주세요. (ex. RecyclerView 등)
CoordinatorLayout
은 다음과 같은 두 가지 속성을 제공하여 화면에 있는 view들이 다른 view들을 기준(앵커, anchor)으로 하여 상호작용 할 수 있도록 해 줍니다.
layout_anchor
: 다른 view를 anchor로 설정할 수 있습니다.layout_anchorGravity
: 위에서 설정한 anchor를 기준으로 한 gravity를 설정합니다.
FloatingActionButton & Snackbar
앞에서 Snackbar
를 설명할 때 언급했던 Snackbar
와 FloatingActionButton
의 상호작용은 CoordinatorLayout
을 활용한 좋은 예입니다. CoordinatorLayout
을 사용하면 스낵바가 표시될 때 FAB를 가리는 대신 애니메이션 효과와 함께 자동으로 위, 아래쪽으로 이동합니다. 뿐만 아니라 Snackbar
의 swipe-to-dismiss도 사용할 수 있게 됩니다.
이를 구현하기 위해서는 FloatingActionBar
을 CoordinatorLayout
의 바로 하위(child)로 추가하고, layout_gravity
속성을 통해 FAB의 위치를 지정해 주어야 합니다.
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content">
<!-- Your other views -->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_plus"
android:layout_gravity="bottom|right"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
이제 Snackbar
를 보여줄 때 CoordinatorLayout
를 view 파라메터로 넘겨주면 됩니다.
Snackbar.make(mCoordinator, "Your message", Snackbar.LENGTH_SHORT)
.show();
App Bar
CoordinatorLayout
을 활용하면 스크롤에 따른 view의 모양을 변화시킬 수 있습니다. 아주 대표적인 예가 바로 App Bar(예전에 Action bar라고 불리던)입니다. 이미 여러분은 레이아웃에서 Toolbar
를 사용하고 계실 것입니다. 디자인 라이브러리는 이 기능을 한 단계 더 발전시킨 AppBarLayout
를 제공합니다. 이것을 사용하면 Toolbar
와 함께 사용되는 여러가지 레이아웃(예컨대 TabLayout
)들이 (ScrollingViewBehavior
로 표시된) 다른 뷰에서 스크롤 이벤트가 일어날 때 툴바와 어우러져 자연스럽게 반응하도록 할 수 있습니다.
<android.support.design.widget.CoordinatorLayout
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">
<! -- Your Scrollable View -->
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
...
/>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
이제 사용자가 RecyclerView
를 스크롤하면 AppBarLayout
이 스크롤 이벤트에 응답할 수 있게 됩니다.
여기서 살펴볼 점은 두 가지가 있습니다.
먼저 layout_scrollFlags
속성에 스크롤에 반응할 방법인 scroll flag을 설정해 주어야 합니다. 여기에서 화면 밖으로 스크롤 될 때 화면 상단에 남아 있을 지, 아니면 사라질 지를 결정합니다. flag의 예는 다음과 같습니다.
-
scroll: 스크롤 이벤트에 반응할 모든 view에 반드시 이 flag를 설정해야 합니다.
-
enterAlways: 아래쪽 방향으로 스크롤할 때마다 이 보기가 표시됩니다. ('quick return')
-
enterAlwaysCollapsed: 해당 view에
minHeight
속성이 있는 경우, 해당 크기로 시작해 맨 위로 스크롤 될 때만 전체 높이로 확장됩니다. -
exitUntilCollapsed: view가
minHeight
가 될 때 까지만 축소됩니다.
Note: scroll 플래그를 사용하는 모든 보기는 이 플래그를 사용하지 않는 보기보다 먼저 선언해야 합니다. 그래야 모든 view가 맨 위쪽부터 화면 밖으로 스크롤되고 고정된 요소들이 화면에 남도록 할 수 있습니다.
다음으로는 RecyclerView
에 사용된 layout_behavior
속성입니다. 이 속성은 RecyclerView
가 CoordinatorLayout
과 함께 동작하도록 하기 위해서 반드시 필요합니다. 즉, 레이아웃이 RecyclerView
의 스크롤 이벤트에 반응할 수 있게 된다는 것입니다. 우리가 위 코드에서 사용한 레이아웃에서는 Toolbar
에 layout_scrollFlags
속성을 선언해 놓았기 때문에 RecyclerView
에서 스크롤이 일어나면 이 이벤트가 ToolBar
에 전달되어 화면 안 혹은 밖으로 스크롤 되게 됩니다. 반면, TabLayout
에는 이 속성을 선언하지 않았기 때문에 스크롤이 일어나더라도 화면 상단에 남아 있게 되는 것이죠.
Collapsing Toolbar
앞에서 살펴본 것과 같이 Toolbar
를 AppBarLayout
로 감싸 주면 enterAlwaysCollapsed
나 exitUntilCollapsed
같은 flag를 지정해 스크롤 이벤트에 반응할 수 있었습니다. 하지만 스크롤 이벤트에 따라 view들을 축소하거나 하는 등의 상세한 조작은 불가능했는데요, 새로 추가된 CollapsingToolbarLayout
을 사용해 Toolbar
를 감싸 주면 스크롤에 따라 Toolbar
를 확장하거나 축소할 수 있게 됩니다.
<android.support.design.widget.AppBarLayout
android:layout_height="192dp"
android:layout_width="match_parent">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
CollapsingToolbarLayout
과 Toolbar
를 함께 사용하는 경우 좋은 점은 레이아웃이 완전히 표시될 때는 제목이 크게 나타났다가 레이아웃이 축소될 때 애니메이션과 함께 제목이 점점 작아져 기본 크기로 자연스럽게 전환된다는 것입니다. 이를 위해서는 Toolbar
자체가 아닌, CollapsingToolbarLayout
의 setTitle()
을 이용해 제목을 설정해 주어야 합니다.
뿐만 아니라, Collapsing Toolbar가 제대로 작동하기 위해서는 app:layout_collapseMode
속성을 지정해 주어야 하는데요, 여기에는 두 가지 옵션이 있습니다.
- "pin":
CollapsingToolbarLayout
이 완전히 축소되면 툴바는 화면 맨 위에 고정(pinned)되어 보여집니다.
- "parallax": 툴바가 축소되는 동안 Parallax 모드 (예컨대
CollapsingToolbarLayout
안에ImageView
를 사용하는 경우)로 동작하도록 합니다. 옵션으로layout_collapseParallaxMultiplier
속성을 사용하여 transition의 translation multiplier를 설정할 수도 있습니다. (예: app:layout_collapseParallaxMultiplier="0.7") 이때CollapsingToolbarLayout
에app:contentScrim="?attr/colorPrimary"
속성을 사용해 주면 view가 축소될 때 그림 위로 지정한 색상이 오버레이 되면서 자연스럽게Toolbar
로 축소되는 애니메이션을 구사할 수 있습니다. (참고: Android Reference
Wrap Up
이제 더 이상 망설일 필요가 없습니다. 지금 바로 여러분의 앱에 머티리얼 디자인을 적용해 보세요!
아직 잘 감이 안 오신다면 이 페이지를 참고해서 차근차근 따라해 보시고, 최근 버전 22.1 업데이트 되면서 디자인 서포트 라이브러리에 추가된 부분들은 구글 안드로이드 개발자 블로그를 참고해 보세요.
References
Google Developers Blog - Android Design Support Library
Google Developers Korea Blog - Android Design Support Library
Ribot Labs - Exploring the new Android Design Support Library
커니의 안드로이드 이야기 - Support Design Library (Google I/O Extended Seoul, 2015)