블로그가 이전되었습니다.

http://blog.bsk.im/2015/08/18/introducing-android-design-support-library/ 에서 확인해 주세요.


Cover image by Manual


Material Design

머티리얼 디자인은 Google I/O 2014에서 안드로이드 5.0(롤리팝)과 함께 처음 소개된 구글의 디자인 철학이자 가이드라인입니다. 모바일 뿐만 아니라 데스크탑을 아우르는 새로운 디자인 가이드라인을 제시하므로써 이전까지 구글이 내놓은 여러가지 제품에서 다소 제각각이었던 디자인들을 일관된 디자인으로 재편함으로써 통일된 UI/UX를 제공하겠다는 것이 목적입니다.

저는 머티리얼 디자인을 매우 좋아합니다. 플랫폼에 상관없이 매우 일관성 있는 디자인 경험을 제공할 뿐만 아니라, 가이드라인이 잘 제시되어 있어 적용하기 쉽고, 미려하기 때문이죠.

머티리얼 디자인에서 표면과 그림자는 물리적인 구조를 형성하여, 사용자들이 화면 상의 어떤 부분을 터치할 수 있고 움직일 수 있는지 쉽게 이해할 수 있도록 돕습니다. 현대적인 출판물 디자인 원칙이 반영되어 다른 부가 요소보다 컨텐츠 자체가 강조됩니다. 모든 움직임에는 의미가 있으며, 화면 요소들 간의 관계를 명확히 하고 세세한 디테일을 통해 사용자에게 이러한 관계를 알려줍니다.

"구글 디벨로퍼 코리아 블로그 - 머티리얼 디자인 (Material Design) 이란?"

머티리얼 디자인에 대한 간단한 설명은 "구글 디벨로퍼 코리아 블로그 - 머티리얼 디자인 (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에는 액션을 추가할 수 있고, 나타나고 사라질때 애니메이션 효과가 적용됩니다. 
from Google Material Design Guideline


물론 액션 없이 그냥 메시지만 표시할 수도 있습니다. 
from Google Material Design Guideline

구현은 매우 간단합니다.

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가 플로팅 레이블 형태로 좌측상단에 나타나 사용자가 입력하고 있는 내용이 뭔지 알 수 있게 됩니다.

미려한 애니메이션은 덤입니다. 강조색은 별도의 설정이 없으면 colorAccent로 설정됩니다.

역시나 구현은 매우 간단합니다. 다음과 같이 레이아웃에서 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
    }
});

참고: http://stackoverflow.com/questions/31197312/textinputlayout-not-animating-when-onfocuschangelistener-applied-to-wrapped-edit

Floating Action Button (FAB)

레퍼런스 / 디자인 가이드

 
A floating action button recentering a map view. 
from Google Material Design Guideline

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 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)의 헤더 색션의 레이아웃을 정의하는 데 쓰입니다. 이 부분은 네비게이션 아이템들의 바로 위에 위치하게 되는데요, 다음과 같이 주로 프로필 섹션으로 사용됩니다.

프로필 드로워 헤더 섹션 (from Google Material Design Guideline)

app:menu 속성은 드로워 내부에 사용될 네비게이션 메뉴를 지정하는 데 사용됩니다. 물론inflateMenu() 를 통해 코드를 이용해 메뉴를 수동으로 구성할 수도 있습니다.

안드로이드 Gmail 앱의 내비게이션 드로워 (from Google Play Store)

네비게이션 메뉴를 구성할 때 서브 헤더를 사용해 항목들을 구분할 수도 있습니다. 위 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를 설정해 주면 됩니다. 이를 이용해 사용자가 네비게이션 메뉴를 선택했을 때 원하는 작업을 수행할 수 있습니다.

NoteAPI21 이상의 기기에서는 NavigationView가 자동으로 상태 표시줄의 scrim protection 문제를 자동으로 처리하여 네비게이션 드로워 위로 scrim이 오버레이 될 수 있도록 해 줍니다.

TabLayout

레퍼런스 / 디자인 가이드

 
탭 터치 애니메이션 (from Google Material Design Guideline)

TabLayout은 탭을 기존보다 보다 쉽고 간편하게 구현할 수 있게 도와주는 컴포넌트입니다. 기본적으로 고정 탭(Fixed tabs)과 스크롤 탭(Scrollable tabs) 2가지를 모두 지원합니다. 고정 탭에서는 모든 탭이 보기의 너비를 골고루 똑같이 나눠 쓰게 하거나 탭을 가운데 정렬 할 수 있는 반면, 스크롤 탭에서는 탭의 크기가 균일하지 않고 가로 방향으로 스크롤할 수 있습니다.

고정 탭, 균일한 너비 (from Google Material Design Guideline)

고정 탭, 가운데 정렬 (from Google Material Design Guideline)

 
Google Play Music 앱의 스크롤 탭 (from Google Material Design Guideline)

가장 먼저 레이아웃에 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.OnPageChangeListenerViewPager에서 페이지의 상태가 변경될 때(스크롤) 이에 따라 탭의 상태도 변경되어야 하기 때문에 필요한 리스너입니다. 이 리스너가 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도 사용할 수 있게 됩니다.

CoordinatorLayout을 이용하면 Snackbar를 표시할 때 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이 스크롤 이벤트에 응답할 수 있게 됩니다.

 
(from Google Material Design Guideline)

여기서 살펴볼 점은 두 가지가 있습니다.

먼저 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)


WRITTEN BY
편지함
The Base Code of the Human Race

,

얼마전 이통3사의 갤럭시S3 젤리빈 4.3 업데이트가 있었습니다. 갤럭시S3나 노트 3는 젤리빈 4.2를 건너뛰고 4.1에서 4.3으로 바로 업데이트 되기 때문에 체감적으로 많은 성능 향상과 기능 추가가 있었습니다. 그 중 하나가 락스크린 바로가기 기능인데, 이상하게도 해외판 펌웨어에서는 락스크린 바로가기 기능이 사용 가능하지만 국내판 펌웨어에서는 관련 기능이 설정 항목에서 숨겨져 있었습니다. 다행히도 관련 기능이 숨겨져 있을 뿐, 삭제된 것은 아니라서 간단한 방법을 통해 이를 활성화 할 수 있습니다.

 

갤럭시S3 LTE 젤리빈 4.3 펌웨어에서 잠금화면 바로가기를 설정한 모습

 


※ 갤럭시 시리즈 히든 설정들에 간편하게 접근할 수 있는 어플이 마켓에 올라왔습니다. 런쳐를 통한 설정이 귀찮으신 분들은 어플을 다운받아 설정하시면 됩니다.

https://play.google.com/store/apps/details?id=uz.dev.app.galaxy.hidden
 
갤럭시 시리즈 젤리빈 4.3 이상에서 락스크린 바로가기 사용하기

 

1. 우선 액티비티 바로가기를 생성할 수 있는 런쳐를 준비합니다. 노바 혹은 아펙스 런쳐가 이를 지원합니다. 구글 플레이나 기타 마켓을 통해 런쳐를 설치합니다.

노바 런쳐: 구글 플레이 https://play.google.com/store/apps/details?id=com.teslacoilsw.launcher

아펙스 런쳐: 구글 플레이 https://play.google.com/store/apps/details?id=com.anddoes.launcher

 

2. 런쳐를 실행해 홈 화면의 빈 공간을 길게 눌러줍니다. 홈 화면에 추가할 항목을 선택할 수 있는 팝업이 나타납니다. 여기서 [바로가기] 항목을 선택합니다.


 

3. 여러가지 항목들 중에서 [액티비티(Activity)] 항목을 선택합니다.


 

4. 기기에 설치된 앱들의 목록이 나오고 해당 앱을 선택하면 관련 액티비티들의 목록이 나타납니다. 여기서는 환경설정의 액티비티가 필요하므로 아래쪽에 있는 환경설정을 찾아 터치해 줍니다. 여러가지 액티비티 중 "바로가기(.lockscreenshortcut.LockScreenShortcutSettings)"를 선택합니다.


 

5. 홈 화면에 생성된 바로가기를 터치해 숨겨진 설정 화면으로 들어갑니다.


 

6. 좌측 상단의 토글 스위치를 터치해 바로가기를 활성화 해 줍니다. 이제 자신의 기호에 맞게 잠금화면 바로가기를 설정해 줍니다. +버튼을 눌러 앱을 추가할 수 있고, 길게 눌러 드래그하면 위치를 수정하거나 삭제할 수 있습니다. 


 

7. 설정을 마치고 잠금화면을 띄워 보면 화면 하단에 바로가기가 활성화 된 것을 확인할 수 있습니다.


 



WRITTEN BY
편지함
The Base Code of the Human Race

,
안드로이드와 루팅

저는 디자이어 이후로 스마트폰은 쭉 안드로이드를 사용해 왔습니다. 중간에 아이팟 터치를 통해 iOS를 경험해 보면서 정말 많은 앱을 구매했고 탈옥와 시디아를 경험해 봤지만, 안드로이드의 개방성과 강력한 공유 기능은 iOS가 샌드박스 구조인 이상 절대 따라올 수 없는 부분이기 때문이었습니다. 디자이어와 갤럭시 노트1 해외판을 사용하면서 받았던 XDA의 강력한 지원은 정말 이루 말할 수 없는 강력함임을 써보셨던 분들을 아실 겁니다. 커스텀의 끝은 순정이라는 말이 있듯이, 이제는 왠만한 커스텀은 다 해 봤기 때문에 런쳐를 바꾸는 등의 작업 외에는 순정 상태를 고집하고 있습니다. 다만, 바뀌지 않는 것이 있다면 iOS를 쓸 때 탈옥을 했던 것처럼, 항상 루팅을 합니다. 필요 없는 통신사 앱을 제거하기 위해서기도 합니다만, 디자이어 때부터 티타늄 백업을 통해 쌓아 온 백업 데이터 때문에 절대 루팅을 포기할 수 없게 되었습니다.

젤리빈 4.3 업데이트, 루팅, 그리고 'KNOX'

바로 어제(11월 13일) 이통3사 갤럭시S3의 젤리빈 4.3 업데이트가 있었습니다. 3G용 펌웨어에는 포함되어 있지 않으나, LTE용 펌웨어에는 삼성의 보안 솔루션인 KNOX가 포함되어 있습니다. 최근 출시된 갤럭시 노트3와 갤럭시 노트 10.1(2014), 그리고 앞으로 출시될 삼성 안드로이드 기기들에는 아마 기본적으로 KNOX가 탑재될 것으로 보입니다. 그런데 KNOX가 탑재된 기기에서 기본의 방법으로 루팅을 하는 경우 KNOX 워런티가 손상되어 KNOX를 사용할 수 없음은 물론, 보증 기간 내에 무상수리를 받을 수 없게 됩니다. 또한 순정 상태이더라도 녹스가 적용되어 있을 경우 녹스가 적용되지 않는 펌웨어로의 다운그레이드는 불가능합니다.

방패가 있으면 창도 있듯이, 갤럭시 노트3와 갤럭시S4는 이미 KNOX를 우회해 루팅할 수 있는 툴(http://www.kingoapp.com/android-root.htm, http://www.mgyun.com/vroot)이 속속들이 나오고 있습니다. 루팅 툴이 작동하지 않는다면 사용자들이 수정한 녹스 우회 루팅 펌웨어를 사용해도 됩니다. 하지만 아직 갤럭시S3 LTE 버전의 루팅 툴이 없는데다, 녹스 우회 펌웨어들도 아직 만들어지지 않았기 때문에 직접 루팅을 시도해 보기로 했습니다.

※ 현재 U+용 갤럭시S3 LTE 버전의 녹스 제외 및 CWM 리커버리 적용 펌웨어는 맛클에서 구할 수 있습니다.(http://www.matcl.com/?m=bbs&bid=imbeded&uid=205623)

갤럭시S3 3G버전은 4.3 펌웨어에 KNOX가 포함되어 있지 않으므로 기존의 루팅 방법을 사용하시면 됩니다.


갤럭시S3 LTE버전 젤리빈 4.3 루팅하기

2013.11.20 업데이트: Vroot를 통한 녹스 우회 루팅이 가능하다는 의견이 있었습니다. Vroot를 다운받으시거나 S_papa님 블로그(http://spapa1004.tistory.com/106)에서 한글화 파일을 받으시기 바랍니다.
Vroot 다운로드 링크: http://www.mgyun.com/vroot

※ 아래 방법으로 펌웨어 업데이트를 진행하기 전에 반드시 백업을 하시기 바랍니다.

※ 이 포스트에서 소개하는 모든 작업으로 인해 발생하는 문제는 전적으로 사용자에게 있습니다. 펌웨어를 다루는 데 익숙하신 분들만 참고하시기 바라며, 정상적인 플래시 과정에서도 문제가 생길 수 있다는 점 항상 유의하시기 바랍니다.

※ 아래 방법은 커스텀 바이너리 카운트를 증가시킵니다. 트라이앵글 어웨이를 사용해 지울 수 있으나, 이에 익숙하지 않으신 분은 루팅카운트가 올라가지 않는 사용자 펌웨어를 기다리시기 바랍니다.

※ 루팅카운트가 올라가지 않는 커스텀 펌웨어를 만드는 방법은 추후 포스팅 하겠습니다. 방법을 아시는 분들은 sboot.bin 파일을 삭제 후 다시 묶어 주시면 됩니다.

기본적인 원리는 공개된 4.3 순정 펌웨어에서 녹스 관련 파일을 제거하는 것입니다. (몸소 녹스 워런티를 확인해 주신 맛클 S_papa님께 감사드립니다. http://www.matcl.com/?m=bbs&bid=usermoim&uid=203350)

이 방법을 사용하는 경우 녹스는 사용할 수 없으며, 다운로드 모드에서 녹스 워런티는 나타나지 않습니다. 즉, 녹스 워런티는 손상되지 않으며, 녹스를 제외하고는 이전 버전의 펌웨어들과 동일하게 동작합니다. 따라서 이전 버전 펌웨어로 다운그레이드하거나 루팅을 하더라도 녹스 워턴티는 깨지지 않으며 언제든지 순정 상태로 되돌릴 수 있게 됩니다.

1. 파티션 및 IMEI 손상 등 만일의 사태를 대비하여 EFS 백업을 수행합니다. 인터넷 검색을 통해 쉽게 하실 수 있을 겁니다.(관련 툴, XDA http://forum.xda-developers.com/showthread.php?t=1308546)


2. 각 통신사에 맞는 4.3 펌웨어를 준비합니다. 맛클 대용량 펌웨어 자료실에서 구할 수 있습니다. (http://www.matcl.com/?r=home&m=bbs&bid=torrent)

3. 다운받은 펌웨어의 확장자를 tar로 바꾼 뒤 이를 압축 프로그램을 이용해 열어 줍니다. 여기서 녹스 관련 이미지 파일인 sboot.bin을 삭제해 줍니다. 알집을 이용하면 되는 것 같은데 저는 알집을 사용하지 않기 때문에 리눅스 쉘을 이용했습니다.


4. 이후 과정은 일반적인 펌웨어 업데이트 방법과 같습니다. 오딘으로 플래시 해 주면 됩니다. (오딘 사용법은 인터넷 검색을 통해 쉽게 알 수 있습니다.)


5. 정상적으로 업데이트 된 것을 확인할 수 있습니다. 다운로드 모드로 들어가 녹스 관련 워런티가 표시되지 않는 것을 확인합니다.

※ 와이파이 등 관련 기능이 동작하지 않은 경우 롬 플래시가 제대로 되지 않은 경우입니다. 공장초기화 후 다시 펌웨어를 플래시 하시기 바랍니다.


6. 이후 커스텀 리커버리나 기존의 방법을 이용해 루팅을 하시면 됩니다.

※ 저는 루팅카운트를 신경 쓰지 않으므로 Philz-Touch Recovery를 오딘으로 플래시 한 후 리커버리 모드로 들어가 (전원+볼륨+홈키) Supersu 바이너리를 플래시 했습니다.

갤럭시S3용 Philz-Touch Recovery 다운로드:http://d-h.st/users/philz_touch/?fld_id=16049#files / 관련글: http://forum.xda-developers.com/showthread.php?t=2002953

SuperSU 바이너리 다운로드: http://download.chainfire.eu/supersu /관련글: http://forum.xda-developers.com/showthread.php?t=1538053


※ 업데이트 후 기기가 너무 느려지거나 배터리 소모량이 지나치게 증가하는 경우 공장초기화를 해 주시기 바랍니다. 일반적으로 버그 픽스가 아닌 버전 업데이트의 경우 공장초기화를 해 주는 것이 좋습니다.


WRITTEN BY
편지함
The Base Code of the Human Race

,

안드로이드의 화면 회전


대부분의 스마트폰은 자이로스코프와 중력센서가 있어 스마트폰을 회전하면 그에 맞게 화면을 회전시킬 수 있습니다. 안드로이드에서는 설정을 통해 스마트폰을 회전했을때 화면을 자동으로 회전시킬지 아니면 그대로 유지할 지를 결정할 수 있습니다.


안드로이드 알림 서랍 (Notification Drawer)의 빠른 설정



일반적으로 세로가 긴 세로화면 모드를 Portrait, 가로가 긴 가로화면 모드를 Landscape라고 부릅니다.

image from "Designing for Multiple Views and Orientations using Blend", blendinsider


화면을 자동으로 회전하는 경우 누워서 스마트폰을 보거나 잠시 들었다 놓았다 하는 경우에도 화면이 회전하기 때문에 불편하고, 화면을 회전할 때에 걸리는 시간이 있어 대부분의 사용자들은 자동 회전을 해제하고 사용합니다.


하지만 가끔 화면을 회전하고 싶은 경우가 있습니다. 이때에는 일일이 설정에 들어가서 1. 화면 자동 회전을 켜고 2. 화면을 회전한 뒤 3. 화면 자동 회전을 끄고 4. 원하는 앱을 실행한 뒤 5. 볼일이 끝나면 다시 설정에 들어가서 6. 화면 자동 회전을 켜고 7. 화면을 다시 세로로 회전한 뒤 8. 화면 자동 회전 옵션을 끄는 (물론 요즘 스마트폰에서 대부분 지원하는 알림 서랍에 있는 빠른 설정을 통해 - 첫번째 스크린샷 참조 - 이 과정을 줄일 수 있지만) 불필요한 과정을 거쳐야 합니다.

안드로이드 디스플레이 설정에 있는 화면 자동 회전 옵션



또, 자동 화면 회전 옵션을 켜더라도 세로모드나 가로모드를 지원하지 않는 앱들이 있습니다. 대표적으로 게임류 앱이나, 삼성 기본 홈 런쳐인 '터치위즈', 금융앱 등이 그것이죠. 그래서 기기 설정에서 '자동 회전' 옵션이 활성화 되어 있더라도 화면을 회전할 수 없습니다.


지금부터 소개할 어플인 'Rotation Lock'은 기기를 회전할때마다 화면을 회전할 지 말지 결정할 수 있도록 도와주는 어플입니다. 화면회전을 지원하지 않는 앱에 대해서도 화면을 강제로 회전할 수 있습니다.

 

화면 회전을 자유롭게! Rotation Lock


Rotation Lock(Adaptive)

MICGOOWARE

Android, Google Play Store

가격: 무료

구동환경: 2.2 (Froyo) 이상

루팅 필요: X

링크 : https://play.google.com/store/apps/details?id=ui.robot.rotate


Rotation Lock은 구글 플레이 스토어에서 무료로 다운로드 가능하고, 루팅은 필요 없으며 안드로이드 2.2 (프로요) 이상 환경에서 작동합니다. 대부분의 스마트폰이 2.3 (진저브레드) 이상이고 프로요는 찾아보기 힘들 정도니 거의 모든 환경에서 잘 작동한다고 보면 됩니다.


판매자의 설명에 따르면, 이 어플을 사용하면 사용자가 원할 때에만 화면을 회전할 수 있으니 매우 생산적이며, 많은 연산을 요구하는 화면 회전을 불필요하게 하지 않기 때문에 배터리를 아낄 수 있다고 합니다.

이 어플을 실행하면 상태바(Status Bar)에 Adaptive Rotation Lock 아이콘이 나타납니다.

(메모리에 어플이 상주하게 되는데, 어플의 크기가 작고 차지하는 메모리도 매우 작으니 걱정할 필요는 없습니다.)


이제 우리가 원하는 회전 모드(Rotation Mode)를 선택하기 위해 상태바를 끌어 내려 알림 서랍의 Adaptive Rotation Lock 아이콘을 터치해 줍니다.


그러면 아래와 같이 8가지 회전 옵션을 선택할 수 있습니다.



각각의 회전 모드를 간략히 설명하면 다음과 같습니다.

  1. Adaptive Lock : 기기을 회전하면 화면에 회전 아이콘을 표시하고, 사용자가 이 아이콘을 터치하면 화면을 회전하여 고정
  2. Landscape : 가로로 화면 고정
  3. Portrait : 세로로 화면 고정
  4. Reverse Portrait : 뒤집힌 세로모드
  5. Reverse Landscape : 뒤집힌 가로모드
  6. Force Auto : 모든 어플 강제로 자동 회전
  7. Stock Auto : 자동 회전 (기본 자동 회전 활성과 동일)
  8. Stock Portrait : 자동 회전 해제 (기본 자동회전 비활성과 동일)
  9. Disable : Adaptive Rotation 어플 종료 (메모리에 어플이 상주하지 않음)

사용자의 입맛에 따라 설정할 수 있지만 개인적으로 'Adaptive Lock' 모드를 추천합니다.

기기를 회전할 경우 아래와 같이 화면 중앙에 회전 아이콘이 나타나는데,


화면을 회전하고 싶은 경우에만 이 아이콘을 클릭하면 됩니다. 즉, 화면을 선택적으로 회전할 수 있는 것이죠. 화면 회전을 지원하지 않는 어플도 강제로 회전할 수 있습니다.

화면 회전 아이콘을 눌러 홈화면을 회전한 모습


아래 그림처럼 기기의 방향에 따라 화면 방향(Orientation)을 자유롭게 변경할 수 있습니다.

image from "Rotation Lock product page" at Google play store


마지막으로 개발자가 올린 어플 구동 영상입니다.




WRITTEN BY
편지함
The Base Code of the Human Race

,