본문 바로가기
개발/안드로이드

CollapsingToolbarLayout

by darksilber 2017. 5. 31.
반응형

출처 - http://blog.naver.com/pistolcaffe/221017061017

 

우리는 CollapsingToolbarLayout 을 사용하여 AppBarLayout 과 함께 툴바의 Collapsing , contentScrim, parallax 등의 기능을 구현 할 수 있습니다. 이번 포스 팅은 CollapsingToolbarLayout 에 대해 다뤄 보려고 하는데, 단순 사용법 부터 실제 CollapsingToolbarLayout 의 내부 코드를 살펴 보면서 구조적인 부분과 주의해야 할 점에 대해서 알아보겠습니다.


1. AppBarLayout 와의 의존성

AppBarLayout 이 CoordinatorLayout 의 child 로 사용 되어야 하듯, CollapsingToolbarLayout 은 AppBarLayout 의  child 로 사용 해야 하는데요. (AppBarLayout 분석 글은 아래 링크를 참조 하세요)
http://blog.naver.com/pistolcaffe/221016672922

그 이유는 CollapsingToolbarLayout 은 AppBarLayout 의 offsetChangeListener 기반으로 동작 하기 때문입니다.

@Override protected void onAttachedToWindow() { super.onAttachedToWindow(); // Add an OnOffsetChangedListener if possible final ViewParent parent = getParent(); if (parent instanceof AppBarLayout) { .... if (mOnOffsetChangedListener == null) { mOnOffsetChangedListener = new OffsetUpdateListener(); } ((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener); .... } }

CollapsingToolbarLayout 의 코드 일부분을 가져 왔는데요.
onAttachedToWindow() 에서 parent 가 AppBarLayout 인지 체크 한 후
AppBarLayout 의 offsetChangeListener 를 등록 하고 있습니다.
여기서 우리는 CollapsingToolbarLayout 은 AppBarLayout 의 child 로 사용 되어야 하는 것을 확인 할 수 있습니다.


2. Toolbar 와의 관계

CollapsingToolbar 는 Toolbar 의 wrapper 형태로 구현 되어 있으며 child 로 toolbar 를 추가 할 시 해당 toolbar id 를 입력 하는 속성이 있습니다.

app:toolbarId

그러나 child 에 toolbar 를 추가 하여 사용 할 때 이 속성을 굳이 추가 해주지 않아도 CollapsingToolbarLayout 내부적으로 child view 들 중에 toolbar 가 있는지 찾도록 구현 되어 있습니다.

private void ensureToolbar() { .... // First clear out the current Toolbar mToolbar = null; mToolbarDirectChild = null; // toolbarid 를 명시 하였다면 해당 id 로 toolbar 를 가져옵니다 if (mToolbarId != -1) { // If we have an ID set, try and find it and it's direct parent to us mToolbar = (Toolbar) findViewById(mToolbarId); if (mToolbar != null) { mToolbarDirectChild = findDirectChild(mToolbar); } } // 지정 된 toolbarid 값이 없으면 child 에서 toolbar 를 찾습니다 if (mToolbar == null) { // If we don't have an ID, or couldn't find a Toolbar with the correct ID, try and find // one from our direct children Toolbar toolbar = null; for (int i = 0, count = getChildCount(); i < count; i++) { final View child = getChildAt(i); if (child instanceof Toolbar) { toolbar = (Toolbar) child; break; } } mToolbar = toolbar; } .... }

위의 코드 처럼 toolbar 를 찾아내어 toolbar 와 연관 된 동작을 수행 하게 되는데,
예를들어 CollapsingToolbarLayout 에 있는 속성 중 app:title 가 별도로 지정 되어있지 않으면 Toolbar 에 설정 된 TitleText 를 가져오도록 구현 되어 있습니다. 

if (mToolbar != null) { if (mCollapsingTitleEnabled && TextUtils.isEmpty(mCollapsingTextHelper.getText())) { // If we do not currently have a title, try and grab it from the Toolbar mCollapsingTextHelper.setText(mToolbar.getTitle()); } .... }

주의 해야 할 점은, Developer 사이트에서는 CollapsingToolbarLayout 을 사용 할 경우 Toolbar 안에 child view 를 넣지 말도록 경고 하고 있습니다.

Do not manually add views to the Toolbar at run time.

그 이유는 내부적으로 toolbar 를 찾아낸 뒤 임의의 Dummy view 를 얹어서 collapse 상태에서 title text 의 가용 width/height 를 계산하게 됩니다. 그 후 해당 영역에 별도의 TextHelper 클래스에서 title text 를 draw 하는데, 이때 이미 toolbar 에 다른 child view 가 있다면 그 view 가 계산에 영향을 주게 되어 잘못 된 위치에 title text 가 그려 질 수 있습니다.

예를 들어 아래와 같이 toolbar 내에 textview 를 추가 하였다면

<android.support.design.widget.CollapsingToolbarLayout ....> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" app:popupTheme="@style/AppTheme.PopupOverlay"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView"/> </android.support.v7.widget.Toolbar> </android.support.design.widget.CollapsingToolbarLayout>

아래와 같이 title text 가 Toolbar 의 child 로 추가 한 TextView 옆에 그려지는 문제를 볼 수 있습니다.

따라서 CollapsingToolbarLayout 을 사용 할 때 이 부분을 주의 해주시면 될 것 같습니다.


3. Collapsing Title

Collapsing Title 에서 먼저 살펴 볼 속성은 아래와 같습니다.

app:titleEnabled app:title

CollapsintToolbarLayout 에는 내부적으로 CollapsingTextHelper 라는 title text 를 collapse / expand 상태에 따라 계속해서 draw 하는 Helper 클래스가 있습니다.

위  2개의 속성은  이 Helper 클래스와 관련 지어 생각하면 편한데,
app:title 은 CollapsingTextHelper 에서 draw 하게 될 title text 를 지정 하는 것이고
app:titleEnabled 는 CollapsingTextHelper 에서 draw 할 것 인지 여부를 지정 하는 것으로 생각 해 볼 수 있습니다.

developer 사이트에서는 이 속성 들에 대해 좀 더 단순하게 설명하고 있는데,
이렇게 큰 범위 에서 말씀 드리는 이유는 toolbar 의 title 를 설정 하는 것과 혼동 될 여지가 있기 때문입니다.
(사실 제가 좀 헷갈렸었습니다만..)
위에서 말씀드렸듯이 app:title 속성을 설정 하지 않으면 toolbar 에 설정 된 title 를 가져 오도록 내부적으로 구현 되어 있습니다.

toolbar title 를 설정 하는 방법이 manifest 에 label 을 설정 하거나 getSupportActionBar() 를 통해 setTitle() 메소드를 사용하면 되므로 일반적으로 app:title 속성을 사용하는 경우는 많이 없지만, app:title 에 별도의 text 를 추가 하였다고 가정 해보도록 하죠.

이때 titleEnabled 를 false 로 지정 하면 CollapsingToolbarLayout 에서 별도로 title text 를 draw 하지 않기 때문에 app:title 에 설정 한 text 가 보이지 않게 되고, 이 상태에선 Toolbar 에 설정 된 title text 가 보이게 됩니다.
결론은, app:titleEnabled 를 false 로 설정 하는 것은 Toolbar 의 title text 를 hide 하는 것이 아님을 유념 하시기 바랍니다.


또한 CollapsingToolbarLayout 에는 title 관련  collapse / expand 각각의 상태에 대한
Title Text 속성을 지정 할 수 있는데요. 아래와 같은 속성들이 있습니다.

app:collapsedTitleGravity app:collapsedTitleTextAppearance app:expandedTitleGravity app:expandedTitleMarginBottom app:expandedTitleMarginTop app:expandedTitleMargin app:expandedTitleMarginEnd app:expandedTitleMarginStart app:expandedTitleTextAppearance

각각의 상태에 대한 Gravity 와 TextAppearance 를 설정 할 수 있으며 expand 상태에서는 여백을 줄 수 있 수 있는 관련 속성 들이 존재 합니다.  이 속성들에 대해 짚고 넘어가야 할 부분으로는 TextAppearance 속성은 TextView 에서 사용 할 수 있는 TextAppearance 속성 과 다르게 제한 적인 속성만 지원 한 다는 것입니다.

void setExpandedTextAppearance(int resId) { TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), resId, android.support.v7.appcompat.R.styleable.TextAppearance); if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor)) { mExpandedTextColor = a.getColorStateList( android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor); } if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize)) { mExpandedTextSize = a.getDimensionPixelSize( android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize, (int) mExpandedTextSize); } mExpandedShadowColor = a.getInt( android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowColor, 0); mExpandedShadowDx = a.getFloat( android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDx, 0); mExpandedShadowDy = a.getFloat( android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDy, 0); mExpandedShadowRadius = a.getFloat( android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowRadius, 0); a.recycle(); if (Build.VERSION.SDK_INT >= 16) { mExpandedTypeface = readFontFamilyTypeface(resId); } recalculate(); }

위 부분은 ExpandedTextAppearance 속성을 적용 하는 부분 인데요.
코드에서 볼 수 있듯이 아래의 속성만 TextAppearance 를 통해 적용 할 수 있습니다.

android:textColor android:textSize android:shadowColor android:shadowDx android:shadowDy android:shadowRadius android:fontFamily

그럼 CollapsingToolbarLayout 에 TextAppearance 를 적용 해 보겠습니다.

<style name="ToolbarTitleStyleParent"> <item name="android:fontFamily">casual</item> <item name="android:shadowDx">0.5</item> <item name="android:shadowDy">0.5</item> <item name="android:shadowRadius">0.5</item> <item name="android:shadowColor">#ffffff</item> </style> <style name="CollapsingTitleStyle" parent="ToolbarTitleStyleParent"> <item name="android:textColor">#FF9800</item> </style> <style name="ExpandedTitleStyle" parent="ToolbarTitleStyleParent"> <item name="android:textColor">#D500F9</item> </style>

우선 styles.xml 에 Collapse / Expand 공통으로 사용 될 ToolbarTitleStyleParent style 를 정의 하였고 font, shadow 관련 속성을 추가 하였습니다. 그리고 Collapse 에서 사용 될 style 인 CollapsingTItleStyle 과 Expand 상태에서 사용 할 ExpandedTitleStyle 을 추가 하였습니다. 그 후 정의한 style 을 TextAppearance 로 지정 해 줍니다.

<android.support.design.widget.CollapsingToolbarLayout .... app:collapsedTitleTextAppearance="@style/CollapsingTitleStyle" app:expandedTitleTextAppearance="@style/ExpandedTitleStyle">

그러면 해당 style 로 적용 되는 것을 확인 할 수 있습니다.


3. Scrim

CollapsingToolbarLayout 에서는 contentScrim 과 statusbarScrim 을 지정 할 수 있는데요.
특정 임계치에 해당 하는 값으로 스크롤이 되면 scrim drawable 이 활성화 되어 나타나는 속성 입니다.

contentScrim 에 특정 이미지를 넣고 statusbarScrim 을 투명으로 설정 해 보겠습니다.
먼저 statusbar 에 반투명 효과를 적용 하기 위해 app theme 에 아래 속성을 추가 하였습니다.

<style name="AppTheme.NoActionBar"> .... <item name="android:windowTranslucentStatus">true</item> </style>

이제 contentScrim 에 배경이미지를 넣고 statusbarScrim 을 투명으로 설정 해 보겠습니다.

<android.support.design.widget.CollapsingToolbarLayout android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:collapsedTitleTextAppearance="@style/CollapsingTitleStyle" app:expandedTitleTextAppearance="@style/ExpandedTitleStyle" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:contentScrim="@drawable/testbg" app:statusBarScrim="@android:color/transparent">

그럼 아래와 같이 특정 스크롤 위치에서 scrim 이 visible 되는 것을 확인 할 수 있습니다.

또한 scrim 이 visible / invisible 될 때 Animation Duration 을 설정 하거나,
visible / invisible 이 발생 할 trigger height 값을 설정 할 수도 있습니다.

app:scrimAnimationDuration app:scrimVisibleHeightTrigger

4. CollapseMode

CollapsingToolbarLayout 은 child view 에서 layout_collapseMode 속성을 통해
특정 collapse 기능을 사용 할 수 있습니다.

app:layout_collapseMode //none(off) , pin , parallax

layout_collapseMode 는 세 가지의 속성 값을 지원 하는데 none (off) 는 말 그대로 collapseMode 를 비활성화 하는 것이고,
나머지 pin 과 parallax 속성에 대해 간략하게 살펴 보겠습니다.

* parallax

CollapsingToolbarLayout 은 child view 의 parallax scroll 속성을 지원 합니다.
대게 parallax scroll 기능을 구현 할 때 이동 해야 할 포지션에 특정 multiplier 값을 곱해서 구현이 되는데,
이 multiplier 값을 지정 해 줄 수도 있습니다. (default 는 0.5 입니다)

app:layout_collapseParallaxMultiplier //0.0 ~ 1.0

그럼 parallax 를 사용하는 예제를 한번 보겠습니다.

<android.support.design.widget.CollapsingToolbarLayout .... android:fitsSystemWindows="true" app:contentScrim="@color/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:scaleType="centerCrop" android:src="@drawable/testbg" app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0.3" /> .... </android.support.design.widget.CollapsingToolbarLayout>

우선 contentScrim 에 특정 색상을 추가 하였고, child 로 imageview 를 넣어 보았습니다.
특정 배경 이미지를 넣었으며 collapsemode 는 parallax , mutlplier 는 0.3 으로 지정 하였습니다

그러면 위와 같이 parallax scroll 효과가 적용 된 것을 확인 할 수 있습니다.

* Pin

CollapaseMode 에서 pin 속성은 AppBar 가 스크롤 되어도 특정 위젯을 고정 시킬 때 사용 합니다.
일반적으로 스크롤과 무관하게 toolbar 가 고정 될 수 있도록 toolbar 에 적용 하여 사용하는 경우가 많습니다.
parallax 예제에서 이미 toolbar 에 pin 속성이 적용 되어 스크롤 할때도 항상 상단에 고정 되어 있는 것을 볼 수 있습니다.


이렇게 해서 CollapsingToolbarLayout 에 대해 알아 보았습니다.
이번 포스팅이 보시는 분들에게 유용한 내용이길 바라면서 이상 마치겠습니다.
즐거운 개발 되세요 :)

반응형

'개발 > 안드로이드' 카테고리의 다른 글

Firebase Cloud Messaging(FCM)  (0) 2017.06.26
app:layout_scrollFlags 속성  (0) 2017.05.31
Android ConstraintLayout  (0) 2017.05.31
RxJava, RxAndroid 의 활용 - 1. Retrolambda  (0) 2017.05.30
Retrofit2 + okhttp3 + Rxandroid 사용법  (0) 2017.05.30

댓글