안드로이드는 theme과 style을 제공한다. 나름 html의 css와 비슷하게 앱 내부에서 일관된 UI를 손쉽게 제공하는 용도로 사용할 수 있는데, 이게 참 거시기한 부분이 많다.
대체 theme의 이 속성이 어떤 역할을 하는지 감이 잘 안온다. android 시스템의 theme.xml 을 열어보면 속성만 수십개가 나온다. 봐선 한번에 이해가 가는 녀석들도 있지만 dropdown 관련된 것만 해도 몇개나 된다. 관련 문서는 전무하다. 다 소스를 뒤져보라는 것인지.
프로그램적으로 다루기 쉽지 않다. 뭔가 theme의 속성을 프로그램적으로 적용하려고 한다거나, default style을 적용한다거나, style을 프로그램적으로 적용해보려면 꽤나 삽질을 해야 한다.
1번이야 별 수가 없이 시행착오를 거쳐야 한다. 그나마 요즘 intelliJ나 android studio의 preview가 워낙 좋아졌기에 이런 부분은 바로바로 확인 가능하다. 또한, actionbarsherlock이나 holoeverwhere 등 theme을 아주 헤비하게 뜯어고친 라이브러리들을 보면서 비교분석해 보면 삽질을 좀 줄일 수 있을 것이다.
오늘 할 얘기는 2번인데, 프로그램적으로 style/theme을 다룰 때 생기는 이슈 중 내가 알아낸 내용만 간단히 정리해보려고 한다.
현재 theme의 속성 resource id 가져오기
현재 theme의 windowTitleStyle 속성의 resouce id를 가져와보자. xml로 따지자면 ?attr:windowTitleStyle에 해당한다.
이런 형태는 커스텀 뷰의 커스틈 xml 속성 가져오기에서 많이 해 봤을 것이다. a.getXXX 메서드를 호출할 때 인자로 넘기는 인덱스 값은 new []{} 에서 넘긴 속성의 인덱스이다. 위에선 android.R.attr.textColor 의 배열 상 인덱스가 1이므로 1을 넘겼다. a.recycle() 까먹지 말고 반드시 호출하자!
커스텀 뷰에 theme에서 정의한 기본 style을 적용하기
안드로이드 UI 작업을 하다보면 커스텀 뷰를 만들 일이 꽤 많다. 그런데 매번 커스텀 뷰의 style을 적용하기 귀찮아서 default style을 theme에 만들어두고 사용하고자 한다면 어떻게 할까? (결국 이게 theme의 목적이겠지)
우선 theme에 커스텀 속성을 하나 만들어둬야 한다. theme에 커스텀 속성을 추가하는 방법은 커스텀 뷰에 커스텀 속성을 추가하는 방법과 동일하다. values/ 에 attribute 정의하는 xml을 하나 만들고 거기 아무 이름이나 styleable을 하나 만들어 둔 다음 attr을 정의하면 된다.
여기서 init() 메서드의 두번째 인자로 R.attr.myCustomView 를 넘긴 것을 눈여겨보자. 이렇게 하면 xml상에서 MyImageView를 선언했을 때 속성을 별도로 명시하지 않았을 때 theme의 myCustomView이 참조하는 스타일의 해당 속성이 적용된다. 만약 저 defStyle 부분을 0으로 해 버리면 아무리 theme에 myCustomView 속성을 명시해 봤자, super의 속성에만 기본 스타일 값이 적용되고, init() 메서드 안에서 조작하고자 하는 녀석들엔 기본 스타일 값 (위 예제에선 myCustomViewDefaultStyle 라는 스타일의 값)이 적용되지 않는다.
직접적인 holo theme 가 아닌 AppCompat theme 를 사용하는 걸 기준으로 합니다. (support v7 기반)
Styles & themes 기반으로 작업하면 좋은 점
애플리케이션 용량
이미지 대신 스타일과 테마로 작업을 하게 되면, 우선 앱 용량에 영향을 줍니다. 외부 라이브러리를 많이 사용하지 않는 경우에는 더더욱 앱을 컴팩트 하게 만들 수 있게되죠. 이미지 리소스 기반이 아닌 컬러와 스타일들로 대부분의 뷰를 만들게 된다면 2MB 정도로 왠만한 애플리케이션 하나를 만들 수 있게 됩니다.
스타일의 재사용
안드로이드를 개발하며 layout 등을 재사용 하는 경우는 생각보다 별로 없게되곤 하죠. 스타일을 사용한다면 실제 layout 에 대한 재사용은 아니지만, 전체적인 분위기를 그대로 들고 다닐 수 있게 됩니다. 한번 잘 정의해 놓은 스타일은 Android library project 에 포함시켜두고 사용하기도 편하죠. 스타일을 만들어두고 재사용을 하게 되면, 어떤 컬러나 위젯에 수정을 해야할 경우 layout 에 직접 코딩하는 것 보다 훨씬 빠르게 수정할 수 있게 됩니다. 레이아웃을 전부 따라다니며 수정 할 필요 없이, 스타일 하나만 수정하면 되니까요.
반복적인 제스쳐 피드백, 앱 전체적인 테마에 대한 효율
스타일만을 포함하고 하기에는 좀 더 큰 범위의 얘기지만, 컬러, 스타일, 테마를 복합적으로 사용한다면 개발을 하는 효율이 좋아지게 됩니다. 물론 초기에 스타일을 정의하고 컬러들을 정의하고 셀렉터들을 만드는 귀찮은 과정이 들어가야 하겠지만, 한번 만들어 두고 나면 계속해서 레퍼런스하며 사용할 수 있기때문에 프로젝트를 진행하는데 있어서의 효율이 좋아지게 되죠. 앱 전체적인 테마를 구성하는 경우에도, 액션바나 기본 배경색이라거나, 텍스트 컬러 같은 것들, 버튼등의 클릭 이벤트시 보여주어야 할 피드백 등, 여러가지 것들을 한번 만들어두고 재사용 할 수 있으니, 초반에 조금 귀찮은걸 감수하면 개발은 편해질 수 있습니다.
이외의 장점에 대한 잡다한 생각
나름대로 생각해본 장점들을 써봤는데, 그게 그거 같은 느낌이긴 하네요. 그럼에도 요즘의 추세인 플랫 디자인으로 오면서는 스타일과 테마를 지정하고 사용하는게 좀 더 적합해지고 있는 느낌입니다. 앱 전체적으로 많은 컬러나 특수한 상황에서의 피드백 같은 것들이 플랫하게 되며 큰 틀로 통합되는 느낌이기도 하구요. (라운딩 처리나 다른 처리 들이 없으면, 컬러만으로 만들어 내기 훨씬 쉽죠.) 플랫한 컬러들을 사용하다보면 심심할때 혼자서 만들어 보는 앱들에는 ‘개발자가 만든 티’를 덜낼 수 있습니다(^^;;) 안드로이드 어셋 스튜디오를 사용한다면, 컬러만으로도 자신만의 테마를 손쉽게 제너레이트 해서 사용할 수 있고요.
스타일 정의 시작하기
처음 프로젝트를 생성하면 styles.xml 에는 아무것도 들어있지 않습니다. 하나 하나 만들면서 추가해나가 보도록 해요. API Guides 의 Styles and Themes 에는 플랫폼 버전별 테마를 분리해서 작성하는 법에 대해 나와있지만, 여기선 AppCompat 을 기반으로 합니다.
하나의 테마, 스타일을 만들어서 액티비티, 애플리케이션에 공통적으로 적용할 수 있습니다. 기본 배경색을 지정하고 애플리케이션 전체에 적용시켜 봅니다.
먼저 colors.xml 에 기본 bg color 를 추가해주고, Theme 에 background color 를 지정해줍니다. (경우에 따라-관리해야 할 컬러가 많지 않다거나- styles.xml 에 함께 두어도 되기는 합니다.) 기본적으로는 AppTheme 라고 생성되지만, NovaAppTheme 로 변경했습니다.
액티비티마다 다른 테마를 사용하고 싶다면, 해당 액티비티(<activity>)에 android:theme 속성을 이용해 적용할 수 있습니다. 여기서는 앱 전체에 공통 테마를 적용하기 위해 <application> tag 에 적용을 합니다.
Widget Style
많이 쓰이는 위젯 중 버튼의 Widget style 을 만들어 보도록 합니다. android:Widget.Button 을 parent 로 하는 style 을 하나 추가해줍니다. (편의상 버튼 이미지는 theme color 를 #e44044 로 하는 Holo light 테마로 생성했습니다. – http://android-holo-colors.com/)
이렇게 테마에 지정한 이후에는 layout 에서 <Button> 을 사용할때, style 지정없이 사용해도 공통되게 Nova.Widget.Button 의 style 을 따라가게 됩니다. 그냥, 기본 위젯을 사용하는 느낌으로 써도, 한번 정의해 둔 style 을 계속 사용할 수 있기 때문에 신경 안쓰고 개발을 할 수 있게 되죠. 또, 버튼의 크기라거나 텍스트 크기가 약간만 다르게 하면 된다면, layout 에서 그 부분에만 추가하면 되죠.
기본적으로는 styles.xml 에 Theme 가 생성되지만, 안드로이드 스타일에 맞게 themes.xml 을 추가해보도록 합니다. values 디렉토리에 themes.xml 파일을 생성해 줍니다. 위에서 styles.xml 에 만들었던, NovaAppTheme 를 themes.xml 로 옮겨줍니다.