본문 바로가기
Kotlin

[Kotlin / Android] 11. 제트팩 라이브러리 - appcompat 라이브러리 : API 호환성 해결

by 쀼르쀼르 2024. 3. 22.

11-2 appcompat 라이브러리 - API 호환성 해결


 

 

androidx 라이브러리에서

가장 많이 사용하는 appcompat 라이브러리는

안드로이드 앱의 화면을 구성하는 액티비티를 만들며

API 레벨의 호환성 문제를 해결해 줌

 

appcompat 라이브러리 사용하려면

그래들 파일의 dependencies 항목(의존성 설정)에 선언해야 함

(이 선언은 모듈 만들 때 자동으로 추가됨)

 

· appcompat 라이브러리 선언

implementation 'androidx.appcompat:appcompat:1.5.1'

 

 

appcompat 라이브러리를 이용해서 액티비티 만들 때는

플랫폼 API의 Activity가 아니라

appcompat의 AppCompatActivity 클래스를 상속받아 작성함

 

· appcompat 라이브러리 사용

import androidx.appcompat.app.AppCompatActivity
(... 생략 ...)
class MainActivity : AppCompatActivity() {
}

 


 

 

액션바

 

 

액션바(ActionBar)

- 액티비티의 구성 요소

- 위쪽에 타이틀 문자열이 출력되는 영역을 의미

 

액티비티의 구성 요소

 

액티비티가 출력되는 전체 창은

액션바와 콘텐츠 영역으로 구분됨

 

액션바 영역에는 기본으로 타이틀이 출력

콘텐츠 영역에는 setContentView( ) 함수가 출력하는 내용이 출력됨

 

 

액션바의 구성

 

액션바의 기본 구성은 다음과 같음

 

타이틀은 보이지 않게 설정할 수도 있음

그 밖에 내비게이션 아이콘, 액션 아이템, 오버플로 메뉴

다양한 요소를 액션바에 출력할 수 있음

 


1. 액션바 색상 설정

 

액션바의 색상

 

액션바의 색상은 이 앱에 자동으로 적용되는 테마에서 결정됨

 

테마 스타일은 res/values 디렉터리에 있는

themes.xml파일에 선언되어 있음

 

· 기본으로 작성된 테마 파일의 내용

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- 기본 테마 -->
    <style name="Theme.AndroidLab" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- 주 색상 -->
        <item name="colorPrimary">@color/puple_500</item>
        <item name="colorPrimaryVariant">@color/puple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- 부 색상-->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- 상태바 색상-->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
    </style>
</resources>

 

 

이름이 Theme.AndroidLab인 스타일이 기본으로 선언되어 있음

이 스타일이 매니페스트 파일에 테마로 설정됨

스타일 이름에서 AndroidLab은 프로젝트 이름임

 

· 매니페스트 파일의 테마 설정

<application
    (... 생략 ...)
    android:theme="@style/Theme.AndroidLab">

 

 

themes.xml 파일의 스타일이 앱의 액티비티에 테마로 자동 설정되고

이 테마 파일에 선언된 색상이 액티비티에 적용됨

 

이 스타일은 머티리얼 디자인에서 제공하는

Theme.MaterialComponents.DayNight.DarkActionBar를 상속받아 작성됨

 

머티리얼 디자인에서 테마의 속성은 각각 다음을 의미함

 

테마의 속성


 

。colorPrimarycolorSecondary는 앱의 브랜드를 표현하는 색상임

 

colorPrimary는 액션바와 버튼의 배경색(background color)으로 사용함

 

colorSecondary는 활성 상태를 표현함텍스트 뷰의 링크,

   체크박스나 라디오 버튼이 체크되었을 때,스위치가 켬(on) 상태일 때,

   플로팅 액션 버튼의 배경색 등에colorSecondary를 사용함

 

statusBarColor는 상태바의 배경색으로 사용함

 

 

colorOnPrimary, colorOnSencondary

    colorPrimary, colorSecondary가 적용되는 곳에서

    내용의 전경색(foreground color)으로 사용함

 

colorPrimaryVariant, colorSecondaryVariant

    그림자의 색상으로 사용함


 

테마 파일의 각 색상값을 변경하는 것만으로도 앱의 기본 색상을 변경할 수 있음

 

 

· 테마 색상 설정

<resources xmlns:tools="htto://schemas.android.com/tools">
    <style name="Theme.AndroidLab" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <item name="colorPrimary">#FF0000</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <item name="colorSecondary">#0000FF</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/white</item>
        <item name="android:statusBarColor" tools:targetApi="l">#CC0000</item>
    </style>
</resources>

 

 

 실행 결과 


2. 액션바 숨기기 설정

 

액티비티의 창은 기본으로 액션바를 포함함

액티비티에 액션바를 숨기는 것도 테마 파일에서 설정할 수 있음

 

테마를 만들 때

Theme.MaterialComponents.DayNight.NoActionBar

상속받으면 액션바가 나오지 않음

 

· 액션바 숨기기

<style name="Theme.AndroidLab" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    (... 생략 ...)
</style>

 

만약 NoActionBar 테마를 상속받아 작성할 수 없는 경우라면

테마의 <item> 속성을 다음처럼 설정하여

액션바가 나타나지 않게 할 수도 있음

 

· <item> 속성으로 숨기기

<style name="Theme.AndroidLab"
    parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    (... 생략 ...)
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

 


3. 업 버튼 설정

업 버튼

 

업(up)버튼

- 액티비티 화면이 앱의 첫 화면이 아닐 때 이전 화면으로 되돌아가는 기능을 함

- 액션바 왼쪽에 이전 화면으로 되돌아가는 화살표 모양(←)의 업 버튼

 

업 버튼은

액티비티가 등록되는 매니페스트 파일에서 설정하는 방법과

액티비티 코드로 설정하는 방법이 있음

 

· 매니페스트 파일에서 업 버튼 설정

<activity
    android:name=".TwoActivity"
    android:parentActivityName=".MainActivity"></activity>

 

 

매니페스트 파일에서 <activity> 태그에

parentActivityName 속성을 등록하는 것만으로도

액티비티 화면에 업 버튼이 나옴

 

 

업 버튼 눌렀을 때 이전 화면으로 되돌아가기 전에

특별한 로직 실행하고 싶을 때는

→ 액티비티에 onSupportNavigateUp( ) 함수를 재정의함

    재정의하면 사용자가 업 버튼 누를 때

    onSupportNavigateUp( ) 함수가 자동으로 호출됨

 

· 업 버튼 클릭 시 자동으로 호출되는 함수 재정의

override fun onSupportNavigateUp(): Boolean {
    Log.d("kkang", "onSupportNavigateUp")
    return super.onSupportNavigateUp()
}

 

 

매니페스트 파일에 parentActivityName 속성을 선언하지 않고

액티비티 코드로 업 버튼이 나오게 하는 방법

 

· 액티비티 코드에서 업 버튼 생성

class TwoActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        (... 생략 ...)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }
    override fun onSupportNavigateUp(): Boolean {
        Log.d("kkang", "onSupportNavigateUp")
        onBackPressed()
        return super.onnSupportNavigateUp()
    }
}

 

 

supportActionBar?.setDisplayHomeAsUpEnabled(true)

구문으로 액션바에 업 버튼이 나옴

 

업 버튼 클릭하면 onSupportNavigateUp( ) 함수가 자동으로 호출됨

 

but

매니페스트 파일의 parentActivityName 속성이 설정되어 있지 않으면

자동으로 이전 화면으로 돌아가지 않음

 

따라서

onSupportNavigateUp( ) 함수에서 onBackPressed( ) 구문 등으로

이전 화면으로 되돌아가는 코드를 직접 작성해 줘야 함

 


 

 

메뉴 구성

 

메뉴는 액션바의 중요한 구성 요소로

액티비티 화면에서 사용자 이벤트를 사용할 수 있도록 함

메뉴의 구성

 

액티비티에 메뉴를 추가하면

액션바 오른쪽에 오버플로 버튼이 나타남

 

사용자가 이 오버플로 버튼을 누르면

메뉴가 아래로 확장되어 나타남

 

오버플로 메뉴 중에서 몇몇은

액션바에 아이콘으로 나오게 할 수 있음

→ 이를 액션 아이템(혹은 액션 버튼)이라고 함

 

결국 오버플로 메뉴와 액션 아이템 모두 메뉴임

 

 

액티비티에 메뉴를 추가하고 싶다면

onCreateOptionMenu( )onPrepareOptionsMenu( ) 함수를 이용함

 

두 함수는 액티비티의 메뉴를 구성할 때 자동으로 호출됨

차이점호출되는 시점이 다름

 

onCreateOptionsMenu( ) 함수는

- 액티비티가 실행되면서 처음에 한 번만 호출됨

- 액티비티에 정적인 메뉴 구성할 때 사용

 

onPrepareOptionsMenu( ) 함수는

- 액티비티가 실행되면서 한 번 호출된 후

  오버플로 메뉴가 나타날 때마다 반복해서 호출됨

- 메뉴가 화면에 나올 때마다 동적으로 구성하고 싶은 경우 선택함

 

but

액티비티 메뉴는 주로 사용자 이벤트를 처리하려고 사용하는 것이므로

대부분 액티비티 내에 정적으로 제공하면 됨

 

따라서

액티비티의 메뉴는 대부분 onCreateOptionsMenu( ) 함수를 이용해 구성함

 

· 메뉴 구성 함수

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    val menuItem1: MenuItem? = menu?.add(0, 0, 0, "menu1")
    val menuItem2: MenuItem? = menu?.add(0, 1, 0, "menu2")
    return super.onCreateOptionsMenu(menu)
}

 

 실행 결과 

 

 

onCreateOptionsMenu( ) 함수의

매개변수로 전달되는 Menu 객체를 메뉴바로 생각하면 되고,

Menu 객체에 메뉴를 추가할 때 다음과 같이 add( ) 함수를 이용함

  • fun add(groupId: Int, itemId: Int, order: Int, title: CharSequence!): MenuItem!

 

add( ) 함수의 두 번째 매개변수메뉴의 식별자

 

메뉴는 이벤트 처리가 목적이므로

어떤 메뉴를 클릭했는지 식별할 때 사용함

 

네 번째 매개변수메뉴의 문자열

 

add( ) 함수의 반환값은 MenuItem 객체이며

이 객체가 메뉴 하나를 의미함

 

onCreateOptionsMenu( ) 함수로 메뉴를 구성하면

액션바에 오버플로 버튼이 나오고

이 버튼을 누르면 위 실행 결과처럼 오버플로 메뉴가 나타남

 

이 메뉴를 사용자가 선택했을 때의 이벤트 처리는

onOptionsItemSelected( ) 함수를 이용함

 

이 함수의 매개변수이벤트가 발생한 메뉴 객체인 MenuItem

MenuItemitemId속성으로

이벤트가 발생한 메뉴 객체의 식별값을 얻어서 이벤트를 처리함

 

· 메뉴 선택 시 이벤트 처리

override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
   0 -> {
       Log.d("kkang", "menu1 click")
       true
   }
   1 -> {
       Log.d("kkang", "menu2 click")
       true
   }
   else -> super.onOptionsItemSelected(item)
}

 


1. 리소스로 메뉴 구현

menu 디렉터리

 

액티비티의 메뉴는 대부분 정적으로 제공되므로

코드가 아니라 리소스 XML 파일로 구성함

 

메뉴를 구성하는 XML 파일은

res 폴더 아래 menu 디렉터리에 만듦

 

· 메뉴 XML

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/menu1"
        android:title="menu1" />
    <item
        android:id="@+id/menu2"
        android:icon="@android:drawable/ic_menu_add"
        android:title="menu2"
        app:showAsAction="always" />
    <item
        android:id="@+id/menu3"
        android:icon="@android:drawable/ic_menu_search"
        android:title="menu3"
        app:showAsAction="ifRoom" />
</menu>

 

 

메뉴 XML의 <item> 태그 하나가 메뉴 하나에 해당함

 

id 속성은 레이아웃 XML에서 뷰의 id값과 마찬가지로

메뉴를 식별하는 데 사용함

 

titleicon 속성은 메뉴 문자열과 아이콘으로 지정함

 

메뉴는 기본으로 오버플로 메뉴로 나오는데,

액션바에 아이콘으로 나타나게 하려면

showAsAction 속성을 이용함

 

showAsAction 속성값에는 다음 3가지 가운데 하나 지정하면 됨

  • never(기본): 항상 오버플로 메뉴로 출력함
  • ifRoom: 만약 액션바에 공간이 있다면 액션 아이템으로, 없다면 오버플로 메뉴로 출력함
  • always: 항상 액션 아이템으로 출력함

 

 

XML을 이용해서 메뉴 작성했다면 XML을 액티비티 코드에 적용해 줘야 함

· 액티비티 코드에 메뉴 XML 적용

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main, menu)
    return super.onCreateOntionsMenu(menu)
}

 

 실행 결과 

 

menuInflaterMenuInflater 타입의 속성이며

이 객체의 inflate( ) 함수에 매개변수로

메뉴 XML 파일을 명시하면 액티비티에 메뉴가 적용됨


2. 액션 뷰 이용

액션 뷰(ActionView)는 액션바에서 특별한 기능을 제공하며

대표적으로 androidx.appcompat.widget.SearchView가 있음

 

서치 뷰는 액션바에서 검색 기능을 제공함

 

· 서치 뷰 사용

<item android:id="@+id/menu_search"
    android:title="search"
    app:showAsAction="always"
    app:actionViewClass="androidx.appcompat.widget.SearchView" />

 

 

액션 뷰를 메뉴에 적용할 때는 actionViewClass 속성을 이용함

이 속성에 이용할 액션 뷰 클래스를 등록하면 됨

→ 액션바에 검색 버튼 생기고 버튼 클릭 시 검색어 입력받는 뷰가 나옴

 

서치 뷰

 

 

코드에서 검색과 관련된 기능을 구현하려면 SearchView 객체를 얻어야 함

 

서치 뷰가 메뉴로 제공되므로

SearchView를 등록한 MenuItem 객체를 얻고

MenuItem 객체에 등록된 SearchView 객체를 구하면 됨

 

· 서치 뷰 검색 기능 구현

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater = menuInflater
    inflater.inflate(R.menu.menu_main, menu)
    val menuItem = menu?.findItem(R.id.menu_search)
    val searchView = menuItem?.actionView as SearchView
    searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener {
        override fun onQueryTextChange(newText: String?): Boolean {
            // 검색어 변경 이벤트
            return true
        }
        override fun onQueryTextSubmit(query: String?): Boolean {
            // 키보드의 검색 버튼을 클릭한 순간의 이벤트
            return true
        }
    })
    return true
}

 

 

MenuItem 객체는 findItem( ) 함수의 매개변수에 MenuItem의 식별값을 주어 얻음

 

MenuItem에 등록된 액션 뷰는 actionView 속성으로 얻음

 

검색과 관련된 이벤트를 처리할 때는

SearchViewsetOnQueryTextListener( ) 함수로 이벤트 핸들러를 지정함

 


 

 

툴바

 

툴바(ToolBar)를 사용하는 목적은 액션바와 같음

 

but

액션바는 액티비티 창이 자동으로 출력하는 액티비티의 구성요소임

툴바는 개발자가 직접 제어하는 뷰

 

액션바와 툴바 비교

 

 

왼쪽은 액션바, 오른쪽은 툴바를 이용한 예

 

오른쪽은 액티비티 창이 출력되면서 액션바를 출력하지 않았음

이렇게 하면 화면 전체가 콘텐츠가 됨

콘텐츠 상단에 툴바로 화면을 구성함

 

결국

개발자가 레이아웃 XML 파일에 툴바를 직접 작성해야 함

툴바는 androidx.appcompat.widget.Toolbar 클래스를 이용하면 됨


 

툴바를 사용하려면

먼저 액티비티 테마 설정에서 액션바가 화면에 출력되지 않게 해주어야 함

액티비티의 화면을 구성하는 레이아웃 XML 파일에 Toolbar를 등록함

 

· 레이아웃 XML에 툴바 등록

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/Widget.MaterialComponents.Toolbar.Primary" />

 

 

툴바를 준비했으면 코드에서

액션바의 내용이 툴바에 적용되도록 지정해 줘야 함

이 때는 setSupportActionBar(binding.toolbar) 구문을 이용함

 

· 액션바의 내용을 툴바에 적용

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        (... 생략 ...)
        setSupportActionBar(binding.toolbar)
    }
}

 


 

 

호환성을 고려한 기본 뷰 클래스

 

플랫폼 API에서 제공하는 기본 뷰를 appcompat 라이브러리에서도 제공함

 

예를 들어

플랫폼 API에서 문자열 출력하는 TextView 클래스를

appcompat 라이브러리에서는

AppCompatTextView라는 클래스명으로 제공함

 

두 클래스는 사용 목적이나 기능이 대부분 같음

AppCompatTextViewTextView를 상속받았음

 

appcompat 라이브러리는

AppCompatTextView 이외에도

AppCompatImageView, AppCompatEditTextAppCompatButton,

AppCompatCheckBox, AppCompatRadioButton

기본 뷰에 해당하는 뷰를 제공함

 

 

이처럼 플랫폼 API에서 제공하는 클래스를

appcompat 라이브러리에서도 제공하는 이유는

호환성 문제를 해결하기 위해서임

 

TextView를 사용하다 보면

문자열의 줄 높이를 지정하는 setLineHeight( )라는 함수가 있는데,

이 함수는 API 레벨 28에서 추가됨

 

따라서

setLineHeight( ) 함수를 사용하려면 호환성 고려해 다음처럼 작성해야 함

 

· API 레벨 호환성을 고려한 예

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    binding.platformTextView.lineHeight = 50
}

 

 

만약 TextView를 사용하지 않고

appcompat 라이브러리의 AppCompatTextView 클래스 이용한다면

 

API 레벨 호환성 문제를 알아서 처리해 주므로

간단하게 작성할 수 있음

 

· API 레벨 호환성을 고려하지 않아도 되는 예

 binding.appcompatTextView.lineHeight = 50

 

이처럼 appcompat 라이브러리는

호환성을 고려한 기본 뷰 클래스를 제공함