ActionBar 를 다뤄봅시다.

Action Bar 가 무엇인가요?

Action Bar

Android 앱을 개발하다보면 ActionBar 가 필요해서 보여주고 싶은 경우 혹은 보여주고 싶지 않아서 지우고 싶은 경우가 생깁니다.

그래서 구글링을 해보면 참 여러 가지 방법이 검색되어 나오는데요.

그런데 이런 방법대로 해보면 잘 안되거나 심지어 앱이 죽는 경우도 생깁니다.

왜 그럴까요?

우선 안전하게 Action Bar 다루는 방법부터 살펴보겠습니다.

Action Bar 는 다음 2가지 클래스로 제공됩니다.

  • android.app.ActionBar
  • android.support.v7.app.ActionBar

먼저, “android.app.ActionBar” 는 Android 3.0 (Honeycomb) 부터 지원되기 시작한 클래스입니다.

Honeycomb 은 태블릿만을 위한 버전으로 현재는 사용되지 않는 버전입니다. 그래서, 실질적으로는 Android 4.0 (Icecream Sandwich) 부터 지원되는 것이고요.
API Level 14 단말에서 사용 가능합니다.

그리고, 별도의 라이브러리없이 바로 쓸 수 있습니다.

(이렇게 생겼네요.)

다음으로 “android.support.v7.app.ActionBar” 는 API Level 7 이상 단말이면 모두 사용 가능한 ActionBar 입니다.

v7 은 minSdkVersion 7 이상이면 쓸 수 있는 라이브러리라는 뜻입니다.

(이건 이렇게 생겼습니다.)

뭔가 좀 더 기능이 많게 생겼죠? 색깔도 자유롭게 변경할 수 있고, 버그가 잡히거나 새로운 기능이 추가되더라도 제조사가 신경 쓸 필요가 없습니다.
게다가 앱이 실행될 단말도 API Level 7 이상이기만 하면 됩니다.

실질적으로 API Level 14 미만의 단말기는 거의 없기 때문에 API Level 7 이상이기만 하면 된다는 점은 현실적으로 크게 장점이 되지는 않습니다만 일부 오래된 프로젝트의 경우는 안정성을 이유로 minSdkVersion 을 잘 올리지 않는 경우가 있으므로 이럴 때에는 도움이 됩니다.

이 글에서는 “android.support.v7.app.ActionBar” 를 잘 쓸 수 있는 방법에 대해서 다뤄보겠습니다. (앞으로 ActionBar 라고 줄여서 부르겠습니다.)

ActionBar 는 compileSdkVersion 이 22 이상이면 사용할 수 있습니다.
(당연히 buildToolsVersion 도 22 이상이어야 합니다.)

targetSdkVersion 의 경우 각자 개발중인 프로젝트를 고려해서 설정해야 합니다.

일부 예전버전의 native code 를 가지고 있는 경우 targetSdkVersion 을 23 으로 설정하면 text relocation 문제때문에 실행이 강제종료됩니다.
이럴 경우에는 targetSdkVersion 을 22 이하로 낮춰야 합니다.

새로 프로젝트를 시작한다면 무조건 최신 버전으로 compileSdkVersion 과 buildToolsVersion, targetSdkVersion 을 맞출 것을 권장합니다.

그리고, 역시 중요한 내용으로 Android Support Library 중 AppCompatActivity 클래스와 ActionBar 클래스를 담고 있는 appcompat-v7 의 버전도 22 이상이어야 합니다.

AppCompatActivity 는 ActionBar 를 쓰기 위한 확장된 개념의 Activity 입니다.

Android Support Library 가 무엇인가요?

Android Support Library 는 “android.support.*” 로 시작하는 모든 클래스들과 테마 관련된 리소스 파일등을 묶어둔 라이브러리 저장소입니다.

다음과 같이 Android SDK 에서 “Android Support Repository” 를 설치해야 Android Studio 에서 사용할 수 있습니다. (Android Support Library 라는 이름의 항목이 있지만, 이것은 설치할 필요없이 maven 기반의 Android Support Repository 를 설치하면 됩니다.)

이제 Android Support Library 를 써볼 준비가 끝났습니다.

minSdkVersion 과 targetSdkVersion 과 compileSdkVersion 의 관계

많이 헷갈리는 부분인데 하나씩 정리해보겠습니다.

  • minSdkVersion
    앱이 실행될 가장 낮은 수준의 API Level 을 결정합니다.
    이 값이 14 로 되어 있으면 최소한 Android 4.0 Icecream Scanwich 가 설치되어 있는 안드로이드 단말이어야 앱이 실행될 수 있습니다.
  • targetSdkVersion
    해당 버전에 대한 적합성을 보장하는 역할을 합니다.
    만약 23 으로 지정되어 있다면 이 앱은 Android 6.0 Marshmallow 의 정책을 준수하는 앱이어야 합니다.
    이 경우, text relocation 을 요구하는 so 파일은 로딩될 수 없다는 것이 정책이므로, 이런 앱을 실행하면 강제종료됩니다.
    이 값은 생략가능한데, 생략한다면 minSdkVersion 의 값이 쓰입니다.
  • compileSdkVersion
    실제로 앱을 컴파일할 때 사용할 SDK 의 버전입니다. SDK Manager 에서 설치되어 있는 버전 중 하나를 기재합니다.

이 3가지 값은 다음의 관계를 가집니다.

minSdkVersion <= targetSdkVersion <= compileSdkVersion

보통 minSdkVersion 은 가능한한 낮은 값을 유지해야 더 많은 단말에서 실행될 수 있는데요.
Google Play 스토어에 접근하는 단말들을 살펴보면 API Level 15 인 Android 4.0.3 Icecream Sandwich 로 지정해도 전체 단말의 96.2% 를 커버하고, API Level 16 인 Android 4.1 Jelly Bean 을 선택해도 92.8% 를 커버한다고 하니 적절한 값을 선택하면 됩니다.

targetSdkVersion 과 compileSdkVersion 은 특별한 이유가 없으면 가장 최신 버전으로 맞춰주는 것이 가장 호환성을 좋게 하는 방법입니다.

(적용방법) compileSdkVersion 22 이상인 경우

compileSdkVersion 22 이상으로 지정하면 가장 만족스러운 결과를 얻을 수 있으며, 모든 기능을 다 쓸 수 있습니다.

(참고로 전체 소스코드는 https://github.com/mindwing/AppCompatTest/ 에서 확인할 수 있습니다.)

Bold 체로 된 부분을 잘 확인해주세요.

[build.gradle]

apply plugin: 'com.android.application'

android {
compileSdkVersion 23
buildToolsVersion '23.0.2'

defaultConfig {
applicationId "kr.mindwing.appcompattest"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
}

[MainActivity.java]

package kr.mindwing.appcompattest;

import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

[styles.xml]

<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#3f7fa0</item>
<item name="colorPrimaryDark">#6fafa0</item>
<item name="colorAccent">#0000ff</item>
</style>

</resources>

build.gradle 에서 compileSdkVersion, targetSdkVersion, appcompat-v7 의 값에 23 이 쓰였습니다.

MainActivity.java 에서도 액티비티가 android.support.v7.app.AppCompatActivity 를 상속하게 했습니다.

AppCompatActivity 는 android.support.v7.app.ActionBar 를 쓸 수 있도록 해주는 확장된 액티비티 클래스입니다. compileSdkVersion 22 부터 지원됩니다.

더불어 styles.xml 에는 AppTheme 테마가 “Theme.AppCompat.Light.DarkActionBar” 를 상속하도록 지정했습니다.
이것은 Material Design 에 속하는 것입니다.

이렇게 구성했다면 API Level 22 이상인 단말기에서는 다음과 같은 화면을 볼 수 있을 것입니다.

만약, ActionBar 를 보이지 않게 만들고 싶다면 다음과 같이 간단하게 Theme 만 변경해주면 됩니다.

[styles.xml]

<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#3f7fa0</item>
<item name="colorPrimaryDark">#6fafa0</item>
<item name="colorAccent">#0000ff</item>
</style>
</resources>

만약, 경우에 따라 코드에서 ActionBar 를 보이게 또는 보이지 않게 처리하고 싶다면, 우선 Theme 를 DarkActionBar 상태로 둬서 ActionBar 가 존재하게 한 다음, 다음 코드를 이용해서 상황에 따라 보이거나 보이지 않게 제어할 수 있습니다.

[MainActivity.java]

package kr.mindwing.appcompattest;

import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
        hideActionBar();
}

private void hideActionBar() {
ActionBar actionBar = getSupportActionBar();

if (actionBar != null) {
actionBar.hide();
}
}
}

이렇게 하려면 반드시 DarkActionBar 테마를 적용해서 Action Bar 가 화면에 존재하도록 해야 합니다.
그래서, getSupportActionBar() 메서드를 이용해서 ActionBar 객체를 얻어올 때에 null 체크를 하는 것입니다.

이제 hide() 메서드를 호출하면 Action Bar 가 화면에서 숨겨지게 됩니다.
이 앱을 API Level 19 인 Kitkat 단말에서 실행하면 다음과 같이 나옵니다.

ActionBar 는 잘 나오고 있고 색깔도 잘 변경되지만, Notification 영역의 색깔은 반영되지 않았습니다.
이것은 Kitkat 단말이 Notification 색상변경을 지원하지 않기 때문입니다.

이렇게 같은 앱이라도 단말의 API Level 에 따라 다르게 보이기도 합니다.

(적용방법) compileSdkVersion 21 이하인 경우

이제 compileSdkVersion 21 이하인 경우를 보겠습니다.

targetSdkVersion 과 appcompat-v7 도 21 로 맞춥니다.
buildToolsVersion 은 최신 버전인 23 을 사용해도 무방합니다.

SDK 21 을 사용하는 개발환경에서는 다음과 같은 제약사항이 생깁니다.

  • AppCompatActivity 를 쓸 수 없다.
  • “Theme.AppCompat.Light.DarkActionBar” 를 쓸 수 없다.

그래서, SDK 21 에서는 AppCompatActivity 대신 Activity 를 상속하며, Theme 도 내장된 “android:Theme.Holo.Light.DarkActionBar” 나 “android:Theme.Holo.Light.NoActionBar” 를 써야 합니다.

Holo Theme 는 Icecream Sandwich 발표때 등장했습니다.
Android 의 UI 는 Holo Theme 가 나오기 전과 그 이후 그리고 Material Design 이 Android Support Library 로 등장한 이후로 나뉩니다.
그래서, compileSdkVersion 별로 사용가능한 Theme 범위가 다릅니다.

다음은 Kitkat 단말에서 실행했을 경우의 화면입니다.

Android Support Library 는 왜 나왔나요?

Android Support Library 를 자세히 들여다보면 복잡한 Android 의 속내가 드러납니다.

간단하게 설명하자면, Google 이 출시하는 Android 의 새로운 기능이 실제로 사용자들의 스마트폰에 적용되기까지 너무 오래걸리는 문제때문에 Google 이 직접 스마트폰에 신규 기능을 제공하려던 과정에서 있었던 여러 가지 부작용이라고 정리할 수 있습니다.

길게 설명하자면, 이것은 Android 의 출시과정에 여러 회사가 관여되는 현실과 관련이 있습니다.

Google 이 새로운 Android 버전을 발표하더라도 이것은 우선 퀄컴이나 삼성같은 AP 제조사의 손길을 거쳐야 합니다.
그 다음에는 삼성이나 LG 같은 스마트폰 제조사에게 전달되어 새로운 스마트폰 출시를 위한 준비과정을 거칩니다.

그런데, 스마트폰 제조사 입장에서는 기존 스마트폰의 업데이트 보다는 새로운 스마트폰을 위한 작업에 개발인력을 더 많이 투입하기 마련입니다.
경우에 따라서는 기존 스마트폰의 업데이트에 개발인력을 할당하기가 어려운 경우도 있는데요.

이렇게 되면 비싼 신규단말기를 사지 않는 한 새로운 Android 버전을 업데이트 받아서 Google 의 신규기능을 써볼 수 있는 시기가 Google 의 신규버전 발표 때와 너무나 차이가 나게 됩니다.

그래서, Google 이 Nenux 시리즈를 직접 판매하기도 하지만 기본적으로 Google 은 단말제조사가 아니기도 하고 다른 단말제조사와의 관계도 있기 때문에 효과적인 방법은 아닙니다.
(사실, Nexus 단말의 출시는 기준이 되는 레퍼런스를 제시한다는 것에 있습니다.)

그래서, Google 은 AP 제조사나 스마트폰 제조사의 손길을 거치지 않고 곧바로 사용자의 안드로이드 폰에 새로운 기능을 배포할 수 있는 방법을 만들었는데요. 이것을 Android Support Library 라고 부릅니다.

Android Support LIbrary 는 주로 UI 와 UX 에 관련된 기능을 담고 있습니다. 이런 기능들은 주로 하드웨어적인 의존성이 적기 때문에 Google 이 라이브러리 형태로 직접 배포해도 괜찮은 것이죠.
(Java 초창기부터 프로그래밍을 했었던 개발자라면 Swing 의 패키지가 javax.swing 으로 시작하는 이유를 알고 있을 것입니다. 비슷한 예입니다.)

하지만, Android Support Library 가 Eclipse 라는 다루기 힘든 개발툴과 만나면서 여러 앱 개발자들의 원성을 듣게 됩니다.
(AndroidSupport Library 의 버그와 Eclipse 의 버그와 ADT 의 버그들이 서로 시너지효과를 내었기 때문입니다.)

게다가 Support Library 자체가 안정화되는데에도 상당히 많은 시간이 걸리기도 했습니다.

이제 Android Studio 가 나오면서 Android Support LIbrary 도 점차 안정화되는 모습을 보여주고 있는데요.

중간중간 새로운 기능을 넣었다가 빼기도 하고 바꾸기도 했지만, 이젠 성숙단계에 접어들면서 제법 쓸만해졌습니다.
(게다가 기능의 종류와 범위도 엄청나게 확장되었습니다.)

특별한 이유가 없으면 항상 최신 버전을 테스트해보고 제품에 적용해보세요.