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를 상속받아 작성됨
머티리얼 디자인에서 테마의 속성은 각각 다음을 의미함
테마의 속성
。colorPrimary와 colorSecondary는 앱의 브랜드를 표현하는 색상임
。 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임
MenuItem의 itemId속성으로
이벤트가 발생한 메뉴 객체의 식별값을 얻어서 이벤트를 처리함
· 메뉴 선택 시 이벤트 처리
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값과 마찬가지로
메뉴를 식별하는 데 사용함
title과 icon 속성은 메뉴 문자열과 아이콘으로 지정함
메뉴는 기본으로 오버플로 메뉴로 나오는데,
액션바에 아이콘으로 나타나게 하려면
→ 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)
}
실행 결과
menuInflater는 MenuInflater 타입의 속성이며
이 객체의 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 속성으로 얻음
검색과 관련된 이벤트를 처리할 때는
SearchView의 setOnQueryTextListener( ) 함수로 이벤트 핸들러를 지정함
툴바
툴바(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라는 클래스명으로 제공함
두 클래스는 사용 목적이나 기능이 대부분 같음
AppCompatTextView는 TextView를 상속받았음
appcompat 라이브러리는
AppCompatTextView 이외에도
AppCompatImageView, AppCompatEditText, AppCompatButton,
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 라이브러리는
호환성을 고려한 기본 뷰 클래스를 제공함
'Kotlin' 카테고리의 다른 글
[Kotlin / Android] 11. 제트팩 라이브러리 - 제트팩과 androidx 소개 (1) | 2024.03.22 |
---|---|
[Kotlin / Android] 10. 다이얼로그와 알림 이용하기 - 카카오톡 알림 만들기 (0) | 2024.03.22 |
[Kotlin / Android] 10. 다이얼로그와 알림 이용하기 - 알림 띄우기 (0) | 2024.03.20 |