본문 바로가기

안드로이드 개발 팁/UI

선택 가능한 항목(리스트, 버튼)의 배경에 머티리얼 디자인 적용하기

UI를 구성하다 보면, 기본 버튼이나 리스트가 아닌 커스텀 UI를 구성하게 되는 경우가 많습니다.

단순히 텍스트나 이미지를 표시해 주는 부분이라면 배경 색이나 텍스트 색상을 변경해 주는 것 만으로도 충분하지만, 만약  버튼이나 리스트 항목과 같이 별도의 인터랙션이 필요한 부분이라면 터치 이벤트나 키 이벤트 등에 반응할 수 있도록 처리해야 합니다.


대표적인 사레로 리스트뷰 항목의 배경 처리를 들 수 있습니다. 리스트뷰에 표시되는 각 항목은 키 이벤트나 터치 이벤트에 따라 배경 색상이 변합니다. 다음 그림은 리스트뷰에서 '디스플레이' 항목이 선택된 상태의 모습입니다.



커스텀으로 이와 같은 효과를 구현하려면, 일반적으로 XML을 사용하여 <selector> 를 구현하면 됩니다. (예: Color state listStateListDrawable)

하지만, 구현 절차가 간단하지 않을 뿐만 아니라 애플리케이션이 실행되는 플랫폼에서 사용하는 배경과 동일하게 맞추기가 상당히 어려워집니다. (플랫폼 버전 별로  별도의 리소스를 준비해야 함)


따라서, 특별히 디자인이 필요한 것이 아니라면 플랫폼에서 제공하는 배경을 그대로 사용하는 것이 편리합니다. 플랫폼에서 제공하는 배경을 사용하는 방법은 크게 아래와 같이 나뉩니다.



방법 1: 커스텀 속성 사용


먼저, values 폴더 하위에 attrs.xml 파일을 생성한 후 다음 내용을 추가합니다. 속성의 이름을 selectableItemBackgroundCompat 으로 지정했습니다.


[attrs.xml]

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="selectableItemBackgroundCompat" format="reference"/>
</resources>


안드로이드 3.0(API Level 11) 미만 버전과 그 이상 사용하는 속성이 다르기 때문에, styles.xml 파일을 버전에 따라 분리해야 합니다.  values 및 values-v11  폴더를 하위에 각각 styles.xml 파일을 생성한 후, 다음과 같이 애플리케이션에서 사용하는 테마에 추가합니다.


[values/styles.xml]

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat">
        <item name="selectableItemBackgroundCompat">@android:drawable/list_selector_background</item>
    </style>

</resources>



[values-v11/styles.xml]

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat">
        <item name="selectableItemBackgroundCompat">?android:attr/selectableItemBackground</item>
    </style>

</resources>


안드로이드 3.0 미만 버전에서는 리스트 항목의 기본 배경 (@android:drawable/list_selector_background)을 사용하고, 그 이상 버전에서는 선택 가능한 항목 (버튼, 리스트 항목 등)의 배경을 사용하도록 설정했습니다.


다음, 위에서 지정한 속성이 적용된 모습을 확인하기 위해 액티비티 레이아웃을 구성합니다.


[activity_main.xml]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".MainActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?attr/selectableItemBackgroundCompat" android:text="Button" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:clickable="true" android:background="?attr/selectableItemBackgroundCompat" android:text="TextView" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:background="?attr/selectableItemBackgroundCompat" android:src="@mipmap/ic_launcher" /> </LinearLayout>


레이아웃 적용 후, 각 플랫폼에서 애플리케이션을 실행해 보면, 각 플랫폼에서 사용하는 배경이 적용된 것을 확인할 수 있습니다. 안드로이드 5.0 버전에서는 Ripple effect 도 적용됩니다.



Android 2.3Android 4.1Android 5.0



방법 2: appcompat-v7 라이브러리에서 제공하는 배경 사용


방법 1에서 플랫폼 버전별로 리소스를 분리한 이유는 ?android:attr/selectableItemBackground 속성이 안드로이드 3.0 버전 이후부터 지원되기 때문입니다. 이 속성을 사용하면 각 플랫폼 별로 특화된 배경을 사용할 수 있으나, 리소스를 분리해야 하기에 다소 번거롭다는 단점이 있습니다. 또한, 머티리얼 디자인을 적용하게 되면 플랫폼에서 제공하는 배경이 애플리케이션 테마와 어울리지 않아 위화감이 들기도 합니다.


이러한 단점을 보완하기 위한 것인지, appcompat-v7 라이브러리에서는 자체적으로 선택 가능항 항목의 배경을 제공합니다. 단, 안드로이드 5.0 미만 플랫폼에선 옅은 회색 배경으로 표시되고, 5.0 이상에서는 Ripple effect로 표시됩니다.


appcompat-v7에서 제공하는 배경을 사용하려면 ?attr/selectableItemBackground 를 사용하면 됩니다. 물론, 이를 사용하기 전에 라이브러리가 프로젝트에 미리 추가되어 있어야 합니다. 다음은 레이아웃에 appcompat-v7 라이브러리에서 제공하는 배경을 적용한 모습입니다. 방법 1에서 android:background로 사용했던 ?attr/selectableItemBackgroundCompat?attr/selectableItemBackground 로 변경했습니다.


[activity_main.xml]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                android:orientation="vertical"
                tools:context=".MainActivity">

    <Button android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="?attr/selectableItemBackground"
            android:text="Button" />

    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="20dp"
              android:clickable="true"
              android:background="?attr/selectableItemBackground"
              android:text="TextView" />

    <ImageButton android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_marginTop="20dp"
               android:background="?attr/selectableItemBackground"
               android:src="@mipmap/ic_launcher" />

</LinearLayout>



Android 4.1Android 5.0


이 포스트에서 사용한 예제는 아래 링크에서 확인할 수 있습니다.